夜雨聆风学习资料网

ARTICLE · 1259501

这个插件化扫描框架,把Redis未授权和Struts2全家桶塞进了命令行

这个插件化扫描框架,把Redis未授权和Struts2全家桶塞进了命令行

免责声明:本文仅用于安全技术研究与交流,请勿在未获得明确授权的情况下对任何网络资产进行探测、攻击或漏洞利用。读者需自行承担因使用本文内容所产生的全部法律责任。

TTLScan 是一个以 Python 编写的插件化漏洞扫描框架,主程序负责命令行解析、输入源调度与插件动态加载,具体探测逻辑全部下沉到独立插件文件中。框架当前内置 9 个验证脚本,覆盖 Redis 未授权访问、Redis 写公钥以及 Struts2 系列远程命令执行,支持单 IP、IP 列表、URL、URL 列表四类目标输入,并可借助 ZoomEye 检索接口批量获取目标集合。

重点导读定位

安全工程师在测试自有或授权范围内的资产时,常见的做法是取来一份 POC 集合逐个手工运行:复制脚本、修改目标地址、执行、记录结果。目标数量一旦上升到 IP 列表级别,重复的复制粘贴与参数替换就取代了探测本身成为主要开销。

该框架把这一过程抽象为两层:上层是输入源与调度,下层是插件。每个插件只需实现两个函数即可被框架装载并执行,新增探测类型不需要修改主程序。框架面向的是资产探测阶段的一次性验证,不承担资产测绘、漏洞管理或持续监控类平台的职能。

重点导读目录

项目结构如下:

textTTLScan/
├── ttlscan.py            # 主程序与命令行入口
├── config/
│   ├── __Init__.py
│   └── config.py         # ZoomEye 接口地址与鉴权头
├── lib/
│   ├── __init__.py
│   ├── parser_zoomeye.py # ZoomEye 响应解析
│   └── ttlscanlogger.py  # 彩色日志输出
├── plugins/
│   ├── ip/
│   │   ├── redis_un.py       # Redis 未授权访问
│   │   └── redis_getshell.py # Redis 写键验证
│   └── url/
│       ├── struts2_005.py
│       ├── struts2_016.py
│       ├── struts2_019.py
│       ├── struts2_032.py
│       ├── struts2_033.py
│       ├── struts2_037.py
│       └── struts2_045.py
├── images/
├── requirement.txt
├── ip.txt
├── LICENSE
└── README.md

重点导读入口

主程序 ttlscan.py 是一段线性脚本,全部逻辑位于 __main__ 分支内。启动后依次完成三件事:

  1. 打印 ASCII 字符画横幅,记录起始时间。
  2. 使用 argparse 构造 8 个命令行参数。
  3. 按输入源类型分派到对应插件。

参数集合如下:

参数 含义
--ip 单个 IP 目标
--ip_list IP 列表文件
--target_url 单个 URL 目标
--target_url_list URL 列表文件
--search 搜索引擎名称,当前为 zoomeye
--search_page 检索页数,默认 20
--script 调用的插件名
--query 查询功能,当前支持 scripts

分派逻辑有两条前置校验。目标字符串先经过正则匹配判断是否为合法 IPv4 地址,非法值直接跳过不进入探测流程。插件则通过 importlib.import_module 按 plugins.ip.{script} 或 plugins.url.{script} 的路径动态载入,插件名由命令行参数拼接,因此脚本名与文件名必须完全一致。

--query scripts 是内置的查询功能:程序遍历 plugins/url 与 plugins/ip 两个目录,筛出以 .py 结尾且文件名中不含 init 的文件,去掉扩展名后以空格分隔打印,供使用者确认可用的插件标识。

重点导读插件

插件按目标类型分置两个目录,共 9 个文件。ip 目录下的插件接收 IP 作为唯一入参,url 目录下的插件接收完整 URL。

ip 目录包含两个 Redis 相关脚本。url 目录包含 Struts2 的 005、016、019、032、033、037、045 共七个编号脚本,对应 Struts2 历史上不同批次的 OGNL 注入问题,其差异在于触发点与表达式构造方式,而非请求方法。

插件加载采取整模块导入的方式,一次性执行模块级代码,之后每次目标探测都是对模块内函数的调用。这意味着插件文件本身也可以作为独立脚本执行,仓库中每个插件末尾都保留了 __main__ 测试入口。

重点导读契约

框架与插件之间的接口约定收敛到两个函数。

POC_INFO() 返回一个字典,包含 name、Chinese_name、author、port 四类元信息。name 与 Chinese_name 描述漏洞类型,author 记录贡献者,port 给出该漏洞对应的服务端口。port 字段并非仅作说明,ZoomEye 分支直接读取它作为检索条件,缺少该字段的插件无法参与搜索引擎目标获取。

POC(target) 执行实际探测,命中时返回 True,未命中返回 False,异常同样被捕获并转为 False。返回值本身不是框架的判定依据,框架依赖插件内部写入日志来输出漏洞信息,探测结果以日志中的 Vuln 标记行为准。

函数命名全部大写,这是插件被动态调用时能够被正确识别的前提。

重点导读输入源

框架提供四种直接目标输入,全部通过命令行参数给出。

单目标模式使用 --ip 或 --target_url。列表模式使用 --ip_list 或 --target_url_list,文件内容按行读取并逐行去除首尾空白。同名单值参数与列表参数同时出现时,单值参数优先,列表分支不会被执行。

IP 目标在进入插件前会做 IPv4 格式校验,URL 目标不做额外格式判断,原样传给插件。

重点导读检索

--search zoomeye 分支把框架与 ZoomEye 的 host 检索接口对接,用于在真实网段中获取具备特定端口特征的目标集合。

执行过程如下:

  1. 载入 ip 类插件,调用 POC_INFO() 取得 port 字段。
  2. 以 port:{port} 构造检索条件,按 --search_page 指定的页数循环请求。
  3. 解析每页响应,取出 matches 数组中的 ip 字段,合并为候选集合。
  4. 对集合中的每个 IP 做格式校验,通过后逐个执行插件。

接口地址与鉴权头集中在 config/config.py 中定义,检索时直接读取,避免硬编码散落在各处。搜索页数默认取 20。

重点导读目标获取

redis_un.py 不依赖 Redis 客户端库,直接用 socket 发送 RESP 协议中的 INFO 命令并读取响应,只要响应中包含 redis 标识即判定为未授权访问。连接超时设置为 2 秒,覆盖大量不可达地址。

redis_getshell.py 在确认端口可访问后,通过 redis-py 的连接池写入一个测试键并回读比对,借此判断实例是否真正具备写入能力。

重点导读探测

url 类插件通过构造特定请求并检查响应特征来确认漏洞。struts2_005 使用查询串注入 OGNL 表达式,使服务端回写一个固定标记,在响应正文中出现该标记即判定命中。

struts2_045 采用 multipart 文件上传路径,把 OGNL 表达式放在 Content-Type 头部中,并在服务端执行命令回显标记字符串后比对响应。

其余编号脚本沿用同一套判定模式:构造触发载荷,发出请求,在返回内容中查找预设标记。

重点导读配置

config/config.py 内容精简,包含三项配置:access_token 存放 ZoomEye 访问令牌,zoomeye_search_api 是检索接口模板,headers 是携带鉴权头的请求头字典。

令牌通过 ZoomEye 官方登录接口获取,接口接受用户名与密码,返回 JWT 格式的 access_token,后续请求在 Authorization 头中以 JWT 前缀携带。

python#coding=utf-8
#Zoomeye Token
access_token=""

#Zoomeye Search API
zoomeye_search_api="https://api.zoomeye.org/host/search?query={0}&page={1}"

#Zoomeye Headers
headers={
    "Authorization":"JWT "+access_token
}

重点导读日志

lib/ttlscanlogger.py 定义了名为 autotest 的 logger,级别为 DEBUG,通过 colorlog 着色器渲染,带时间戳、文件名与消息三段内容。漏洞命中信息使用 error 级别输出,因此终端中与常规流程日志在颜色上可区分。日志直接输出到控制台,未做文件落盘。

重点导读依赖

requirement.txt 列出两个第三方包:redis 用于 Redis 客户端通信,colorlog 用于彩色日志输出。此外主程序还直接依赖 requests 发起 HTTP 请求。

代码库中的插件使用 Python 2 语法,包括 print 语句与 except Exception, e 形式,在 Python 3 下无法直接执行,需要按 Python 2 解释器运行或自行完成语法迁移。

重点导读用法

查询可用插件:

bashpython ttlscan.py --query scripts

命令输出为插件标识列表,对应关系如下图所示:

TTLScan 查询可用插件的输出

对单个 IP 执行 Redis 未授权检测:

bashpython ttlscan.py --ip 1.1.1.1 --script redis_un

对 IP 列表批量执行:

bashpython ttlscan.py --ip_list ip.txt --script redis_getshell

对 URL 执行 Struts2 检测:

bashpython ttlscan.py --target_url http://example.com/ --script struts2_045

通过 ZoomEye 获取目标集合后批量探测:

bashpython ttlscan.py --search zoomeye --search_page 20 --script redis_un

程序启动时打印的字符画横幅如下:

TTLScan 启动横幅

重点导读细节

源码中存在若干值得注意的实现细节。

一是 URL 列表分支的循环体内调用的是 --target_url 而非当前迭代变量 url,多个目标实际不会被逐个探测。ip 列表分支不存在该问题。

二是 zoomeye 检索循环中,接口模板的 page 参数传入的是 search_page 而非页码变量,分页结果会重复请求同一页。

三是 redis_getshell.py 的 POC_INFO() 缺少 return 语句,返回 None,在 ZoomEye 分支读取 port 字段时会触发异常。

四是 struts2_045.py 使用了 multipart_encode 与 urllib2,但文件中未导入 multipart_encode,异常被捕获后函数直接返回 False。

五是 ip.txt 提供了默认的目标列表样例,使用前需按自身测试范围调整。

重点导读小结

TTLScan 的价值在于把分散的 POC 收敛为统一的插件契约:主程序只处理输入、调度与日志,探测逻辑各自独立,新增脚本的成本被压缩到单个文件内。框架同时把 ZoomEye 检索接入批量流程,使从目标获取到漏洞验证形成闭环。

受限于 Python 2 依赖、单线程串行执行、部分分支的参数传递缺陷以及未落盘的结果记录,该框架更适合授权范围内的中小规模资产验证与 POC 效果复测,而非生产环境的高并发扫描。

重点导读源码

本公众号非项目作者,仅做技术分享。本文介绍的项目开源地址如下:

texthttps://github.com/tiaotiaolong/TTLScan

广告时间

低价考证包括但不限于CISP系列、PMP等等国内网安证书、网络安全交流群请关注公众号后点菜单栏的找棉花糖。

糖心会员站,网络安全必备网站,包括在线内网靶场、web靶场、src靶场、应急响应靶场,以及各种网安资料、教程、方案模版、以及超级多在线工具,99元包年!详细介绍:棉花糖会员站介绍(26年4月26日版本) :在线内网靶场、网安资料方案、在线工具全能资源站,看完介绍百分百心动!

相关学习资料