Wordpress 被黑:登录或支付成功后跳转到外部网站
明确结论
这不是 WordPress 的正常行为。登录或支付成功后跳转到陌生域名,通常说明网站被植入了恶意重定向代码,或者服务器、DNS、支付平台中的返回地址遭到篡改。
即使禁用了普通插件、切换了主题,也搜索不到完整域名,仍然不能排除网站已被入侵。恶意代码可能藏在 mu-plugins、WordPress 核心文件、服务器配置、数据库序列化数据、计划任务或站外加载的 JavaScript 中。目标域名也可能经过 Base64、字符串拼接或十六进制编码,甚至由远程服务器动态返回。
不要只删除发现的跳转代码。应先暂停交易并保留现场,再从可信来源重建网站,同时更换所有凭据。
先判断重定向发生在哪里
先确认跳转是由服务器通过 HTTP 重定向返回,还是页面加载后由浏览器执行 JavaScript 触发。
可以在终端检查登录页和支付完成页的响应头:
curl -I https://topvegans.com/wp-login.php
curl -IL https://topvegans.com/wp-login.php
如果支付完成页要求登录或依赖特定订单状态,应在浏览器开发者工具的 Network 面板中检查:
- 响应状态为
301、302、303、307或308时,查看响应中的Location。 - 如果原页面先正常加载,过一会儿才跳转,应重点检查 JavaScript、Service Worker 和第三方脚本。
- 如果只有部分设备、访客或特定来源会触发跳转,恶意代码可能会根据 Cookie、IP、User-Agent、Referer 或订单状态判断是否执行。
还应使用无痕窗口、另一台设备和另一个网络复测,排除浏览器扩展、缓存或本地 DNS 污染。
立即采取的措施
问题发生在支付成功后,应暂时关闭结账功能,或将网站切换到维护状态,避免客户继续被导向恶意页面。
接着处理以下事项:
- 为当前文件、数据库、Web 服务器日志和访问日志制作只读备份,以便后续调查。
- 在可信设备上修改主机面板、SSH、SFTP、数据库、WordPress 管理员、支付平台和 DNS 账户的密码。
- 启用多因素认证。
- 撤销未知的 API Key、应用密码、部署密钥和活跃会话。
- 联系支付服务商,确认账户中的成功页、回调地址和 Webhook 地址有没有被修改。
- 检查是否出现了未知管理员,以及订单、客户资料或支付数据是否可能泄露。
如果网站处理真实交易,还应根据支付服务商、主机商和适用法规的要求,评估是否需要报告安全事件。
检查 WordPress 地址配置
先在数据库中确认 home 和 siteurl 的值:
SELECT option_name, option_value
FROM wp_options
WHERE option_name IN ('home', 'siteurl');
数据表前缀不一定是 wp_,应以 wp-config.php 中的 $table_prefix 为准。
正常情况下,两项都应指向网站自己的域名,例如:
https://topvegans.com
同时检查 wp-config.php 是否覆盖了数据库设置:
define('WP_HOME', 'https://topvegans.com');
define('WP_SITEURL', 'https://topvegans.com');
只有确认站点确实使用这个地址后,才能添加或修改这两项。它们可以纠正错误的 WordPress 基础地址,但不能清除恶意程序。
还要检查 PHP 或服务器环境中是否存在异常的 HTTP_HOST、代理头或强制跳转规则。如果站点位于 CDN、反向代理或负载均衡器之后,错误的代理配置也可能生成异常 URL。不过,固定跳转到无关域名更像是网站遭到了入侵。
不要只搜索完整域名
攻击代码未必会直接保存完整的 cartoonmines.com/scount。搜索时应扩大范围,包括:
cartoonmines
scount
base64_decode
gzinflate
str_rot13
eval(
assert(
shell_exec
passthru
proc_open
preg_replace
Location:
window.location
location.href
document.location
atob(
String.fromCharCode
发现这些字符串不等于发现了恶意代码。例如,正常软件也可能使用 base64_decode(),需要结合文件位置、代码上下文和修改时间判断。
还要注意通过字符串拼接生成的地址:
$host = 'cartoon' . 'mines' . '.com';
header('Location: https://' . $host . '/scount');
exit;
以及经过编码的跳转:
window.location.href = atob('aHR0cHM6Ly9leGFtcGxlLmNvbS8=');
不要运行来源不明的解码结果。只能在隔离环境中把它当作文本分析。
检查禁用插件时仍会执行的位置
在 WordPress 后台选择“禁用所有插件”,并不能关闭所有可执行入口。需要重点检查:
wp-content/mu-plugins/wp-content/advanced-cache.phpwp-content/object-cache.phpwp-content/db.phpwp-content/sunrise.phpwp-config.php- 网站根目录的
index.php .htaccess.user.iniphp.ini- Web 服务器的 Nginx 或 Apache 配置
- 主机面板设置的自动追加文件
- WordPress 核心目录中的异常 PHP 文件
- 上传目录中的
.php、.phtml、.phar等可执行文件
尤其要检查 .user.ini 或 PHP 配置中是否包含:
auto_prepend_file=/path/to/unknown-file.php
auto_append_file=/path/to/unknown-file.php
这类配置会让恶意文件在每个 PHP 请求之前或之后执行,所以切换主题、停用插件都不会使其失效。
在 Apache 配置中,还要检查类似规则:
RewriteRule ^wp-login\.php https://example.com/scount [R=302,L]
也要留意根据 Cookie、Referer 或查询参数匹配的条件规则。
校验并替换 WordPress 核心文件
如果可以使用 WP-CLI,可通过以下命令校验核心文件:
wp core verify-checksums
对于来自 WordPress.org 的插件,也可以尝试:
wp plugin verify-checksums --all
校验失败不一定说明文件是恶意的,因为部分商业插件、定制包或语言文件没有可用的校验值。但如果核心文件校验失败,必须认真处理。
更可靠的处理方法是:
- 保留
wp-config.php和检查过的用户上传内容。 - 从 WordPress 官方来源下载对应的可信版本。
- 完整替换
wp-admin、wp-includes和根目录中的核心文件。 - 从开发商或官方市场重新安装插件和主题,不要复用现场中的 PHP 文件。
- 删除不再使用的插件和主题,不要只停用。
- 检查
wp-content/uploads。正常情况下,上传目录不应包含可执行 PHP 文件。
不要直接用一份可能已被感染的旧备份覆盖网站。恢复前要确认备份早于入侵时间,并在恢复后立即修补最初的入侵入口。
检查数据库中的隐藏配置
完整域名可能藏在序列化数据、JSON、Base64 内容或拆分后的字段中。除了 wp_options,还要检查:
- 未知管理员账户
- 用户元数据中的权限提升
- 自动加载的异常选项
- 页面、区块、小工具和自定义 HTML
- 订单完成页配置
- 支付插件的返回 URL
- 注入了脚本的文章或模板数据
检查管理员:
SELECT ID, user_login, user_email, user_registered
FROM wp_users;
检查具有管理员权限的用户时,要注意数据表前缀:
SELECT user_id, meta_key, meta_value
FROM wp_usermeta
WHERE meta_key LIKE '%capabilities%';
查找可疑的自动加载配置:
SELECT option_id, option_name, LENGTH(option_value) AS value_length
FROM wp_options
WHERE autoload = 'yes'
ORDER BY value_length DESC
LIMIT 50;
不要使用普通 SQL 全文替换直接修改序列化数据,否则可能破坏其中的长度信息。处理序列化字段时,应使用能够识别 WordPress 序列化格式的工具,并提前备份数据库。
检查计划任务和持久化机制
攻击者可能利用计划任务反复写回恶意代码。先查看 WordPress Cron:
wp cron event list
然后检查系统级计划任务、主机面板定时任务和部署脚本。重点关注:
- 执行陌生 PHP 文件的任务
- 定期下载远程脚本的任务
- 定期修改
.htaccess、wp-config.php或插件文件的任务 - 名称模仿 WordPress 更新任务,但回调函数不存在或来源不明的事件
如果删除的文件很快又出现,通常说明网站中仍有后门或计划任务,也可能是控制面板凭据已经泄露。
检查登录后的重定向逻辑
正常插件可能通过以下钩子修改登录后的地址:
add_filter('login_redirect', function ($redirect_to, $requested, $user) {
return home_url('/');
}, 10, 3);
这段代码只能用于临时诊断,或在确认业务需求后使用。它无法阻止发生得更早的服务器重定向,也不能修复后门。
还要检查代码中是否调用了:
wp_redirect($url);
wp_safe_redirect($url);
header('Location: ' . $url);
wp_safe_redirect() 默认会限制外部主机,但攻击者可以修改允许的主机列表,也可以直接使用 wp_redirect()、header() 或 JavaScript 绕过限制。检查是否存在异常的:
add_filter('allowed_redirect_hosts', ...);
add_filter('login_redirect', ...);
add_action('wp_login', ...);
add_action('template_redirect', ...);
如果只有管理员登录后才发生跳转,还应检查管理后台页脚脚本、管理员专用钩子和用户资料中的异常字段。
检查支付成功后的返回链路
支付流程通常涉及多个地址,需要逐一确认:
- 用户支付完成后的浏览器返回地址
- 支付取消地址
- 服务器端 Webhook 或 IPN 地址
- 商店中的“订单已收到”页面
- 支付平台后台配置的默认成功页
- 支付插件保存的网关配置
- 自定义结账代码或支付服务商托管页面
Webhook 是服务器之间使用的通知地址,通常不应成为用户浏览器最终访问的页面。不要为了消除跳转而随意修改 Webhook,否则订单状态可能无法更新。
使用浏览器 Network 面板记录完整的跳转链,可以判断外部地址是由哪一环返回的:
支付平台 → 商店回调地址 → 订单完成页 → 外部域名
如果外部地址由支付平台直接返回,应检查支付平台账户,以及交易请求中提交的 return_url、success_url 或同类参数。不同网关使用的参数名称不同,在不知道支付服务商的情况下不能统一判断。
检查站外脚本、DNS 和浏览器持久化
如果服务器响应中没有 Location,应检查页面加载的所有第三方资源,尤其是:
- 不认识的 JavaScript 域名
- 被篡改的统计代码
- Google Tag Manager 或类似标签管理器
- 聊天、广告和转化追踪脚本
- 远程字体或脚本加载器
- 仅在支付完成页加载的脚本
如果标签管理平台账户被盗,恶意代码可能完全不会出现在 WordPress 文件或数据库中。
同时核对:
- DNS 的 A、AAAA、CNAME 和 NS 记录
- CDN 的重定向规则、Workers 或边缘函数
- 主机面板的域名转发
- 反向代理规则
- Service Worker
在浏览器开发者工具的 Application 面板中注销可疑的 Service Worker,清除该站点的存储后再测试。如果只有某台设备出现问题,还应禁用浏览器扩展,并检查系统代理和 DNS 设置。
修复后的收尾工作
确认跳转已经消失后,还要完成以下工作:
- 更新 WordPress、插件、主题和服务器软件。
- 删除未知管理员、废弃账户和不必要的应用密码。
- 重新生成 WordPress salts,并强制所有用户退出。
- 检查文件所有者和权限,避免 Web 进程随意修改不需要写入的目录。
- 禁止上传目录执行 PHP。
- 检查支付订单、管理员操作和登录日志。
- 持续监控一段时间,关注文件变化、计划任务、外连请求和异常登录。
- 对可能涉及的客户数据和密钥进行风险评估。
新的 salts 可以通过 WordPress 官方密钥服务生成,然后替换 wp-config.php 中对应的常量。替换后,现有登录会话会失效,这是正常现象。
最稳妥的恢复方法,是在干净环境中重新部署 WordPress,安装来自可信来源的扩展,只迁移检查过的数据和媒体文件,而不是继续在原站点中逐段寻找并删除可疑代码。只要还有一个后门没有被发现,外部跳转就可能再次出现。
备注:内容仅供参考。
