前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过POC来学习漏洞的原理

通过POC来学习漏洞的原理

作者头像
信安之路
发布2018-08-08 15:42:19
7390
发布2018-08-08 15:42:19
举报
文章被收录于专栏:信安之路信安之路

本文介绍的是 easyFTPServer 1.7.0.2 ‘Http’ remote Buffer Overflow 的漏洞执行流程,通过已知的 POC 来推断程序的大概执行流程以及漏洞利用原理。

Poc 和软件的下载地址:

https://pan.baidu.com/s/1dHjKFCX (提取码 5h7h )

0x01 了解 FTP

FTP 服务全称为文件传输协议服务,其工作模式采用 C/S 模式,用户想要通过 FTP 服务访问到共享的文件信息时,首先需要在自己的计算机系统上运行一个 FTP 客户端,这个客户端可以是一个 FTP 应用程序,也可以是操作系统自带的命令行程序,然后在 FTP 客户端中输入用户名和密码来登录 FTP 服务程序,成功登陆后,用户就可以获取远程计算机上共享的文件信息了。

emmmm……,换句话说客户端连接远程FTP服务器需要以下几个步骤

(1)建立 TCP 连接

(2)客户端向 FTP 服务程序发送 USER 命令以标识用户自己的身份,然后服务程序要求客户端输入密码

(3)客户端发送 PASS 命令,同时将用户密码发送给远程 FTP 服务程序

(4)服务端判断并通过认证

(5)客户端开始利用其它 FTP 协议进行文件操作

(6)结束此次连接,用 QUIT 命令退出

0x02 搭建实验环境

FTP 服务端:

win xp sp3(我用的是吾爱破解论坛的虚拟机)

FTP 客户端:

win 7 (要求 Python 环境)

easyFTPServer 1.7.0.2 解压到 xp 上,点击运行,目录下会产生 3 个 XML 配置文件以及名为 anonymous 的文件夹,该程序默认情况下会设置一个名为 anonymous 的初始用户,而 anonymous 文件夹就是该用户使用的文件目录。

好了,我们已经配置完服务端了。

大家可能已经发现了,这个软件除了提供 21 端口的 FTP 服务之外,还提供了 8080 端口的服务,这个 8080 端口就是今天我讲解的重点,我们先访问一下

http://192.168.1.106:8080

emmmm,输入 anonymous,anonymous 之后界面如下

们就可以通过网页对 FTP 服务器上分享的文件进行下载其本质是把 FTP 分享的文件以 web 页面的方式给大家呈现了出来。

0x03 poc 的简单介绍

本文我们不研究怎么写 poc,当然我会在最后给大家介绍一下 寻蛋(egghunter) 这种 exp 开发技术,我打算通过已经写好的 exp 来给大家反向讲解一下这个漏洞的成因及原理,个人认为这样更容易理解,具体怎么样要问各位看官了…… 我会在调试的时候把自己的思路给大家详细说明一下

exp 如下(Python2.7 版本)

我大概讲解一下这个 exp 的内容,本质是构造了一个 get 请求,给 path 参数一个超长的字符串里面有我们覆盖返回地址的跳板地址,而 HOST 后面的字符串是我们构造的 shellcode,Authorization 后接我们的用户名和密码。

这样我们就可以简单的登录并把恶意的数据发送给 192.168.1.106:8080 页面所在的服务端上。

这里的 shellcode 是在 win xp sp3 上弹出计算器,可以通过 metasploit 的 msfvenom 模块进行生成,我就不演示了。

0x04 执行 exp

一切准备就绪,我们开始执行 exp

首先,在 FTP 服务端上用 OD 附加 EasyFTP Server 这款软件,由于这个软件运行的时候有两个进程,我们附加它的FTP服务进程,也就是 ftpbasicsvr 这个进程(这里尽量使用原版 OD )

点击附加

然后我们按 F9 让程序继续执行,即让服务端接着监听 8080 端口的请求。

我们回到客户端,执行之前写好的 exp。

按下回车键,回到服务端看看发生了什么?

emmmmm…… 计算器弹了出来,说明 exp 没有什么问题。

那么问题来了,我想看 shellcode 的详细执行流程啊,而不是仅仅弹出一个计算器。

接着我陷入了沉思:客户端和 FTP 服务端进行通信的时候,本质上是 socket 通信,只不过是端口变成了 8080 罢了,有发送就会有接收,我们在 recv() 下断点不就可以截获从客户端发来的数据了吗?

写过 c/c++ 的人都知道,socket 中的 recv() 函数是在 WS2_32.dll 中的。

在 OD 上直接下断 bp recv,然后重启 FTP 服务器,重新附加进程,客户端重新连接,OD 的状态如下:

emmmmm……

看来我们的想法是对的,程序断在了 recv() 函数的入口,注意看堆栈窗口有一堆 aaaaaaaaaaa,那就是我们构造的 get 请求中 path 的参数,接下来我就要说明一下这个栈溢出漏洞的根本原因了,我们可以看到函数入口这样一条汇编

sub esp 124

就是开辟了一个 292 字节的栈空间(124 是十六进制),get 请求中的 path 参数的值,也就是 exp 中 buf 的值,就存放在这个空间中。

而这个漏洞产生的原因就是没有对 path 参数的值,也就是 buf 的值进行长度的校验,以致于我们可以构造超长的字符串从而覆盖这个处理函数的返回地址进而对程序执行流程进行劫持。

具体怎么实现的,我们拉到这个处理函数的末尾,在平栈的时候下断也就是在 0040b92D 处下断,按 F9 执行:

此时要注意堆栈,有一堆 6161616161 , 而 61 正是 a 的十六进制表示,说明我们构造的超长字符串已经入栈,F8 单步执行到 retn 8,我们看到返回地址被覆盖成了跳板地址(这里的跳板地址是 ntdll.dll 中的 jmp esp,地址可以用 OD 插件或者 msf 的模块进行搜索)

执行完 jmp esp 后,F8 向下执行,进入 寻蛋(egghunter) 部分(这里就可以解释一下 py 脚本中在跳板地址后面为什么有 8 个 \x63,因为 retn 8 嘛,返回的时候跳过了 8 个字节)

来到了我们的寻蛋指令,emmmmm 关于这部分,你只需要知道的是蛋(也就是我们的 shellcode )包含四个字节的标志头. 如果寻蛋开始, 首先它会搜索整个内存直至找到重复两次找到这个标志(如果标志是 `”\x44\x7A\x32\x37”, 那么就搜索 ”\x44\x7A\x32\x37\x44\x7A\x32\x37” )。当找到这个标志, 改变执行流跳转到标志后的 shellcode 执行。

接着我们对着 00a2F196 按 F4,我们看寄存器 edi 的值变为了 003D4960 也就是寻蛋完成了,然后按 F8 执行,跳转到了 003D4960 这段空间

明的你已经发现了,此时执行的 shellcode 正是 py 脚本中 HOST: 后边的那一部分,抛开这个跳转不谈,此时,你或许有这样的疑问

HOST: 后面的那些我们构造的 shellcode 到底存放在哪呢?

我一开始想到的是栈,因为我们构造的 path 参数的值就在栈里面,那么 HOST 这一部分也应该在里面,但我光速否决了,栈顶是 00a2 开头的跟 003D 相差太大。

那只能是堆了……

判断栈地址还是堆地址直接用快捷键 ALT+M 就可以看到了,其实这个方法是后来大佬给我说的,我当时判断的方法是这样的:

我先在数据窗口 ctrl+G 输入 003D0178

003D0178 指向 003D5178(chunk),我们转到 003D5178

发现 003D5178 指向 003D0178,而 shellcode 的起始位置 003D4960 正好处于 003D0688003D5178 之间,所以这段 shellcode 确实在堆中……

emmmm…… 跟大佬的方法一比,还是对 OD 有点不熟练啊……

接下来我又产生了一个问题:FTP 服务端是什么时候把 HOST: 后面的 shellcode 写到堆中的呢?

要解决这个问题,我们要以一个开发者的思维来考虑,当一个 get 请求来的时候,我们肯定会创建一个堆区来保存 path,Host,Authorization 这些字段的值,据我的开发经验 c/c++ 对堆使用的函数

(1)一个是 malloc() ,动态分配,涉及到堆的分配

(2)一个是 HeapCreate() ,创建一个堆,紧接着用 HeapAlloc() 方法分配堆空间

我倾向于第二种,所以我对 HeapCreate() 下了一个断点( bp HeapCreate ),该函数位于 kernel32.dll 中,重新运行,客户端建立连接,发现 OD 并没有断在该函数上……

emmmm…… 难道我想错了?

我接着陷入了沉思: HeapCreate() 返回的句柄会不会是一个全局变量,而且在我附加到进程之前就已经进行初始化了,所以才没有断下来,那么我在 HeapAlloc() 下断不就可以了吗?因为开发者肯定会在数据到服务端的时候才进行堆分配并赋值的!

接着我把目标转向了 HeapAlloc()。这里要注意一下在 OD 直接对 HeapAlloc() 下断是不行的,因为 kernel32.dll 中的 HeapAlloc() 函数执行时紧接着会调用 ntdll.dll 中的 RtlAllocateHeap() 所以我们直接对 RtlAllocateHeap() 下断(bp RtlAllocateHeap),重启服务器,重新建立连接之后

程序断到了 RtlAllocateHeap() 的入口处,紧接着在数据窗口转到 003D4960 ,观察数据窗口,一直按 F9,会产生第一次突变

此时已经把 path 参数的值写入堆中,然后接着按 F9,会产生第二次突变。

exp 中 Host: 的值,也就是弹出计算器的 shellcode 已经分配到堆中了,整个流程也就分析完了。

0x05 小结

该漏洞是通过 http 的 get 请求提交的超长字符串淹没程序的返回地址,进而控制程序流程,再使用寻蛋技术使程序跳向堆中进行执行我们已经构造好的 shellcode。

0x06 关于寻蛋技术

我这里粗略的讲一下寻蛋技术的概要,通过前面的溯源我们应该都清楚了缓冲区溢出是怎么工作的,以及我们怎么劫持一个程序的执行流程,那么问题来了,如果像这个漏洞一样栈的空间不足放不下那么大的 shellcode 怎么办?

很明显我们通过把 shellcode 放到堆里面,换句话说就是布置 shellcode 在不同的内存区域,如果离的很近那么我们直接用 jmp offerset

如果离的远,就像本例一样,一个在栈,一个在堆。那么我们就需要一个新的技术来找到它,这便是寻蛋技术的由来。蛋指的是 shellcode 的前四个字节,就相当于一个标志头。寻蛋开始时,首先它会搜索整个内存(栈/堆/…)直至找到重复两次找到这个标志。

当找到这个标志, 改变执行流跳转到标志后的 shellcode 执行。相信大家结合这个实例一定会对寻蛋技术有一个更深的体会。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 信安之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 了解 FTP
  • 0x02 搭建实验环境
  • 0x03 poc 的简单介绍
  • 0x04 执行 exp
  • 0x05 小结
  • 0x06 关于寻蛋技术
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档