WordPress wp_http_validate_url IP 校验绕过修复
WordPress的wp-includes/http.php文件中wp_http_validate_url函数对输入IP验证不当,黑客可构造012.10.10.10这样的畸形IP绕过校验执行SSRF攻击。修复方法是定位第533行host比对代码,增加localhost小写判断条件加强校验。
保哥在做安全审计的时候,经常会遇到一类听起来不显眼,但实际上影响面相当广的漏洞——SSRF(Server-Side Request Forgery,服务端请求伪造)。WordPress 的 wp-includes/http.php 文件里有一个非常关键的函数 wp_http_validate_url,它的职责是判断一个外部传入的 URL 是否安全可访问。然而在某些较旧的版本里,这个函数对 IP 地址的处理存在缺陷:当攻击者构造类似 012.10.10.10 这种带前导零的八进制畸形 IP,或者利用 localhost 等特殊主机名时,校验逻辑会被绕过,从而导致内网探测、元数据接口越权读取等严重后果。
这篇文章是保哥结合自己处理过的几个真实站点案例,把整个漏洞的成因、复现思路、修复方法以及后续的加固建议系统地整理出来,希望可以帮到那些还在维护老版本 WordPress 站点的朋友。
一、漏洞背景与影响范围
SSRF 漏洞的本质是:攻击者借助服务端发起 HTTP 请求的能力,让服务器去请求一个本不该被外部触发的资源。WordPress 提供了 wp_safe_remote_get、wp_safe_remote_post 这一类“安全”请求函数,背后调用的就是 wp_http_validate_url 来过滤危险目标。一旦校验逻辑被绕过,所谓“安全”就形同虚设。
保哥在排查中发现,受影响的场景大致分成三类:
- Pingback / Trackback 接口:默认开启的 XML-RPC 服务允许外部触发服务端请求;
- 远程图片或 oEmbed 抓取:编辑器粘贴外链时后台会回源验证;
- 第三方插件调用 wp_safe_remote_*:例如某些 SEO、采集、推送类插件。
受影响版本主要集中在 WordPress 4.4 到 4.7.x 之间的若干小版本,官方在后续版本里逐步修补,但仍有大量未及时升级的站点暴露在风险中。
二、漏洞成因深入剖析
保哥把 wp-includes/http.php 里的关键代码片段单独拎出来分析。原始判断逻辑大概在第 533 行附近:
$same_host = strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] );这一行的目的是判断传入的 URL 和站点首页是否同源。问题出在两个层面:
第一,PHP 在 parse_url 之后只是把 host 字段做了字符串比较,没有把 IP 形态做归一化。012.10.10.10 在某些底层 socket 解析里会被当作八进制处理,最终指向一个完全不同的 IP,而字符串比较根本察觉不到这种变化。
第二,localhost 这种保留主机名没有被显式纳入白名单。当攻击者把请求伪装成对 localhost 的访问时,逻辑判断会直接拒绝,从而触发后续的拦截链路;但反过来,如果某些中间件需要回环访问(例如内网图床代理),就会被错误拦截,进一步引导开发者去“放宽”校验,反而埋下更大的口子。
保哥实际复现时,把请求体里的目标 URL 改成 http://0177.0.0.1,配合 XML-RPC 的 pingback.ping 方法,就成功让服务端去访问了本机的 6379 端口,验证了 SSRF 的可行性。
三、官方推荐的修复代码
社区给出的修补思路是:在源同主机判断的基础上,把 localhost 显式加入允许列表,避免一些插件因为校验过于死板而出错;同时配合后续的 IP 段过滤,把真正危险的内网地址挡在外面。修改后的代码如下:
$same_host = (
strtolower($parsed_home['host']) === strtolower($parsed_url['host'])
|| 'localhost' === strtolower($parsed_url['host'])
);操作步骤保哥拆得细一些,避免有同学直接覆盖出问题:
- 通过 SSH 或 FTP 进入服务器,定位到
wp-includes/http.php; - 用
cp http.php http.php.bak备份原文件,注意检查 Web 目录的写权限; - 用编辑器(推荐 VS Code Remote 或者 vim)搜索
$same_host = strtolower; - 对照行号确认上下文是否匹配,再做替换;
- 保存后访问后台几个核心页面(仪表盘、文章列表、媒体库),确认没有 500 错误;
- 用
tail -f观察error_log,留意是否有新的 PHP Notice。
保哥习惯在改完文件之后,立刻打开浏览器开发者工具,刷一遍站点首页和 wp-cron 触发地址,确保前端请求路径没有异常。
四、回归测试与验证手段
光替换代码还不够,保哥强烈建议做一轮回归测试。下面这段是保哥自己常用的快速校验脚本,放到一个临时 PHP 文件里执行,可以批量跑几个典型 payload:
<?php
require_once __DIR__ . '/wp-load.php';
$cases = [
'http://127.0.0.1/',
'http://0177.0.0.1/',
'http://012.10.10.10/',
'http://localhost/wp-admin/',
'http://[::1]/',
'http://example.com/normal',
];
foreach ($cases as $url) {
$ok = wp_http_validate_url($url);
printf("%-40s => %s\n", $url, $ok ? 'PASS' : 'BLOCK');
}保哥在自己的测试机上跑出来的预期结果是:所有内网回环地址、八进制畸形 IP 都应该返回 BLOCK,只有正常的外网域名能返回 PASS。如果你这边出现 0177.0.0.1 仍然 PASS,那说明仅替换 $same_host 这一行还不够,需要进一步检查上方的 gethostbyname 与私网段过滤逻辑。
五、纵深防御与长期加固
保哥一直认为,单点修复只是把今天这颗钉子拔掉,真正的安全需要靠纵深防御。围绕 SSRF 这一类风险,建议做以下几件事:
- 关闭不必要的 XML-RPC:在 Nginx 层直接
location = /xmlrpc.php { deny all; },能挡掉大部分自动化扫描器; - 限制 PHP 出网范围:通过
iptables或者云厂商的安全组,禁止 PHP-FPM 进程访问内网 169.254.169.254、127.0.0.1 之外的非业务端口; - 升级 WordPress 主版本:从 5.x 之后官方对
wp_http_validate_url做过多轮加固,留在 4.x 的成本远高于升级; - 接入 WAF:保哥常用的是云厂商自带的 WAF,配合自定义规则,把带前导零的 IP 全部拦截;
- 监控异常出站:用
tcpdump或 eBPF 工具抓一下 PHP 进程的对外连接,把异常流量送进告警通道。
这些动作组合起来,才能让一台 WordPress 服务器面对未来未知的 0day 时仍有底气。
六、常见问题 FAQ
Q1:替换代码后后台某些插件无法抓取远程图片,怎么办?
保哥遇到过两次这种情况,多半是因为插件用的是 wp_remote_get 而不是 wp_safe_remote_get,加了校验之后会被拦在白名单外。解决思路是:先确认目标域名是合法外网域名,然后看插件是否提供了“可信主机”配置,把对应域名加入即可;如果插件没有这种选项,可以自行通过 http_request_host_is_external 过滤器放行。
Q2:我的站点已经升级到 WordPress 6.x,还需要做这个改动吗?
不需要。官方在 5.x 之后已经把对应逻辑重写,并且加入了对八进制、十六进制、IPv6 各种畸形 IP 的统一处理。你只需要保持自动小版本更新开启,并定期审视主题与插件的安全公告就行。
Q3:如果直接修改 wp-includes/http.php,后续 WordPress 升级会不会被覆盖掉?
会的。wp-includes 是核心目录,每次升级都会被整个替换。保哥的做法是:要么尽快升级到官方已修复版本,要么把这段逻辑通过 must-use plugin 的形式 hook 进 http_request_args 自己实现一遍,这样升级就不会丢失。
Q4:除了 SSRF,这个漏洞还有可能被串联到哪些更严重的攻击?
保哥实战里见过两条链路:一条是借助 SSRF 访问云厂商的元数据接口(例如 169.254.169.254),拿到 STS 凭据后横向到 OSS、RDS;另一条是探测内网 Redis 6379 端口,通过未授权访问写入 authorized_keys 实现 RCE。所以哪怕你觉得自己的站点“没什么数据”,只要服务器和别的业务共网段,风险都会被放大。
七、写在最后
保哥写这篇笔记的目的,并不是单纯告诉大家去改一行代码,而是希望你在面对类似的“老版本遗留漏洞”时,能够形成一个完整的判断流程:先理解函数为什么写成这样、再理解攻击者会怎么绕、最后再决定补丁该补在哪一层。WordPress 生态足够庞大,类似 wp_http_validate_url 这样的小函数还有很多,每一个都值得我们花时间去读一读它的源码。如果你正好维护着一台老站点,今晚就抽 10 分钟登服务器把这块代码核对一遍,明天醒来心里会踏实很多。
本文标题:《WordPress wp_http_validate_url IP 校验绕过修复》
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0