我的网站被劫持了?排查 20 分钟,发现凶手是自己
那天打开 limzero.com,页面糊上来一层遮罩:
解锁更多内容
请做出选择以便继续访问此网站上的内容
○ 观看一则短广告
○ 获得 24 小时的网站级访问权限
我盯着这行字看了三秒,第一反应是——完了,被挂马了。
这套话术太眼熟了。黑产攻破 WordPress 之后注入一段 JS,把正常流量导去他们的广告联盟刷收益,行话叫 content locker(内容锁)。我甚至已经开始在脑子里排流程了:备份数据库、拉最近改动的文件、准备重装。
结果排查了二十分钟,凶手是我自己。
这篇写写完整的排查过程。重点不是结论,是方法——尤其是「不登录服务器也能判断网站有没有被黑」的那几招,我觉得每个自建站的人都该会。
一、为什么我第一反应是中毒
因为这类劫持有一套非常固定的画像,而我的症状全中:
- 强制性遮罩,不看广告不让走
- 话术带「解锁」「权限」这种诱导词
- 我完全不记得自己配置过这东西
最后一条是最要命的。站长对自己的站有一套心理地图,一旦出现地图外的东西,本能就是「外来入侵」。
而且这类劫持有个特别阴的地方:它通常只对移动端、或者只对从搜索引擎点进来的访客触发。站长自己习惯直接输网址、用桌面浏览器、还常年登录着后台——恰好是黑产设定的排除条件。所以往往是读者跑来问「你网站怎么弹广告啊」,站长才知道自己被黑了半个月。
这个技术叫 cloaking(差异化投放),直译「斗篷」。它是判断「是不是黑产干的」最有价值的一个特征。记住这个词,下面要用。
二、不碰服务器,怎么判断被没被黑
我没有第一时间 SSH 上去翻文件。原因很简单:外部检测零风险、零成本,而且能排掉八成情况。
登录服务器手一抖删错东西,或者慌乱中重装了一遍其实没问题的站,比中毒本身还惨。
第 1 招:拿四种身份抓同一个页面
这是整套排查里信息量最大的一步。
# 桌面 Chrome
curl -sL -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0" \
https://limzero.com/ -o desktop.html
# iPhone + 从 Google 点进来
curl -sL -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5) Mobile/15E148" \
-e "https://www.google.com/" https://limzero.com/ -o mobile.html
# Android + 从百度点进来
curl -sL -A "Mozilla/5.0 (Linux; Android 13) Mobile Safari/537.36" \
-e "https://www.baidu.com/" https://limzero.com/ -o baidu.html
# 搜索引擎爬虫
curl -sL -A "Mozilla/5.0 (compatible; Googlebot/2.1)" \
https://limzero.com/ -o bot.html
# 比大小
wc -c *.html
我的结果:四份全是 48146 字节,一个字节不差。
这一下就把 cloaking 排除了。做黑帽的必然要区别对待不同访客,四版完全一致,说明服务器对谁都是同一套内容。
补充一句:如果字节数不一样也别急着下结论,有些站装了移动端主题或缓存插件也会有差异。真正的信号是内容结构不同——比如手机版凭空多出一段脚本。用
diff看一眼就清楚。
第 2 招:搜混淆代码特征
黑产注入的代码几乎必然是混淆过的,不然一眼就被看穿。常见的几个函数是硬指纹:
for k in 'eval(' 'atob(' 'String.fromCharCode' 'unescape(' 'document.write('; do
printf "%-22s : %s 次\n" "$k" "$(grep -o -F "$k" desktop.html | wc -l)"
done
我的结果:全是 0。
正常的现代 WordPress 页面里,这几个函数出现的概率极低。一旦出现一长串 String.fromCharCode(104,116,116,112...) 这种东西,基本可以确诊了。
第 3 招:把所有外链域名拉出来看一遍
grep -oE 'https?://[a-zA-Z0-9._-]+' desktop.html \
| sed 's|https\?://||' | sort | uniq -c | sort -rn
我的结果里只有:自己的几个子域名、百度统计 hm.baidu.com、WordPress 官方 s.w.org,以及 pagead2.googlesyndication.com。
每一个我都认识。 这是很强的清白证据——注入的脚本总要有个地方加载,域名藏不住。
第 4 招:扫一遍常见后门文件名
黑产用的 webshell 就那么些成名工具,文件名高度雷同:
for p in shell.php alfa.php wso.php wp-content/uploads/wp-config.php \
wp-includes/wp-class.php; do
printf "%-42s -> %s\n" "$p" \
"$(curl -so /dev/null -w "%{http_code}" https://limzero.com/$p)"
done
全部 404。 顺手再看一眼 wp-content/uploads/ 是不是能列目录,我这边返回 403,正常。
(这招只能查到懒的攻击者,认真藏后门的不会用这种文件名。但成本一分钟,值得跑。)
三、真相:Google 亲手给我装的
四项检测全绿,但弹窗确实存在。那它从哪来的?
线索在第 3 招里已经躺着了:pagead2.googlesyndication.com——Google AdSense 的官方域名。
搜了一圈,答案清楚了:这是 AdSense 2024 年推出的一种广告格式,叫 Offerwall(积分墙)。
它的逻辑是:访客浏览到第 N 个页面时弹出遮罩,看完一支短广告,就能获得一段时间的免打扰访问权。CPM 比普通展示广告高不少,所以 Google 在后台推得很积极。
而我看到的那句「获得 24 小时的网站级访问权限」,就是 AdSense 积分墙的中文默认文案,一字不差。
三个细节让它伪装得格外像木马:
- 不需要改站点代码。只要页面里有 AdSense 脚本,后台点一下「发布」就全站生效,站长这边零改动、零感知。
- 弹窗内容是浏览器端动态渲染的,查看源代码根本搜不到「解锁更多内容」这几个字——这一点和注入型木马的表现完全一样。
- 默认文案写得太像黑产了。「观看短广告换访问权限」这句话,本来就是内容锁的标准句式。
至于我为什么会开这个功能——大概是某次在 AdSense 后台点「优化建议」的时候顺手同意的。Google 的引导式弹窗,谁没手滑过。
四、怎么关掉它
AdSense 后台 → 隐私权和消息 → 找到「积分墙 / Offerwall」卡片 → 管理 → 点开消息 → 取消发布或删除。
不想彻底关也行,调两个参数就能大幅降低干扰:
- 计量:默认可能是「第 1 次浏览就触发」,改成「浏览 4–5 个页面后触发」
- 网址排除项:把首页、关于页这些排除掉,只在内容页触发
改完有几小时缓存延迟,别以为没生效又去反复折腾。
五、留下来的三条经验
第一,先做外部检测,再登服务器。
不碰服务器的排查零风险,而且能排掉大部分情况。慌乱之下 SSH 上去乱翻,删错文件比中毒更麻烦。
第二,「四种身份抓同一页面」应该成为肌肉记忆。
一条命令,几秒钟,直接判掉 cloaking——这是黑帽 SEO 和广告劫持的核心手法。以后再遇到「读者说弹窗我自己看不到」这种情况,先跑这一招。
第三,也是最扎心的:先怀疑自己,再怀疑黑客。
站长手里往往挂着一堆自己都忘了的东西——某次试用后没卸干净的插件、某个 A/B 测试留下的脚本、后台某个顺手点开的「优化建议」。
出问题时的正确顺序是:先翻自己的账号后台,再查服务器。 我这次要是一上来就登服务器翻文件,大概率会在 functions.php 里反复找一段根本不存在的注入代码,然后越找越确信「藏得真深」。
附:一份可以照抄的自查清单
下次再怀疑网站被黑,按这个顺序走:
| 步骤 | 动作 | 说明 |
|---|---|---|
| 1 | 四种 UA/Referer 抓页面比对 | 判 cloaking,信息量最大 |
| 2 | 搜 eval atob fromCharCode | 找混淆代码 |
| 3 | 列出全部外链域名 | 有陌生域名就是线索 |
| 4 | 探测常见 webshell 路径 | 快速排除低水平攻击 |
| 5 | 翻广告/统计平台后台 | ← 这一步最容易被跳过 |
| 6 | 核对核心文件校验和 | 前五步没结论才需要登服务器 |
| 7 | 查近 30 天改动文件、数据库隐藏管理员、crontab | 确认被入侵后的深度排查 |
第 5 步是我这次的教训。 它排在登服务器之前,因为它零成本,而且——你自己配的东西,确实比黑客的手笔更常见。



发表评论