《邮件钓鱼攻防工程》Part 2:构建不可溯源的基础设施
首发至公众号:Yofune安全研究 欢迎关注。
隐匿与对抗——构建不可溯源的基础设施
本文是「邮件钓鱼攻防工程」系列的第二篇。上一篇我们拆解了传统钓鱼框架(GoPhish)的架构债务与检测面。这一篇深入基础设施层,从域名获取、网络拓扑、TLS指纹到探针,逐层拆解如何构建一套经得起威胁情报溯源、沙箱分析的基础设施。
0x01 基础设施的生存窗口
基于OPSEC守则红蓝对抗中基础设施的存活时间决定成败因素。用一台公网IP裸奔的VPS,挂一个刚注册12小时的域名,HTTPS还是自签证书——可以设想一下没有更改过特征的原生CS,这套设施的生存周期通常不超过10分钟。现代企业的纵深防御体系拥有多维度情报源:
•邮件网关(SEG):国内主流邮件安全网关(奇安信邮件安全网关、深信服邮件安全网关、Coremail论客)的域名信誉模块会综合域名注册时长、发信历史、SPF/DKIM配置状态等维度打分。网易企业邮、腾讯企业邮等云端服务则内置了基于发信行为画像的反垃圾引擎,对注册不足30天的新域名默认增加风险权重。国际厂商如Proofpoint TRAP(实时关联域名信誉与URL分类)、Spamhaus DBL的域名黑名单在国内也有一定部署,主要用于在华外企和跨国企业。
•网络边界:下一代防火墙(华为HiSec、深信服AF、奇安信智慧防火墙)和上网行为管理设备(深信服AC、网康)的TLS解密模块可提取Client特征。国内多数甲方企业采购上网行为管理时已附带SSL审计功能,虽然性能损耗显著,但一个配置了SSL审计的网络出口确实能够生成JA3/JA4指纹用于识别非浏览器自动化工具。
•端点与沙箱:国内EDR市场以奇安信天擎、深信服EDR、火绒企业版等等。邮件沙箱方面,奇安信天眼沙箱、微步在线OneSandbox(国内沙箱即服务代表)、安恒威胁情报沙箱等安全厂商的设备能力均会对邮件中的URL执行headless浏览器爬取,提取DOM结构和JavaScript探针返回内容。部分厂商沙箱已支持Chromium内核完整JavaScript执行(猜测?不确定)。
•威胁情报中心:国内SOC团队常用的CT日志监控路径与海外不同,相比较直接订阅国外大厂的安全情报API实时推送在国内网络环境下不稳定,主流做法是通过奇安信威胁情报中心、微步在线ThreatBook的域名监控服务,设置与组织域名相似的新注册域名告警规则。阿里云和腾讯云DNSPod也提供了面向企业客户的域名注册监测API。
设施即第一要素,这就意味着,面向实战的钓鱼演练平台必须在架构上将反溯源和高隐匿作为第一等优先措施。
0x02 域名信誉工程:从获取到养护
基础设施的第一步不是买服务器,是买域名。域名的信誉级决定了邮件能否通过SEG的第一道关卡。
2.1 抛弃新域名,拥抱老域名
红队常见错误:为某次行动注册一个corp-login-update.com这样的新域名。致命问题在于域名年龄太短。有些安全厂商明确的会把域名注册时长当作核心因素之一。新域名注册后立刻发送含链接邮件,大概率进入垃圾箱或被直接隔离(550 5.7.1)。这一点在国内的189邮箱,136邮箱,163邮箱上都有经历。
实战获取策略:
老域名收购。寻找已有数年历史、被域名注册商归类为"科技"或"商业"的正常域名,因为忘记续费而进入冷却期间。这类域名自带初始信誉。据The Record报道(2024年),某9年历史的老域名digitalscrapbookingfreebies.com冷却后被钓鱼团伙重新注册,成功绕过某州卫生机构的邮件网关。域名年龄数据来自Whois历史记录,信誉分数则继承自域名的DNS解析历史和SSL证书签发连续性。
子域名接管。2024年2月,Guardio Labs研究员Nati Tal和Oleg Zaytsev揭露了代号"ResurrecAds"的大规模子域名接管活动(参见Guard.io研究论文)。攻击者扫描大型企业(MSN、VMware、McAfee、Cornell University、UNICEF等)的过期CNAME记录——例如marthastewart.msn.com的CNAME指向一个已废弃22年的域名msnmarthastesweeps.com。重新注册该域名后,即可用@marthastewart.msn.com发信,直接继承MSN的顶级信誉。该活动高峰期每日发送约500万封恶意邮件,使用约22,000个唯一IP地址(含住宅ISP代理),每个劫持域名仅使用1-2天即轮换。Guardio Labs提供了免费检测工具(checker.guard.io)。
同态形似词(Homoglyph)与Punycode。利用Unicode字符视觉混淆——拉丁字母a(U+0061) 与西里尔字母а(U+0430),拉丁字母o(U+006F) 与希腊字母ο(U+03BF)。现代浏览器(Chrome 58+、Firefox 58+、Edge)对跨脚本混合有Punycode自动显示机制:当域名包含不同Unicode脚本集的字符时,地址栏强制展示xn--编码形式。但2025年腾讯云安全团队的研究指出,单一脚本内(如全日语Hiragana字符集内)的同形混淆仍能绕过浏览器检测——例如用ん(U+3093) 替代/,构造https://account.booking[.]comんdetail这种在视觉上看起来像子目录结构的钓鱼URL。(至今仍然活跃在在Web3的攻击事件中)
- •蓝队对策:利用
confusables-inspector(GitHub开源工具,2025年6月更新)自动检测同形字符;在SEG侧对邮件正文中所有URL进行Punycode解码检测。
2.2 邮件认证协议的正确配置
SPF、DKIM、DMARC已是必选项而非可选项。如果想让钓鱼邮件进入受害者收件箱,这是条件之一。
•SPF的
include:链劫持:SubdoMailing活动展示了SPF的include:机制可作为攻击面——当企业SPF记录引用了一个已废弃的外部域名,攻击者注册该域名后即可注入恶意内容。•DKIM的选择长度:建议使用rsa-sha256且密钥长度至少2048位。1024位密钥面临暴力破解风险(参考CVE-2023-50387中DNSSEC密码学分析的前车之鉴)。
•DMARC策略:自建邮件网关中 这是不可或缺的一部分。应将DMARC设为
v=DMARC1; p=reject;——这看似严苛的策略反而增加了域名的"可信度",因为显得域名所有者非常重视邮件安全。
0x03 网络拓扑:别让蓝队摸到你的真实IP
长久以来,直接将钓鱼平台的公网IP写入域名A记录意味着:蓝队可封禁IP、扫描开放端口、通过ASN溯源VPS供应商并向IDC提交Abuse投诉封机,这一点在国内云依然有效,参考华为云/腾讯云/阿里云的策略,不仅有来自内部的扫描封机,外部同样如此。
3.1 Cloudflare Tunnel:从被动入站到主动出站
当初在构建TaiGong项目的时候这一点也是参考了一些国外的技术文章,总的来说就是利用隧道技术完成对整个基础设施的隐藏。Nginx在一定程度上能力有限,于是我们摒弃传统反向代理,全面采用Cloudflare Tunnel。
工作原理:在服务器本地运行cloudflared守护进程,主动向Cloudflare边缘节点发起出站HTTP/2或QUIC长连接(默认连接到7844端口)。服务器不需要开放任何入站端口——防火墙安全组可以配置为"拒绝所有入站流量"。当请求到达Cloudflare边缘节点时,通过已建立的隧道复用流将请求透传至本地回环地址(127.0.0.1:80)。
核心收益:
|
维度
|
传统反向代理
|
Cloudflare Tunnel
|
| :-- | :-- | :-- |
|
入站端口
|
必须开放80/443
|
无需任何入站端口
|
|
IP隐匿
|
CDN兜底,但源站IP可能被网络测绘引擎(FOFA、ZoomEye钟馗之眼、Shodan)的历史数据扒出
|
物理级隐藏,测绘引擎只能扫到CF节点IP
|
|
ICP备案
|
国内云服务器未备案域名会被ISP网络层RST阻断
|
出站流量不受Host头嗅探影响,完美穿透
|
|
DDoS防护
|
依赖前置CDN
|
CF边缘天然抗D
|
CDN前置鉴权:仅靠Tunnel还不够——蓝队拿到域名后仍可用脚本进行请求调用,导致数据库产生大量脏数据,据此可以参考Cs伪装上线一样的反制手法。TaiGong的解决方案是结合Cloudflare Transform Rules:在边缘节点为所有合法请求注入自定义Header,后端的TaiGong引擎只在验证Header正确后才返回钓鱼页面。没有令牌的请求(测绘引擎、沙箱爬虫、直接扫描)被302重定向到无害目标(如https://www.baidu.com),从根源上尽量避免空间引擎扫描以及溯源追踪。
- •蓝队对策:检测异常高频出站连接到
[IP范围]:7844(Cloudflare Tunnel端口),通过流量设备识别Tunnel特征流量,关注观察请求包中的不常见头信息。
3.2 边缘层环境感知过滤
在Cloudflare Workers或Edge Functions实现第一道过滤逻辑,基于请求特征选择性回源:
- ASN黑名单:
ASN黑名单:过滤云服务商和安全公司的ASN——这些IP段几乎不可能是真实受害者。
- 阿里云(AS45102、AS37963):国内最大的公共云,覆盖华东1/2、华北1/2/3、华南1等地域,几乎所有的国内商业SEG沙箱和第三方邮件安全服务商的爬虫节点都部署在阿里云上。
- 腾讯云(AS45090、AS132203):腾讯云公网出口,腾讯企业邮和微信企业邮箱的安全检测模块的沙箱流量从此出站。
- 华为云(AS55967):华为云公网出口,部分政企客户的邮件安全。
安全厂商ASN:奇安信深信服、知道创宇扫描节点IP段——这些厂商的邮件沙箱和威胁情报爬虫通常有固定的出口IP段,但是我并没有找到具体CIDR范围。
以上参考资料均来源于:https://bgp.tools/
- Bot Management Score:利用Cloudflare的Bot Management引擎,对分数低于30的请求返回虚假内容。
- Geo-IP限制:根据演练目标受众的地理位置限制来源国家,如果你已知目标的IPC范围地址,那么可以直接限制到市以内,超出范围的请求直接丢弃。(类似后文实现的地理围栏功能,如果使用CDN的话就可以直接实现。)
0x04 TLS指纹对抗:JA3/JA4与uTLS逃逸
这部分是攻防博弈中最底层的技术细节——TCP/IP之上的第一层。
4.1 ClientHello指纹原理
客户端发起HTTPS连接时发送ClientHello报文,包含TLS版本、密码套件(Cipher Suites)、扩展列表(Extensions)、椭圆曲线(Elliptic Curves)、压缩方法等字段。不同软件栈发送的ClientHello结构存在固定差异:
•JA3(Salesforce 2017提出):将特定字段串联后MD5哈希,生成32字符指纹。例如Go的默认JA3指纹为
771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0。•JA4(FoxIO 2023提出):更结构化的分级指纹方案,格式为
t13i<cs>_<ext>_<groups>,移除了MD5哈希,支持对TLS握手的两侧分别指纹化(JA4 Server / JA4 Client / JA4S / JA4XSS等)。
如果使用Go标准库net/http或gomail发起请求,网络流量携带Go独有的JA3/JA4指纹。(开源方案如Zeek的ja4 plugin、Suricata的JA3支持;国内商业方案如奇安信天眼、绿盟NTA、科来网络分析系统的TLS指纹检测模块,还有部分厂商的全流量设备等)在边界看到陌生的外联IP正频繁特定的指纹包进行TLS握手,且行为类似POST凭证收集,可立刻阻断。
4.2 uTLS浏览器模仿
TaiGong借鉴了refraction-networking/utls(GitHub,Go 1.21+)的方案。uTLS是Gocrypto/tls的fork,允许程式化地对ClientHello进行精确控制:
•预置浏览器模板:
HelloChrome_Auto、HelloFirefox_Auto、HelloSafari_16_0、HelloIOS_14等,精确模仿对应浏览器的密码套件顺序、扩展列表和椭圆曲线组合。•随机化指纹:
HelloRandomized从uTLS所有支持的密码套件和扩展中随机选取组合,适用于绕过黑名单式检测。•自定义指纹:
HelloCustom允许完全手动构建ClientHello——可以先捕获真实浏览器的ClientHello报文(通过tcpdump或Wireshark),然后用Fingerprinter.FingerprintClientHello()反向解析为uTLS配置,再ApplyPreset()实现精确复制。
重要限制:uTLS目前只能在ClientHello阶段模仿浏览器。tls-client尽管实现了服务器Hello验证阶段的类似模仿,但成熟度不如uTLS。另外Go标准库缺少TLS 1.3的某些扩展支持(如Compressed Certificate),导致部分Chrome模板在特定服务器握手失败。参考GitHub Issue [#321](javascript:😉。
4.3 GREASE对抗GREASE
RFC 8701定义了TLS GREASE(Generate Random Extensions And Sustain Extensibility)机制,本意是确保协议扩展的向后兼容性。Chrome率先在ClientHello中随机插入GREASE密码套件(如0x0A0A)和扩展类型,防止服务器因遇到未知值而中断连接。
有趣的是,GREASE反而让JA3检测变得更加困难:因为Chrome每次连接可能发送不同的GREASE值,JA3指纹也随之变化。
- •红队进阶手法:在uTLS自定义ClientHello中主动加入GREASE值,使指纹更接近真实Chrome。但需要维持一致性——AI模型(如PerimeterX、DataDome的ML分类器)不仅看单次握手指纹,还分析整个会话的TLS行为一致性(例如握手完成后发送的HTTP/2 SETTINGS帧序列是否与浏览器一致)。
0x05 载荷层对抗:多级探测页面
基础设施隐匿的最后防线在着陆页本身。taigong在这一层采用的是客户端JS探针 + 服务端多因子联判的双层架构。这篇内容部分照搬了TaiGong项目中《第6章 高级识别之争》的研究笔记,目前不是很玄乎,也不是特别优秀的构思,刚好发出来举个栗子了。
5.1 客户端双层探针:magic.js + finger.js
taigong在每个落地页模板中注入了两个独立的客户端脚本,一个magic.js,一个fing.js。他们的作用大概如下:
- .检测 navigator.webdriver
• 检测 UA 中的 HeadlessChrome/PhantomJS 等关键词
• 检测 Chrome DevTools Protocol 是否已连接
• 检测 navigator.plugins 是否为空
• 检测 navigator.languages 是否存在
• 检测 window._phantom、window._selenium 等自动化框架属性
• 检测 window.outerWidth/outerHeight 是否为零
• 检测 Notification.permission 与权限 API 查询结果是否矛盾
• 检测 WebGL 渲染器是否为 SwiftShader/llvmpipe 等软件渲染器
• 检测 performance.timing 的 DOM 解析耗时是否短于 5ms
判定逻辑:signals >= 2(默认阈值)时,直接document.write将整个页面替换为Nginx标准404 HTML。100ms后setTimeout再跑一次,覆盖异步加载的检测(如权限API)。检测到沙箱后还会向服务端发一个/report?id=xxx&reason=sandboxer的像素上报用于统计。
红队视角:这个探针的工程取舍是明确的——宁愿漏报也不误报,因为在我看来钓鱼演练更重要的是效果送达率。阈值设为2意味着单个异常信号(如仅UA匹配了HeadlessChrome)不会触发阻断,只有两个以上独立维度同时命中才判定为沙箱。在Taigong的实际使用中,后端控制了这一点,你可以通过修改threshold变量调整灵敏度,建议范围1-3。
蓝队视角:magic.js的检测逻辑是静态的、完全可溯源的的。它检测不到以下场景:
• 使用真实Chrome实例(非headless模式)的沙箱——WebDriver标记被抹除、WebGL渲染器正常、插件存在
• 从真实办公网络IP出站的爬虫——不命中任何静态检测项
•
puppeteer-extra-plugin-stealth+ 真实浏览器实例的组合——第1、3、4、5项全部被绕过
这从反面说明了为什么服务端联判必不可少。
fing.js(客户端环境采集)— 与magic.js同时注入的第二个脚本,在magic.js判定非沙箱后执行。采集内容包括:
• Canvas指纹:固定文本+固定颜色+固定矩形的
canvas.toDataURL()哈希值——不同浏览器/OS/GPU组合产生不同的渲染结果• 浏览器属性:
navigator.userAgent、navigator.language、navigator.platform• 屏幕属性:`screen.width×screen.height```
• 环境属性:时区偏移(
new Date().getTimezoneOffset())、触屏支持('ontouchstart' in window)、Cookie启用(navigator.cookieEnabled)、DNT设置(navigator.doNotTrack)
采集数据通过POST发送到后端端点,服务端将其与click事件关联写入数据库,用于后续的受害者行为画像和风险评分。
5.2 服务端多因子联判:CheckEvasion
taigong在服务端函数中执行了以下判定,优先级从高到低:
- JA4黑名单:客户端TLS指纹精确匹配已知扫描器/沙箱库(如Evilginx的
t13d191000_9dc949149365_e7c285222651)
- JA4黑名单:客户端TLS指纹精确匹配已知扫描器/沙箱库(如Evilginx的
- IP黑名单:支持精确IP、CIDR、通配符前缀三种格式
- 云厂商IP阻断:内置200+条CIDR规则覆盖AWS/Azure/GCP/阿里云/腾讯云
- 爬虫UA拦截:35个已知Bot UA关键词,从Googlebot/Bingbot到python-requests/Go-http-client到VirusTotal/Censys,常见的都在添加。
- 三级地理围栏:支持国家级(
CN)、省级(CN-广东)、市级(CN-广东-深圳),多区域逗号分隔混用
- 三级地理围栏:支持国家级(
判定为可疑后,根据配置执行:返回302重定向到合法网站、返回伪装Nginx 404、或照常显示。
再举个实际场景的例子,假设一次针对某公司财务部的演练,邮件伪装成"年度审计通知",落地页是伪造的 Office 365 登录框。
第一层——服务端请求还没到落地页就被拦)
部分邮件网关在用户收到邮件之前就已经在后台访问了这个链接。请求到达 taigong 服务端时:
• 云厂商 IP 阻断:出口 IP 落在 腾讯CDN的 CIDR 段里 → 如果开启了 云厂商IP拦截,直接返回302跳转到百度首页。网关看到的是"这个链接会跳转到百度",判定安全。
• 即使云厂商检测没开,UA 检测:TencentCloud的 UA 里含"TencentCloud/Tencent-Cloud"关键词,命中爬虫黑名单 → 同样被拦截。
第二层——magic.js(服务端没拦住,落地页已返回,但沙箱看不到真内容)
假设换了个高级沙箱——从真实办公网络 IP 出站、自定义了正常浏览器 UA、没命中 JA4 黑名单——成功拿到了落地页 HTML。此时 magic.js 开始执行:
• navigator.webdriver === true → +1(沙箱用 Selenium/Playwright 驱动)
• navigator.plugins.length === 0 → +1(headless 没装插件)
• 加载时间 < 5ms → +1(机器秒开 DOM)
signals = 3 >= 2,触发阻断。document.write 把浏览器的整个 DOM 替换成:
404 Not Found
同时发一个像素上报:/report?id=Ab7Xk2Q&reason=sandboxer。
沙箱的视角:它以为自己访问了一个已经下线的页面(404),什么钓鱼特征都没抓到。沙箱判定:安全。
真实用户的视角:收到邮件,点击链接,浏览器正常打开落地页。magic.js 跑了一遍——navigator.webdriver 是 false,插件正常加载,渲染耗时正常几百毫秒——signals = 0,不触发阻断。页面正常显示 Office 365 登录框。输密码、提交、跳转。全程无感。
所以这套设计的实际设想是:
不是"阻止沙箱访问",而是让沙箱和真人看到不同的东西。沙箱看到 Nginx 404,真人看到钓鱼表单。两层防线叠加后,沙箱要在服务端绕过 IP/UA/JA4 三层过滤,还要在客户端同时命中不到 2 个检测信号,看起来反溯源设计是不是很合理?这套设计仍然有弊端性存在。
5.3 这个方案的实际效果与局限性
局限性:
•没有行为分析:magic.js不做鼠标轨迹追踪、不做滚动检测、不做Fitts定律分析。市面上有些商业反检测方案把这些写进白皮书,但工程上行为分析延迟高、误报多、资源消耗大,在钓鱼场景下性价比不如静态检测。
•没有延迟渲染:Landing Page的HTML是完整返回的,magic.js在页面已渲染后执行替换。SEG如果能dump出magic.js执行前的DOM快照,仍然能看到钓鱼表单。Sandbox的进化方向恰好是捕获初态DOM。
•不是"叠加态":叠加态这只是我一种之前的设想,目前并没有找到一个好的思路进行实现。目前的实际逻辑是"检测到沙箱后销毁页面",而不是"不确定身份时不显示"。这个区别在工程上很关键——初次响应不含任何恶意元素的"真正叠加态"需要服务端在收到客户端探针回调后才下发钓鱼内容,这带来了额外的延迟和复杂度。
以上就是所有我对钓鱼行动安全的基础设施建设看法,但在如今的攻防对抗中,即使基础设施完美隐匿,一旦进入内容对抗阶段,传统的静态HTML和硬编码话术依然会被AI分类器(如Microsoft Defender的ML模型、Exchange Safety AI)无情击杀。下一篇将深入邮件正文的对抗:多态HTML与语义稀释引擎实现,让每封钓鱼邮件都成为独一无二的指纹,尝试调试贝叶斯和NLP分类器的防线。
参考资料
•SubdoMailing Research— Guardio Labs, Nati Tal & Oleg Zaytsev, February 2024. https://guard.io/labs/subdomailing-thousands-of-hijacked-major-brand-subdomains-found-bombarding-users-with-millions
•Cloudflare Tunnel Documentation. https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
•JA4 Network Fingerprinting— FoxIO LLC. https://github.com/FoxIO-LLC/ja4
•ja4-nginx-module— FoxIO. https://github.com/FoxIO-LLC/ja4-nginx-module
•Refraction Networking uTLS. https://github.com/refraction-networking/utls
•RFC 8701 — TLS GREASE. https://datatracker.ietf.org/doc/html/rfc8701
•Cisco Talos Sender Maturity FAQ. https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance/215660-sdr-sender-maturity-faq.pdf
•Proofpoint Emerging Threats Intelligence. https://www.proofpoint.com/us/products/advanced-threat-protection/et-intelligence
•IDN Homograph Attack & Japanese Character Confusion— Tencent Cloud Security, 2025. https://cloud.tencent.com/developer/article/2597395
•confusables-inspector— OpenSecurity, 2025. https://github.com/opensecurity/confusables-inspector
•Certifiably Vulnerable: Using Certificate Transparency Logs for Target Reconnaissance— Pletinckx et al., IEEE EuroS&P 2023. https://doi.org/10.1109/EuroSP57164.2023.00038
•Don’t Phish-let Me Down: FIDO Authentication Downgrade— Proofpoint US. https://www.proofpoint.com/us/blog/threat-insight/dont-phish-let-me-down-fido-authentication-downgrade
•FIDO authentication undermined— CSO Online. https://www.csoonline.com/article/4040128/fido-undermined.html
•Puppeteer Stealth Anti-Detection. https://scrapingant.com/blog/avoid-detection-with-puppeteer-stealth
•Impact Assessment of Adversary-in-the-Middle Attack Evilginx Bypassing 2FA— Springer ICR 2025. https://link.springer.com/chapter/10.1007/978-3-031-95652-2_14
•Browser Fingerprinting Techniques. https://fingerprint.com/blog/browser-fingerprinting-techniques/
•Aged-domain acquisition bypass— The Record / newestek.com. https://cybersecurityinfocus.com/?p=8455