一个只读的被动扫描器,用于检查 REDCap 主机是否暴露于 **CVE-2026-90817** 并是否已被其入侵——该漏洞是未经身份验证的 survey 端点远程代码执行漏洞,已被 Palo Alto Unit 42 记录的 XMRig 加密货币挖矿活动所利用
redcap_cve_2026_90817_scan.py 是一个只读、被动的扫描器,用于检查
REDCap 主机是否暴露于 CVE-2026-90817 并是否已被其入侵——该漏洞是
未经身份验证的 survey 端点远程代码执行漏洞,被 Palo Alto Unit 42 记录的
XMRig 挖矿活动所利用
(2026-10-07-XMRig-campaign-through-CVE-2026-90817)。
它针对所运行的主机回答两个问题:
此工具仅用于检测。它:
它面向管理员针对自己的 REDCap 服务器运行。
pip 安装。单文件;将其复制
到主机并运行。/var/log/apache2(Debian)或 /var/log/httpd
(RHEL/CentOS/Rocky/Alma),nginx 日志位于 /var/log/nginx。在 RHEL 8 上,系统
python3 为 3.6,因此请在那里调用 python3.9(或更新版本);RHEL/Rocky/Alma 9
自带 3.9+。以 root 身份运行以获得完整扫描(否则每用户 crontab、其他用户的
authorized_keys 和 /proc/<pid>/exe 以及部分日志将无法读取)。非 root 身份下它仍可运行,
并会明确将结果标记为部分扫描。# Simplest: defaults to /var/www/html/redcap, auto-detects the mitigation date
sudo python3 -I redcap_cve_2026_90817_scan.py
# Point at a non-default install, pin the mitigation date, list every attack entry
sudo python3 -I redcap_cve_2026_90817_scan.py \
--redcap-root /var/https/redcap \
--mitigation-date 2026-09-21 \
--max-log-matches all
# Machine-readable output for monitoring / ticketing
sudo python3 -I redcap_cve_2026_90817_scan.py --json
在 Windows 上,请从提升权限的命令提示符运行,并将 --redcap-root 指向安装目录(平台和默认日志目录会自动检测):
# IIS or Apache/XAMPP; logs under C:\inetpub\logs\LogFiles or the Apache logs dir
python -I redcap_cve_2026_90817_scan.py --redcap-root C:\redcap --json
-I 以隔离模式运行 Python(忽略 $PYTHONPATH 和当前工作目录)——在扫描可能已被篡改的主机时,这是一种良好的卫生习惯。
| 标志 | 默认值 | 用途 |
|---|---|---|
--redcap-root PATH | /var/www/html/redcap | REDCap 安装根目录。 |
--log-dir DIR(可重复) | 平台相关(Linux 上的 Apache/nginx;Windows 上的 IIS LogFiles + Apache) | 要扫描的 Web 服务器日志目录(处理轮转的 .gz 和 IIS W3C 日志)。 |
--home-dir DIR(可重复) | /root + 每个 /home/*(Linux);每个 C:\Users\*(Windows) | 额外要扫描的主目录,用于查找 SSH 密钥 / shell/配置文件。 |
--platform auto|linux|windows | auto | 要假定的主机平台。从操作系统自动检测;强制指定以选择提供程序(操作系统特定的主机探测仍仅在匹配的实时操作系统上运行)。 |
--mitigation-date YYYY-MM-DD | 自动检测 | 通过升级修复该漏洞的日期。参见攻击时间线。 |
--since YYYY-MM-DD | — | 仅检查此日期当天或之后的 Web 日志条目(含当天)。最后修改时间早于此的日志文件不会被打开。适用于两轮日志检查;主机状态检查不受影响。参见攻击时间线。 |
--until YYYY-MM-DD | — | 仅检查此日期当天或之前的 Web 日志条目(含当天)。 |
--full-hash-scan | 关闭 | 递归计算 temp/cache 和暂存目录下文件的哈希(较慢)。 |
--max-log-matches N | all | 100 | 每个类别列出多少条攻击日志条目,保持原样。 |
--attacker-ip IP(可重复) | — | 额外的已知攻击者 IP(例如来自另一台主机的扫描)。按文档中的 IOC IP 同等对待。参见第二轮。 |
--json | 关闭 | 输出 JSON 而非文本报告。 |
--quiet | 关闭 | 在文本输出中抑制 OK/INFO 行。 |
| 代码 | 含义 |
|---|---|
0 | 干净且已修补,并且扫描完全完成。 |
1 | 一项或多项发现(存在漏洞的版本和/或 IOC/攻击)——需要调查。 |
2 | 扫描无法完全完成(出现错误,或非特权且在其他方面干净——无法完全断言干净结果)。 |
每个文档化的 IOC 都映射到一个被动本地检查:
redcap_vX.Y.Z 目录名读取已安装版本,并与已修补版本进行比较(16.0.49 LTS / 17.3.10 LTS / 17.4.4 Standard;受影响下限为 13.3.0)。temp/cache 中的已知恶意文件 — 该目录(以及暂存目录)中的每个文件都会与 Unit 42 dropper 的 SHA-256 进行哈希比对。注意:该工具不会按 .bladec 扩展名标记文件。REDCap 核心使用 BladeOne 模板引擎,该引擎会将哈希命名的 .bladec 编译模板作为正常输出写入 temp/cache,因此该扩展名并非入侵指标;真正恶意的投放文件会通过其内容哈希被捕获,无论扩展名如何。.syslog-* 目录、xmrig* 归档,以及该活动锁定为权限 000 的竞争目录(/dev/shm/duet、/var/tmp/.bin)。/var/tmp、/dev/shm、/tmp 运行的可执行文件,以及 syslog-ng-* 伪装进程。127.0.0.1:50001 上的本地 XMRig JSON-RPC API,以及与攻击者源 IP 的活动连接——包括文档中的 IP、观察到的 IP、任何 --attacker-ip,以及 Web 日志检查发现的每个 IP。authorized_keys — 与攻击者后门指纹匹配的密钥。(它有意不列出无关密钥供“审查”——在正常主机上,那些密钥属于真实账户,且无法独立验证。)~/.profile — 注入的启动代码和 HISTFILE 反取证。__passthru 和 route= 本身是合法的 REDCap 参数 —— 外部模块通过 __passthru 进行路由,而正常的调查流量使用普通的 __passthru=index.php 来访问 PdfController 和 SendItController 等良性控制器。因此,扫描器绝不会仅凭这些参数进行匹配。只有当日志行显示以下情况时,才会被视为攻击形态:
__passthru 到达 DataImportController(报告中记录的汇聚点),无论采用何种编码;或__passthru 到达任何内部 …Controller —— 例如 __passthru=%2569ndex.php&route=ParcelController:show。这是该攻击活动的可泛化特征:合法客户端发送的是普通的、字面量 index.php,绝不会是双重编码的版本,因此混淆(而非控制器名称、也非 HTTP 状态码)才是区分攻击的关键。这正是能够捕获除已记录 IOC IP 之外的其他攻击者主机的依据;或部分请求被单独归类为侦察(MEDIUM),而非攻击形态请求:
--attacker-ip,或下方观察到的 IP。/redcap/surveys/surveys?s=…、/redcap/surveys/redcap/surveys/、/redcap/redcap/surveys/。这是攻击工具中的路径拼接缺陷。/surveys 请求上出现的工具生成的用户代理。这意味着 Gecko 令牌中包含日期时间,例如 Gecko/8618-10-13 16:07:47.714447 Firefox/3.8,即 Python Faker 生成的那类字符串。真实浏览器发送的是例如 Gecko/20100101。仅匹配上述两种探测模式之一的日志行会被列为侦察,但不计入攻击总数或攻击时间线判定。任一模式都可能来自良性来源,例如失效链接或异常客户端。当同一行来自已知攻击者 IP 时,则照常计入。
这些 IP 在 2026 年 10 月向 UF REDCap 主机发送了攻击形态请求。它们的处理方式与已记录的 IP 完全相同:
| IP | 首次出现 | 行为 |
|---|---|---|
| 72.167.149.182 | 2026-10-14 | python-requests;基础路径探测,随后 ParcelController |
| 18.163.208.19 | 2026-10-15 | python-requests;基础路径探测,随后 ParcelController |
| 159.65.159.30, 45.225.183.6, 159.89.115.81, 165.22.168.95 | 2026-10-19 | 伪造的用户代理,成对 IP;ParcelController |
| 5.180.77.102 | 2026-10-19 | DataImportController;与 202.61.130.221 相同的调查目标和重复路径缺陷 |
大多数是可能被重新分配的云主机,因此每条证据行都会显示该 IP 的来源。当条目过期时,从 IOC_OBSERVED_IPS 中移除。
以下内容在同一流量中出现,但不是 IOC:
python-requests:过于通用。由于检测依据是请求模式而非 IP,扫描器会汇总所有发出攻击形态请求的不同源 IP,并标出已记录 IOC IP 之外的任何 IP。每个攻击命中还会按 HTTP 状态码排序:2xx 表示 __passthru 未经认证到达了控制器,而非 2xx 则是未到达控制器的尝试。到达控制器属于可达性,并非代码已执行或主机已失陷的证明 —— 主机状态检查才是确认实际入侵的依据。
匹配的条目会逐字保留且不截断,每条都加上其解析日期、匹配原因和状态排序作为前缀,从而保留原始证据以供审查。
每个被发现发出类似漏洞利用请求的 IP 都会经过第二轮处理:
日志。 收集来自这些 IP 的所有其他请求——探测、路径猜测、后续活动。这些行不属于漏洞利用形态,因此第一轮会跳过它们。它们会以 Other traffic from N attacker IP(s) discovered by pattern(MEDIUM)的形式报告。
主机状态。 在这些 IP 相关的活动套接字、计划任务以及启动/配置文件进行搜索。命中即为 HIGH,并强制判定为 LIKELY COMPROMISED,与命中已记录的 IOC IP 相同。
候选 IOC。 扫描器收集所有攻击者流量的特征,并与已知信息进行比对。它会在文本报告中的 Candidate IOCs for review 下列出尚未覆盖的任何内容,并在 --json 中作为 candidate_iocs 输出:
KNOWN_EXPLOIT_CONTROLLERS 中的控制器;: → %3A 的编码变化会显现出来);候选按从旧到新列出,以便你了解攻击随时间的变化。
候选绝不会被自动添加。请审查它们,如果它们具有泛化价值,再更新 IOC 常量块。要在主机之间共享 IP,请通过 --attacker-ip 将它们传递给其他主机的扫描。候选 survey 哈希会以 ?s=HASH 形式打印,因此 sanitize_scan_result.py 会像处理其他哈希一样对其进行掩码。
如果主机在磁盘上未被入侵,那么已记录的攻是否重要取决于它们相对于补丁发生的时间。扫描器会将每个攻击条目按缓解日期分为之前 / 当天 / 之后,并据此设置判定:
| 攻击时间 | 判定 |
|---|---|
| 缓解日期之前 | LIKELY COMPROMISED(在存在漏洞期间遭到攻击) |
| 缓解日期当天 | POSSIBLY COMPROMISED — 将每次攻击的时间与实际升级时间戳进行比较 |
| 缓解日期之后 | ATTACKED but NOT LIKELY COMPROMISED |
缓解日期来自 --mitigation-date,如果省略,则从最早已修补的 redcap_vX.Y.Z 目录的安装 mtime 推断(即主机首次运行非易受攻击版本的时间)。报告头部会显示使用了哪个来源。主机状态 IOC(已知恶意文件哈希、挖矿程序、持久化)无论时间如何,始终强制判定为 LIKELY COMPROMISED。