20192431张潇文《网络与系统攻防》实验一 逆向及Bof基础实践
实验 逆向及Bof基础
1.1 实验目标
实验对象是pwn1的Linux可执行文件,该程序原本的正常执行流程是main函数调用foo函数,foo函数会回显用户输入的字符串。另一个代码片段为getshell,正常情况下这个代码段是不会运行的,本次的实验目标就是想办法运行这个代码片段。
实验目标是:
- 学会利用反汇编查看程序代码,使用vi等编辑器来手工修改call指令要跳转到的地址,要会算这个地址,学会使用补码进行地址的计算。从而使得程序直接跳转到getshell。
- 通过foo函数的Bof漏洞,构造攻击的字符串覆盖到函数的返回地址,从而使得程序跳转到getshell函数。
- 仍然利用这个漏洞,注入一个自己的shellcode并运行。
- 通过这些过程了解缓冲区溢出的原理,掌握一些基础的Linux命令。
1.2 基础知识的学习以及原理的理解
- Linux基本操作命令:
反汇编命令:objdump -d pwn20192431 | more
-d表示disassemble ,表示从objfile中反汇编那些特定指令机器码的section。
more表示分页显示
管道符表示将前面每一个进程的输出直接作为下一个进程的输入
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
输出重定向 >可以把一些字符串或者一些文件等输出到一个新的文件当中,在实验里输入input文件的时候都用到了输出重定向。
perl在linux下可以理解为增强版本的shell,是一种脚本语言,具有程序结构,很多内建功能,也方便调用其它程序。
catcat命令是linux下的一个文本输出命令,通常是用于观看某个文件的内容的。
xxd16进制查看文件的内容。
(gdb)info r
用来显示寄存器的值,可以方便我们找到EIP,EBP的值。
cp可以复制这个文件,用来备份。
查找需要的地址\e8d7,能够省下一定的查找所需要的时间。
转换16进制为原格式:%!xxd -r。
转换root权限su
- EIP寄存器:EIP寄存器始终存储的是下一条将要执行的指令的地址,所以在实验当中就可以考虑改变EIP的值,使他指到我们的目标函数执行的地址。
- call指令:call指令就相当于两条指令,分别是mov和jmp。先把下一个IP压入栈后,跳转到所要执行的地址,调用子函数。
- 补码计算指令的地址,比如说本次实验中的call 8048491,就是foo函数的地址,e8是跳转的意思,d7ffffff是补码,就是用你需要执行的函数地址减去下一条指令的地址,取补码就可以算出来。可以采用计算器进行计算也可以手动按照补码的规则进行计算。
- 堆栈的原理和缓冲区溢出的原理:
内存和栈的增长方向是相反的,如下图:也就是主函数先
实践1 直接修改程序机器指令,改变程序的执行流程
1.1实验思路
实验思路:因为这个程序执行的过程比较简单,就是通过call,把我们想要调用的函数的地址压入EIP,使得执行的时候就执行到这个程序,我们就可以采用vi编辑器,直接把这个程序以16进制的方式输入到屏幕当中,然后通过r,修改一下地址就可以使得call的时候把getshell的执行地址压入EIP中,然后触发想要执行的函数。
1.2实验步骤
-
使用objdump查看原文件的函数调用的过程,可以看到在主函数中,最右边的汇编语言里有一个call 8048491
,就说明接下来将要把这个foo函数调进来,然后呢左边就是e8机器码,跳转到ffffffd7的位置,只要把这个使用vi编辑改掉就可以了。 -
通过计算(8048491-80484ba)补,可以算出ffffffc3就是getshell的地址,于是呢按:wq!保存并退出即可。
-
再使用objdump反汇编修改好了程序,来验证是否成功修改,如下图:
-
运行一下修改后的文件可以发现,修改后的文件,就会从原来的回显,变成现在的shell(类似于命令行)。
实践2 通过构造输入参数,造成BOF攻击,改变程序执行
2.1 基础知识
- 堆栈结构:一种先进后出的结构,通常用在函数的调用过程当中,基本的操作就是压栈和弹栈,函数调用的过程和栈的先进后出的结构很类似,比如主函数第一个执行,最后一个退出。在内存中,函数是由高地址向低地址增长的。放的时候是高地址到低地址,指令执行的时候是低地址到高地址。
- 缓冲区溢出:冯诺依曼结构的缺陷就是数据和指令不好区分,所以填充过多的数据就会造成缓冲区溢出,改变了返回的地址。关键在于确定溢出的位置。溢出的位置就是在栈这种数据结构
2.2实验步骤
-
观察程序的漏洞,如图:我们可以看到foo函数的漏洞在于这一个地方:
sub $0x38,%esp
lea -0x1c(%ebp),%eax。
mov %eax,(%esp)
思路:第一个指令就是给了foo函数一个栈帧,一个38的空间,下一个也就是表面了缓冲区的大小就是十进制28这么大的空间,也就是说一个字符串的前28个会放到这个空间里,超出的部分就会覆盖别的地方,后面四个字节是EBP,如果接下来的四个字节则是EIP,如果我把getshell的地址放在这里就可以使得程序跳转到getshell运行。所以最重要的是三十二个字节之后的那四个字节。写什么很重要。call调用foo,会在堆栈上压上返回地址值:80484ba,也就是下一条指令的地址。 -
确认哪些字符会覆盖到返回地址:
通过gdb来调试,用r,就是开始运行,可以看到不超过28的时候,没问题,超过以后就会报出段错误。因为地址被改掉了。就可以看到是四个5覆盖到了返回地址。接下来显示寄存器的值。确认好究竟是哪些字符会覆盖上去。
3. 只要把getshell的内存地址放到EIP上就行了,因为是小端优先,所以用\x7d\x84\x04\x08写在那个位置上就可以。由于键盘没办法输入ASCII码,所以采用perl语言输入。把这个字符串放入input这个文件。这里就用到了输出重定向。
4. 用16进制查看input里的内容是否和我们预期的一样。
5. 将input的输入通过管道符,作为pwn的输入。
6. 运行一下验证可以看到已经成功的调用了getshell了。
实践3 注入shellcode并执行
3.1 实验思路
最重要的是考虑构造的shellcode的哪一部分会覆盖EIP的值。shellcode要放在哪里。EIP肯定需要放shellcode的起始地址,所以shellcode可能放在EIP之前,也可能放在EIP之后。前面的32字节可以随便填,后四个字节放SHELLCODE的地址,但是要确定注入的时候内存把shellcode分配在哪里了。所以常常在shellcode里加好多好多的空指令,看有没有空指令落在RS上。
3.2 实验步骤
- 准备一段shellcode,shellcode是一段机器指令。一般是为了获取交互式的shell。
- 准备工作:
要设置堆栈可执行,因为shellcode一般都是在缓存区里,只要操作系统限制堆栈中的数据只可读写,不可执行,一旦堆栈中的数据被执行立即报告错误,并退出。那么溢出成功后也不能执行shellcode。但是为了本次实验就需要设置堆栈可执行。
同时也要关闭地址随机化,也是为了防止缓冲区注入攻击。不然每次的堆栈地址都不一样,调试起来就非常的麻烦。 - 构造要注入的代码。
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr。
使用第一种方式。
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
由于返回位置大概在33,34,35,36的位置,所以呢就看上面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。 - 利用gdb来调试,找到1234的地址改成在内存的地址。首先找到进程号,启动进程的调试,然后设置断点断在ret,执行完ret以后就会把1234push进去。如图:
我们可以看到我们的esp的栈顶的地址是:ffffd16c
找空指令就可以看到我们的shellcode的位置是:ffffd170,所以也就是说把1234改成我们自己的地址的话就可以成功的注入了。
5. 把shellcode输入到文件里
6. 注入到代码中,就可以看到我们的实验已经成功了。
四 实验中遇到的问题
- 实验三不能加\0a的原因是为了让程序能够停在那里,相当于没输回车,就可以停在那边观察。
- 实验三还出现注入没有成功的情况:如图:
原因是因为我最开始照搬了老师的地址,没有看自己的地址,后来重新使用gdb查看了自己的地址之后,实验成功。
3. 在实验过程当中,出现了在虚拟机里安装gdb的时候出现网址not found的情况。解决方法,经过查找资料以后是因为以前使用的是国外的更新源,后来通过vim /etc/apt/sources.list
指令把以前的网址注释掉了,加上了清华源,然后再去安装那些软件最后就成功了。
4. 在修改主机名的时候,说我没有权限,而且当时修改更新源的时候还出现了Linux修改文件出现错误E45:“readonly” option is set(add ! to override)退出不了vim。原因是因为我当时没有以root身份登录这个kali。后来最开始解决的方法是切换成了root用户,使用sudo passwd root
设置密码,然后用su切换身份最后成功。
五 实验感想
对于堆栈有了更进一步的了解,同时缓冲区溢出攻击的构造方式有了进一步的理解。实验三中设置堆栈可执行,因为shellcode一般都是在缓存区里,只要操作系统限制堆栈中的数据只可读写,不可执行,一旦堆栈中的数据被执行立即报告错误,并退出。那么溢出成功后也不能执行shellcode。但是为了本次实验就需要设置堆栈可执行。同时也要关闭地址随机化,也是为了防止缓冲区注入攻击。不然每次的堆栈地址都不一样,调试起来就非常的麻烦。通过实验对周三课上所讲的缓冲区溢出攻击的防范和原理有了更好的理解,明白其实就是想办法把我们自己想要执行的代码地址想办法填充到EIP中。