基本步骤
- 理解漏洞
- 漏洞特性
- 触发条件
- 影响利用
- 复现漏洞
根据复现教程,结合靶场和实际漏洞进行复现。同时还应该在此环境进一步强化第一步理解漏洞。
比如:Apache Kafka Connect CVE-2023-25194 JNDI 注入漏洞
网上有靶场的复现教程和自己搭建环境的复现教程,靶场的复现教程没啥很大难度,但是自己搭建环境复现这个环境就比较繁琐出了很多问题。
我可以简单地以我写这个插件的过程为例子简单剖析漏洞复现结合理解漏洞的重要性。
首先,编写这个漏洞我是参照如下博客: https://blog.csdn.net/qq_40646572/article/details/130672387
搭建 vulnhub 靶场进行复现,采用 dnslog 回显日志记录验证即可,但是触发了如下几个问题:
(1)路径和 payload 通用性
(2)漏洞触发因素模糊
(3)指纹是服务指纹还是 web 指纹
于是乎,本着解铃还须系铃人的原则,我开始搭建环境验证漏洞,加强漏洞理解顺便回答以上存在疑问的问题。
参考知乎文章:https://zhuanlan.zhihu.com/p/616398569
完成 kafka 漏洞环境的搭建和复现,从搭建完那一刻我就清晰漏洞触发原因(开启了服务),触发路径,和触发通用 payload 应该是怎样的。之前的 payload 只能针对靶场,一旦放入实际环境中,基本没中的。当然这个实际资产环境也可以根据具体产品改编 POC。(下一步骤提及)
-
编写 POC
这里可以根据复现漏洞的环境进行编写测试,同时也可以将一个通用漏洞(比如 fastjson)结合不同产品(海康威视…)对应路径和 POC 进行细分 POC 编写。并且在这个编写步骤中也应该着重考虑一些友好的规范因素(在下一标题会详细总结)。 -
充分测试和验证
这是一个长久的持续利用过程,并且需要结合下一步骤不断优化,形成一条友好的生态圈。
关于测试和验证我个人目前觉得比较不错的几种方法是:
(1)客户使用反馈记录,迭代问题报告。
(2)对某些资产或者通用型漏洞周五部署编辑好,周末批量测试(前提,确保 POC 无害性,以及一些危害操作和敏感资产处理)。不断迭代,形成一份长久的测试数据,同时根据测试的问题,不断迭代测试方案和测试最佳流程。
(3)靶场覆盖数据维护
(4)可以结合现有 POC,以小组形式一起编写批量漏洞挖掘提交脚本,在各种漏洞平台提交 nday(个人能力提升,问题发现,知名度,团队核心凝聚力)
- 更新和维护
随着时间的推移,漏洞的修复和新的检测方式可能会出现,并且在上一步骤测试过程会出现 POC 需要优化的情况。
一些友好的编写规范
准确性
- 关键字匹配
- 特征码识别
- 可预测的变化检测
关键字匹配
以命令执行举例,通常会使用 phpinfo,echo,print 这些命令用来检测,但是也还是会产生一些无法避免的误报问题。
- 页面本身就是一个打印 phpinfo 的页面
- 目标网站将你的输入,原样的输出在返回的 body
特征码识别
按照漏洞逻辑执行完后,直接通过判断 HTTP 响应状态码,判断漏洞是否存在。
这是很大概率会产生误报的行为,直接根据特征码判断是不可取的。
可预测的变化检测
简单来说就是对直接生成的随机数加上一些加减乘除编码加密操作
- 代码执行
1 | print({{randint(8)}} * {{randint(8)}}),匹配相乘后的结果 |
1 | print("{{randstr(8)}}" + "{{randstr(8)}}" ),匹配字符串拼接后的结果 |
1 | print(md5({{randstr(16)}})),匹配 md5 后的结果 |
1 | printf("{{randstr(16)}}%%{{randstr(16)}}")实际输出会少一个百分号,检出时只需要匹配 {{randstr(16)}}%{{randstr(16)}}即可 |
- 命令执行
- Linux 下可以使用
1 | expr {{randint(8)}} - {{randint(7)}},匹配相减后的数字即可 |
1 | echo aaaa""bbbb,echo aaaa''bbbb,匹配 aaaabbbb 即可 |
1 | echo aaaa\bbbb,匹配 aaaabbbb 即可 |
- Windows 下可以使用
1 | set /a result={{randint(8)}}-{{randint(7)}} && call echo %result%,匹配相减后的数字即可 |
- SQL 注入类型
1 | select md5({{randstr(16)}}),匹配 md5 后的结果 |
1 | select concat('{{randstr(8)}}','{{randstr(8)}}'),匹配字符串拼接后的结果 |
- 文件读取
- Linux 下
1 | root:.*:0:0:,使用正则进行匹配 |
others
综上,为了确保 POC 的准确性,采用状态码 + 可预测的变化检测是一种比较友好的方式,但是并不是所有的 POC 都可以这么做,还得根据特定漏洞来定制,当可预测的变化检测没有想到合适的时候,可以结合状态码 + 关键字匹配,或者多个条件 if 验证,也可以加强 POC 的准确性。并且,还需要考虑 POC 的应用场景,当 POC 是运用到一个自动化渗透测试系统中时,可以针对特定产品 POC 特殊考虑,有时候对指纹识别的准确提高也可以加强 POC 的准确性。
通用性
在编写 PoC 时,应该确保 Payload 或包含的检测代码兼顾各个环境或平台,比如 ipconfig 和 ifconfig,id,cat\etc\passwd 等
- 构建通用的 Payload
- 考虑常见的安全防护机制
- 无法通用检测时,尽可能覆盖多种情况
构建通用的 Payload
能通过代码执行的漏洞,就不要再通过代码调用系统命令来执行。
使用 system 去执行命令来进行检测,这种方式存在很多的弊端:
- 不够通用,Windows 下和 Linux 下可能没有足够通用的命令用于检测
- 没有考虑安全防护机制,比如 PHP 经常存在的 disable_functions 以及一些 WAF 之类的
考虑常见的安全防护机制
结合前面说到的,建议选择打印随机字符串来进行检测,或其他的无害化的方式检测漏洞,降低触发其安全防护机制的概率。
无法通用检测时,尽可能覆盖多种情况
-
系统不确定
这种情况常见于命令执行、文件读取漏洞的情况,可以根据对应平台分别发送检测 Payload 进行检测,如发送两个检测包,第一个是针对 Windows 的,第二个是针对 Linux 的即可。 -
多种 payload
当有多种检测 payload 时,可以写成 for 循环进行测试,也可以使用 if 条件语句进行测试。
无害性
- 确保编写的 PoC 不会对目标造成危害。包括但不限于,插入,删除,篡改等等直接或间接产生危害的操作。
- 对于文件上传类漏洞,请尝试清理 PoC 测试中产生的测试文件,即在访问该文件后删除自身
- php
1 | <?php echo md5(233);unlink(__FILE__);?> |
- asp
1 | <% |
- aspx
1 | <%@Page Language="C#"%> |
- jsp
1 | <% out.println(new String(new sun.misc.BASE64Decoder().decodeBuffer("ZTE2NTQyMTExMGJhMDMwOTlhMWMwMzkzMzczYzViNDM=")));new java.io.File(application.getRealPath(request.getServletPath())).delete(); %> |
- jspx
1 | <jsp:root xmlns="http://www.w3.org/1999/xhtml" version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jsp/jstl/core"> |
随机性
- 避免固定字符和个人信息
- 使用随机字符串或哈希值,增加 Payload 的多样性和复杂性,提高对目标系统的覆盖能力
- 规避安全设备的特征识别
常见 POC 框架
-
Pocsuite
Pocsuite 框架现为知道创宇 Seebug 平台通用的漏洞验证框架,使用 Python 编写 POC。可以提交 POC 换 kb, kb 可以用来兑换现金,挣点零花钱还是相当不错的。老司机们可能听过 Sebug, 那是 Seebug 的前身, 2016 年 Sebug 收购了另一个优秀框架 Beebeeto 后,更名为 Seebug。 -
Tangscan
Tangscan (唐朝扫描器) 是 wooyun 社区的官方框架,使用 Python 编写 POC。可以提交 POC 换汤圆,参与现金分红。 -
Bugscan
Bugscan 是四叶草的官方框架,使用 Python 编写 POC。提交 POC 插件获取 rank 奖励,可兑换实物奖励,奖品还是蛮丰富的。