文章目录
- 漏洞利用概念及示例
-
- 漏洞利用概念
-
- 漏洞利用手段
-
- shell
- shellcode
- 漏洞利用的核心
-
- Exploit的结构
- 覆盖临接变量示例
-
- verify函数的缓冲区
- 漏洞利用:覆盖临接变量
- Shellcode代码植入示例
-
- 漏洞利用:代码植入
- 编写shellcode
-
- 用汇编语言编写MessageBoxA需要三个步骤
- Shellcode编写
-
- Shellcode编写的难点
- 简单编写Shellcode的方法
-
- 第一步:用c语言书写要执行的Shellcode
- 第二步:换成对应的汇编代码
- 第三步:根据汇编代码,找到对应地址中的机器码
- 编写输出“hello world”的Shellcode
- Shellcode编码
-
- Shellcode编码的必要性
- Shellcode编码方法
-
- 异或编码
-
- 编码程序
- 解码程序
- 获取代码当前指令地址
- 完整的shellcode代码
漏洞利用概念及示例
漏洞利用概念
漏洞利用(exploit)是指针对已有的漏洞,根据漏洞的类型和特点而采取相应的技术方案,进行尝试性或实质性的攻击。Exploit 的英文意思就是利用,它在黑客眼里就是漏洞利用。有漏洞不一定就有Exploit(利用),但是有Exploit就肯定有漏洞。
假设,刚刚发现了一个Minishare的0Day漏洞。Minishare是一款文件共享软件,该0Day漏洞是一个缓冲区溢出漏洞,这个漏洞影响之前的所有版本。当用户向服务器发送的 文长度过大(超过堆栈边界)时就会触发该漏洞。
得到该漏洞后,可以做点什么呢意点的,可以对同学或者朋友的电脑搞搞恶作剧,让他的电脑弹出个对话框之类的。恶意的话,可以利用这个漏洞来向目标机器植入木马,窃听用户个人隐私等。
漏洞利用手段
在1996年,Aleph One在Underground发表了著名论文《SMASHING THE STACK FOR FUN AND PROFIT》,其中详细描述了Linux系统中栈的结构和如何利用基于栈的缓冲区溢出。在这篇具有划时代意义的论文中,Aleph One演示了如何向进程中植入一段用于获得shell的代码,并在论文中称这段被植入进程的代码为“shellcode”。
shell
实际上,Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。
shellcode
现在,“shellcode”已经表达的是广义上的植入进程的代码,而不是狭义上的仅仅用来获得shell的代码。
漏洞利用的核心
漏洞利用的核心就是利用程序漏洞去劫持进程的控制权,实现控制流劫持,以便执行植入的shellcode或者达到其它的攻击目的。
当攻击者掌握了被攻击程序的内存错误漏洞后,一般会考虑发起控制流劫持攻击。早期的攻击通常采用代码植入的方式,通过上载一段代码,将控制转向这段代码执行。在栈溢出漏洞的利用过程中,攻击的目的是淹没返回地址,以便劫持进程的控制权,让程序跳转去执行shellcode。
Exploit的结构
要完成攻击,Exploit需要执行shellcode,但Exploit中并不仅是shellcode。
- Exploit要达到攻击目标,要做的工作更多,比如对应的触发漏洞、将控制权转移到shellcode一般均不相同,而且他们通常独立于shellcode的代码。
- 能实现特定目标的Exploit的有效载荷,称为。
一个经典的比喻,将漏洞利用过程比作导弹发射的过程:Exploit、payload和shellcode分别是导弹发射装置、导弹和弹头。Exploit是导弹发生装置,针对目标发射导弹(payload);导弹到达目标之后,释放实际危害的弹头(类似shellcode)爆炸;导弹除了弹头之外的其余部分用来实现对目标进行定位追踪、对弹头引爆等功能,在漏洞利用中,对应payload的非shellcode的部分。
Exploit是指利用漏洞进行攻击的动作;Shellcode用来实现具体的功能;Payload除了包含shellcode之外,还需要考虑如何触发漏洞并让系统或者程序去执行shellcode。
覆盖临接变量示例
假设我们已知一个系统的注册机验证过程的漏洞,程序举例如下:
verify函数的缓冲区
Verify函数的缓冲区为44个字节,对应的栈帧状态如下图所示:
基于上述程序,编写shellcode,完成代码植入
Shellcode往往需要用汇编语言编写,并转换成二进制机器码,其内容和长度经常还会受到很多苛刻限制,故开发和调试的难度很高。
植入代码之前要做大量的调试工作,比如:
- 弄清楚程序有几个输入点,这些输入将最终会当作哪个函数的第几个参数读入到内存的哪一个区域,哪一个输入会造成栈溢出,在复制到栈区的时候对这些数据有没有额外的限制等;
- 调试之后还要计算函数返回地址距离缓冲区的偏移并淹没之;
- 选择指令的地址, 最终制作出一个有攻击效果的“承载”着的输入字符串。
编写shellcode
目标:编写一段代码,使其能够淹没返回地址,该返回地址将执行一个MessageBox函数,弹出窗体。
为了能淹没返回地址,需要在reg.txt中至少写入:buffer(44字节)+flag(4字节)+前EBP值(4字节),也就是53—56才是要淹没的地址。让程序弹出一个消息框只需要调用Windows的API函数MessageBox。
MSDN对MessageBox函数的解释如下:
- hWnd
消息框所属窗口的句柄,如果为NULL,消息框则不属于任何窗口。
- lpText
字符串指针,所指字符串会在消息框中显示。
- lpCaption
字符串指针,所指字符串将成为消息框的标题。
- uType
消息框的风格(单按钮、多按钮等),NULL代表默认风格。
用汇编语言编写MessageBoxA需要三个步骤
- 装载动态链接库user32.dll。MessageBoxA是动态链接库user32.dll的导出函数。虽然大多数有图形化操作界面的程序都已经装载了这个库,但是我们用来实验的consol版并没有默认加载它。
- 在汇编语言中调用这个函数需要获得函数的入口地址。
- 在调用前需要向栈中按从右向左的顺序压入4个参数。为了让植入的机器代码更加简洁明了,我们在实验准备中构造漏洞程序的时候已经人工加载了user32.dll这个库,所以第一步操作不用在汇编语言中考虑。
第一步:获取函数入口地址
获取函数入口地址:MessageBoxA的入口地址可以通过user32.dll在系统中加载的基址和MessageBoxA在库中的偏移相加得到。可以使用VC6.0自带的小工具“Dependency Walker”获得这些信息,如下图user32.dll的基地址为 0x77D10000,MessageBoxA的偏移地址为0x000407EA。基地址加上偏移地址就得到了MessageBoxA函数在内存中的入口地址:0x77D507EA。
第二步:编写函数调用的汇编代码
编写函数调用的汇编代码:先把字符串“westwest”压入栈区,消息框的文本和标题都显示为“westwest”,只要重复压入指向这个字符串的指针即可;第1个和第4个参数这里都将设置为NULL。
机器代码 | 汇编指令 | 注释 |
---|---|---|
33 DB | XOR EBX,EBX | 将EBX的值设置为0 |
53 | PUSH EBX | 将EBX的值入栈 |
68 77 65 73 74 | PUSH 74736577 | 将字符串west入栈 |
68 77 65 73 74 | PUSH 74736577 | 将字符串west入栈 |
8B C4 | MOV EAX,ESP | 将栈顶指针存入EAX(栈顶指针的值就是字符串的首地址) |
53 | PUSH EBX | 入栈Messagebox的参数-类型 |
50 | PUSH EAX | 入栈Messagebox的参数-标题 |
50 | PUSH EAX | 入栈Messagebox的参数-消息 |
53 | PUSH EBX | 入栈Messagebox的参数-句柄 |
B8 EA 07 D5 77 | MOV EAX, 0x77D507EA | |
FF D0 | CALL EAX | 调用MessageBoxA函数,注意,每个机器的该函数的入口地址不同,请按实际值写入。 |
第三步:注入Shellcode代码
得到的shellcode为:33 DB 53 68 77 65 73 74 68 77 65 73 74 8B C4 53 50 50 53 B8 EA 07 D5 77 FF D0。
将这段shellcode写入reg.txt文件,且在返回地址处写buffer的地址。
用nop指令填充空余部分(ASCII值:90)
在工程中编写汇编语言如下:
第三步:根据汇编代码,找到对应地址中的机器码
在汇编第一行代码处打断点,利用调试定位具体内存中的地址:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORsSG4CT-1655372162228)(c58fa397f5aebffcee94d5abdd6a0ef1.png)]](https://www.iruanshi.com/news2/wp-content/uploads/2023/02/slt.png)
接下来就可以利用这个Shellcode来实现漏洞的利用了,一个VC6测试程序如下:
#include #include char ourshellcode[]="x33xDBx53x53x53x53xB8xEAx07xD5x77xFFxD0";void main(){ LoadLibrary("user32.dll"); int *ret; ret=(int*)&ret+2;//指向返回地址 (*ret)=(int)ourshellcode; return声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!