漏洞验证和利用代码编写指南

漏洞验证准则已有人总结过 《漏洞检测的那些事儿》:
文章里很好的提出编写漏洞验证代码时需要注意的 三个准则 ,简单总结和补充如下:
1. 随机性
保证关键变量、数据和无明显含义要求的值应该具有随机性 。
如: 上传文件的文件名,webshell 密码,print 的值,探测 404 页面使用的路径等 。
2. 确定性
通过返回的内容找到唯一确定的标识来说明该漏洞是否存在 。
验证漏洞尽可能达到与漏洞利用时同样的水准,勿使用单一模糊的条件去判断,如HTTP状态码、固定页面内容等来判定漏洞是否存在 。
比如验证文件上传漏洞,最好上传真实的文件进行判断;验证通过不常见的API接口未授权添加管理员,仅是通过判断不常见的API接口是否存在来判定漏洞是否存在是不够的,最好是要实际去添加一个管理员用户,最后按照添加成功与否来判定这个漏洞是否存在 。
3. 通用性
兼顾各个环境或平台,兼顾存在漏洞应用的多个常见版本 。
勿只考虑漏洞复现的单一环境,要考虑到存在漏洞的应用的不同版本、安装应用的不同操作系统、API接口、参数名、路径前缀、执行命令等的不同情况 。
4. 无损性
有效验证漏洞的前提下尽可能避免对目标造成损害 。
验证漏洞时,在有效验证漏洞的前提下,尽量不改写、添加、删除数据,不上传、删除文件 。可以的话,验证漏洞完毕后应恢复数据和验证漏洞前的数据一致 。
漏洞验证方法如果根据实际可操作性,对主流的漏洞验证方法定义,梳理和总结如下:
一. 直接判断
即可直接通过目标的不同响应和状态来判断目标是否存在漏洞,主要包括下面四种方法:
1. 结果回显判断
最直接的漏洞存在的判定方法,受我们的输入控制影响,目标响应中完整输出了我们期望的结果 。
2. 报错回显判断
使目标处理我们输入的数据时内部错误,并在错误的输出中携带了受我们期望的结果 。
3. 写数据读取判断
将结果或标志写入目标文件或数据库等类似数据存储系统,并尝试读取存储的内容来判断目标是否存在漏的方法 。
4. 延时判断
通过控制在目标机器上执行的代码,让目标机器等待N秒后再响应我们的请求 。
在延时SQL注入、执行命令 sleep、执行代码 sleep等漏洞判定应用场景里常有不可替代的重要作用 。
二. 间接判断
通过控制目标向第三方发送信息,通过第三方是否接收到信息来判定目标是否存在漏洞,主要包括下面几种方法:
1.DNSLOG 方式判断
当目标可解析域名并且允许请求外网 DNS 服务器时使用,因为有部分机器默认允许请求外网 DNS 服务器而且防火墙也不会轻易拦截,所以此方法已被广泛使用 。JAVA 反序列化中的 URLDNS payload 就是属于此判断方法 。
2.WEBLOG 方式判断
在目标可以对外发送 TCP 请求时,使用 Web 服务器接收目标发送而来的请求,以此来判断我们可以控制目标发送请求到特定第三方 Web 服务器,目标存在漏洞 。
虽然灵活运用各种漏洞验证方法可以有效的验证漏洞是否存在,但是对于仅使用单一方法来验证漏洞是否存在时,我倾向于下面的方法优先级:
结果回显判断 > 报错回显判断 > 写文件读取判断 > 延时判断 > DNSLOG 方式判断 > WEBLOG 方式判断
漏洞利用准则之所以把漏洞利用和漏洞验证分开来叙述,是因为在我看来漏洞利用才是安全研究人员需要额外注意的部分,也是最能体现安全研究水平和代码编写水准的方面 。
不少安全研究人员可能仅出于研究目的,或因为怕研究成果被恶意利用,再加上编写 漏洞验证 代码通常比真实的 漏洞利用 代码更为简单,所以通常仅是给出一个十分简单的漏洞验证步骤或 demo 代码 。漏洞之所以被重视,根本原因是某些漏洞被利用后能对目标造成很大的损害,这不是一个 CVE 编号或者高中低危评价就能够衡量的,而是由真实的漏洞利用代码来评判的 。
结合自己的漏洞利用代码编写经验,遵守的准则主要有以下几个部分:
1.结果回显优先
优先将漏洞成功利用获得的信息显示出来 。
比如对于一个命令执行漏洞,漏洞利用代码应该朝着直接获得执行的命令的输出结果去努力,而不是一开始就去尝试做反弹 shell、写文件读取达到回显效果这种事 。
具体说,我曾经编写过一份结合 CVE-2017-12635和CVE-2017-12636 两个漏洞的代码 。CouchDB先垂直越权添加管理员用户,然后利用添加的管理员用户通过Authorization头认证,创建新数据库,将执行命令的结果存储到该数据库,最后从该数据库中读取执行命令的结果,再删除该数据库,从而达到执行命令结果回显的目的 。


推荐阅读