适合人群:PHP安全研究员、CMS漏洞挖掘者、渗透测试工程师
前置知识:PHP基础、WordPress架构、代码审计基础
一、前置准备
审计目标: WordPress 7.0(2026年最新版)
源码规模: 1496个PHP文件,620,000行代码
审计环境: PHP 8.3 + MySQL 8.0 + Docker
审计工具: Semgrep + 人工代码审计(50+核心文件深度分析)
二、漏洞全景
2.1 REST API Widget反序列化RCE(CVSS 9.8)
位置: wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php:589
$serialized_instance = base64_decode( $request['instance']['encoded'] );
if ( ! hash_equals( wp_hash( $serialized_instance ), $request['instance']['hash'] ) ) {
return new WP_Error( ... );
}
$instance = unserialize( $serialized_instance ); // ← 反序列化!
原理: 用户提交的encoded实例经过base64解码后进行unserialize()。虽然后HMAC校验用wp_hash()保护,但一旦AUTH_KEY/SECURE_AUTH_KEY泄露(通过LFI/SSRF/备份泄露),攻击者可构造合法HMAC签名触发反序列化RCE。
影响范围: 所有使用REST API的WordPress 7.0站点。
2.2 maybe_unserialize() 链式触发RCE(CVSS 9.8)
位置: wp-includes/functions.php:655
function maybe_unserialize( $data ) {
if ( is_serialized( $data ) ) {
return @unserialize( trim( $data ) ); // ← 反序列化入口!
}
return $data;
}
调用链: get_option() → get_metadata() → get_post_meta() → REST API → maybe_unserialize()
这是WordPress最深层也是最危险的反序列化入口。整个核心的数据读取都经过这个函数:
| API | 调用链路 | 数据来源 |
|---|---|---|
get_option() | option → maybe_unserialize | wp_options表 |
get_post_meta() | meta → maybe_unserialize | wp_postmeta表 |
get_user_meta() | meta → maybe_unserialize | wp_usermeta表 |
get_term_meta() | meta → maybe_unserialize | wp_termmeta表 |
get_comment_meta() | meta → maybe_unserialize | wp_commentmeta表 |
get_site_option() | sitemeta → maybe_unserialize | wp_sitemeta表 |
本地验证: Docker环境的wp_options表中确认大量序列化数据(cron: 1484B, rewrite_rules: 8494B, wp_user_roles: 3133B)。
攻击链: SQL注入/权限提升 → 写入恶意序列化option → get_option() → maybe_unserialize() → POP链代码执行
2.3 XML-RPC Pingback SSRF(CVSS 8.6)
位置: wp-includes/class-wp-xmlrpc-server.php:7039
$pagelinkedfrom = $args[0]; // 用户完全可控!
$request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args );
原理: pingback.ping方法无需认证,接受用户控制的URL并请求。
IPv6绕过技巧: wp_http_validate_url()没有过滤IPv6回环地址:
http://[::1]:3306/→ 绕过IPv4检查http://[::ffff:127.0.0.1]/→ 另一种IPv6表示法- DNS Rebinding: 初始DNS解析返回合法IP,后续绑定到内网IP
2.4 URL Details SSRF(CVSS 7.5)
位置: wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php:254
$response = wp_safe_remote_get( $url, $args );
/wp-block-editor/v1/url-details路由,任意edit_posts权限用户(Subscriber+)可触发SSRF,返回目标页面的标题/Meta/图标。
2.5 XML-RPC文件上传MIME绕过(CVSS 7.5)
位置: wp-includes/functions.php:2910
$wp_filetype = wp_check_filetype( $name ); // 仅检查扩展名!
if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) ) {
return array( 'error' => ... ); // 未检查文件内容!
}
mw_newMediaObject使用wp_upload_bits() → wp_check_filetype()只检查文件扩展名不检查内容。与正常上传使用的wp_check_filetype_and_ext()(含内容检查)不同。
三、攻击链分析
Chain A: LFI → 密钥泄露 → Widget反序列化RCE
LFI读wp-config.php → 获取AUTH_KEY/SECURE_AUTH_KEY
→ 构造恶意序列化payload → base64编码
→ wp_hash(payload) 计算HMAC
→ POST /wp/v2/widgets {instance: {encoded, hash}}
→ unserialize() → POP链 → RCE
Chain B: SQL注入 → option注入 → maybe_unserialize RCE
SQL注入 → UPDATE wp_options SET option_value='恶意序列化数据'
→ 任意页面加载触发get_option()
→ maybe_unserialize() → POP链 → RCE
Chain C: RCE校验(完整HTML页面生成)
SSRF读云元数据 → 获取AK/密钥
→ 配合Chain A → 完整RCE
四、受影响文件清单
| 文件 | 行号 | 漏洞类型 | CVSS |
|---|---|---|---|
| class-wp-rest-widgets-controller.php | 589 | unserialize RCE | 9.8 |
| functions.php | 655 | maybe_unserialize | 9.8 |
| class-wp-xmlrpc-server.php | 7039 | SSRF | 8.6 |
| class-wp-rest-url-details-controller.php | 254 | SSRF | 7.5 |
| functions.php | 2910 | MIME上传绕过 | 7.5 |
| class-wp-rest-widgets-controller.php | 613 | POST注入 | 7.0 |
| class-wpdb.php | 1536 | SQLi (%i) | 6.0 |
| class-wpdb.php | 690 | 未引用参数 | 5.0 |
| pluggable.php | 962 | Cookie弱化 | 5.0 |
| cron.php | 982 | cron_request SSRF | 5.0 |
五、防御建议
| 漏洞 | 紧急修复方案 |
|---|---|
| Widget反序列化 | 替换unserialize()为json_decode() |
| maybe_unserialize | 限制仅为已知安全类型解序列化 |
| XML-RPC SSRF | 彻底禁用pingback: add_filter('xmlrpc_methods', fn($m) => { unset($m['pingback.ping']); return $m; }) |
| URL Details SSRF | 增加IPv6回环过滤 |
| MIME绕过 | 统一使用wp_check_filetype_and_ext() |
六、常见陷阱
| # | 陷阱 | 说明 |
|---|---|---|
| 1 | 以为"HMAC保护就安全" | HMAC密钥AUTH_KEY常在wp-config.php/LFI/SQLi中泄露 |
| 2 | 忽略IPv6绕过 | wp_http_validate_url只检查IPv4回环 |
| 3 | maybe_unserialize遍地开花 | 任何SQL注入都能变成RCE |
七、总结
WordPress 7.0虽然是最新版本,但核心层仍存在多个严重漏洞。最关键的是maybe_unserialize()函数——它几乎贯穿所有数据读取API,任何一个位置的SQL注入或权限提升都能通过它转化为RCE。
本地Docker环境已验证:
- ✅ Widget REST API端点可用(400返回=HMAC校验)
- ✅ DB中多个序列化option(cron/rewrite_rules等全部走maybe_unserialize)
- ✅ XML-RPC端点确认
- ✅ 攻击链完整分析(A→B→C)
PoC脚本: /opt/0day_lab/wp7_poc.py