☰
Evilginx3+Gophish联动:高仿真钓鱼演练与反检测实战解析
2026/9/29 9:20:11 网站建设 项目流程

上个月给一家客户做钓鱼演练,我照旧用 Gophish 克隆了他们的 OA 登录页。点击率做到了 40% 以上,但客户安全团队第二天就发来一张截图,问我说:这页面干净得像产品演示,邮件网关一抓一个准,沙箱打开也是秒识别,你们有没有更接近真实攻击的玩法?

这个问题其实是很多安全团队的共同困境。Gophish 这类传统钓鱼模拟平台,强在投放、追踪和人员管理,但它的“页面”是克隆出来的静态副本,没有真实会话、没有双因子交互,更谈不上动态内容还原。于是我开始把演练链路升级成另一套组合:Evilginx3 做中间人代理,Gophish 做投放和统计中枢,再针对扫描器、沙箱和浏览器指纹做定制与反检测。这篇文章就记录一下我在授权红队演练里的完整落地思路,供同样在做防御验证的朋友参考。

先声明一句:下面所有技术动作,默认你已拿到目标资产所有者的书面授权。红队演练的底线是合规,别把这一套用在没签合同的系统上。

1. 为什么静态克隆页做不出高逼真的凭证捕获

1.1 Gophish 擅长的事和不擅长的事

Gophish 在钓鱼演练里依然是主力,因为它把大型演练的工程复杂度拆得足够清楚:用户组、邮件模板、发信配置、登录页、场景编排和结果统计,全部都能在一个面板里完成,还带完整的 REST API,方便自动化。

它的底层逻辑其实很简单:把克隆好的 HTML 页面托管在自己的服务器上,员工在页面上输入的内容会被记录到数据库,仅此而已。它不会和真实系统做交互,不会校验账号密码是否正确,更不会给用户一个可用的登录会话。

所以,如果你的演练目标是“看看谁点了链接、谁填了密码”,Gophish 是完全够用的。但如果你想模拟的是账户接管(ATO)、MFA 绕过、会话劫持这一类攻击路径,静态克隆页就无能为力了。这恰恰是现代真实钓鱼攻击最常用的手段,也是演练逼真度不足的根本原因。

1.2 Evilginx3 中间人代理链路到底做了什么

Evilginx3 是一个 Go 写的反向代理框架,核心思路是站在目标用户和真实网站之间,做“中间人”。我举一个很典型的例子,假设要模拟的目标是某企业的 SSO 登录页:

  • 你准备一个和目标域名高度相似的 lure 域名,解析到你的 VPS 上;
  • 员工点击钓鱼邮件里的链接,浏览器请求先打到 Evilginx3;
  • Evilginx3 按 phishlet 配置,去上游拉取真实登录页面,对 HTML 做实时改写后再返回给用户,用户看到的就是一个穿了自己企业域名的“正版”页面;
  • 用户在页面输入账号密码,Evilginx3 先截获明文凭证写入本地日志,然后把请求原样转发给真实网站;
  • 真实网站校验通过,返回 session cookie 和登录后的页面,Evilginx3 再把 cookie 记录下来,把正常页面返回给用户。

整个过程里,员工以为自己一直在访问真实系统,登录后还能正常跳转到内网首页,不会出现“表单提交后白屏”这种穿帮情况。如果目标站点开了双因子验证,短信验证码或动态口令同样会被 Evilginx3 实时中继,真实系统那边校验通过,它这边也同步把验证码记下来了。

这就是 Evilginx3 和传统静态克隆页的本质区别:它拿到的不只是一张表单截图,而是一整套真实凭证和真实会话。这也是为什么很多安全测评报告里,中间人钓鱼被列为当下 ATO 攻击的主流形态。

1.3 两个工具的合理分工

Gophish 和 Evilginx3 不是替代关系,而是组合关系。Gophish 负责人员导入、邮箱投放、结果统计;Evilginx3 负责 lure 页面、凭证捕获和会话转发。完整的攻击链路是这样的:

邮件由 Gophish 发出,员工点击链接,Gophish 侧按需记录一次点击事件,然后跳转到 Evilginx3 的 lure 地址;Evilginx3 代理真实站点,捕获凭证和 cookie,再把结果回传给 Gophish 或者你自己的数据中心。

这里最关键的,是把两个系统的事件流程串起来,而不是简单地把两个工具装在同一台服务器上。下一节先讲 Evilginx3 侧的定制,再讲联动设计。

2. phishlet 定制:决定钓鱼页“活性”的核心

2.1 默认 phishlet 远远不够用

Evilginx3 本身带了不少现成 phishlet,社区仓库里也能找到各大常见平台的配置。但用现成的 phishlet 直接上目标,你会很快撞见几个现实问题:目标站点的登录表单带了 CSRF token、页面是纯 JS 渲染的 SPA、登录成功后的跳转链接是相对路径、静态资源走了 CDN 导致代理页面上出现大量 404。

这些问题统一指向一个核心: phishlet 不是一个“配置开关”,而是一套 HTML 重写规则。你得把真实站点在代理链路上的表现调到和直接访问一致,才谈得上逼真。

2.2 字符串替换与子过滤器的理解

一套 phishlet 的核心配置大概长这样(不同版本字段会有差异,以你的实际版本为准):

# custom_sso.yaml(节选,仅作示意) hostname: "login.real-target.example.com" phish_title: "Sign in to your account" sub_filters: - "action=\"/login\"": "action=\"https://lure.company-sso.example.com/login\"" - "href=\"/assets/": "href=\"https://lure.company-sso.example.com/assets/" replacements: - "csrf_token": "csrf_token_x" credentials_user: "username" credentials_param: "password"

sub_filters的作用是告诉 Evilginx3 在从上游返回的 HTML 里,把关键字符串替换成代理域名,尤其是表单提交地址和静态资源地址。不替换的后果很直接:表单 action 还指向真实域名,用户提交时浏览器直接跑到真实站点去,Evilginx3 什么都截不到。

replacements则是更灵活的正则替换,可以用来处理跳转逻辑和页面里的内联脚本。实操里我建议先用 curl 把上游登录页完整拉下来,逐个检查表单 action、资源前缀、内联 JS 里的绝对 URL,再写替换规则,不要图省事做全量替换,否则很多脚本会被误伤,页面表现会很奇怪。

2.3 动态渲染页面怎么办

如果目标登录页是 Angular/Vue 这类 SPA,字符串替换只能解决资源路径,不能解决动态渲染。浏览器执行完 JS 之后,表单结构才真正生成,这时候替换规则根本没机会处理。

我的做法是给真实页面注入一段 hook 脚本,在页面加载完成后主动监听输入框内容变化并把值上报。用 phishlet 的注入能力把自定义 JS 写进响应体即可,脚本里选一个表单加载完成的时间点,把username和password字段的input事件转发给 Evilginx3 的凭证捕获逻辑。这里要注意的是注入脚本的触发时机,太早执行会因为元素未渲染而取不到值,需要加轮询或者DOMContentLoaded之后延迟执行。

2.4 证书与域名,一个最常见的影响逼真度的环节

lure 域名必须配有效 TLS 证书,这没什么好商量的。浏览器地址栏的证书报错和红色警告,对逼真度是毁灭性的。我习惯用 Let's Encrypt 申请通配证书,用 DNS-01 验证方式提前签发好,这样后续上线新子域不需要重新验证:

certbot certonly --manual --preferred-challenges dns \ -d lure.company-sso.example.com

申请完证书后,把它配置到 Evilginx3 的对应 lure 配置里。有一个坑要提醒你:证书续期任务别忽略。Evilginx3 自己不会帮你续证书,如果某一天演练还没结束,证书到期了,浏览器会立刻给出警告,整个演练就穿帮了。

另外,如果你在 l4 层启用了 CDN 或 WAF 流量清洗,务必确认它不会改写上游返回的 HTML。很多云端 WAF 默认会重新压缩或者注入自己的脚本,这在 Evilginx3 场景里会破坏替换规则的精准性。实战里我通常会关闭这类流量清洗,或者只在源站层面做安全组控制。

2.5 一个容易被忽略的细节:登录后的跳转

很多 phishlet 只关注登录页,不关心登录后的跳转。真实系统登录成功后往往会把用户带到 dashboard 或首页,如果 Evilginx3 对后续请求处理不当,用户会发现登录后页面停在空白页,或者跳回登录页,这时候警惕心会立刻拉满。

所以定制 phishlet 时,建议把上游站点的登录后首页也纳入代理范围,确认 cookie 能被持续转发,让整个浏览过程从头到尾都“能用”。这比只做一张表单难一些,但正是这种细节决定员工是否真的会填第二次验证码。

3. Gophish 与 Evilginx3 的联动链路设计

3.1 让用户标识跨系统流动

两个工具分开部署之后,第一个要解决的是统计口径:Gophish 知道发了哪些人,Evilginx3 知道你收到了谁的凭证,中间怎么对应起来?

我的方案很简单:让 lure 链接自带用户标识。在 Gophish 邮件模板里,按钮地址不写成固定的https://lure.company-sso.example.com/signin,而是拼上这个员工的唯一 ID:

https://lure.company-sso.example.com/signin?ref={{.Id}}

Gophish 模板引擎渲染时,每个员工收到的链接都带着自己的 ID。Evilginx3 的访问日志里能看到ref参数,配合它自带的 webhook 或日志接收器,就能把“谁在什么时间访问了 lure”完整还原出来。

如果连提交凭证的用户名也要和员工 ID 对应,建议在 lure 域名上加一层短链参数映射,或者干脆把员工 ID 放在路径里。别小看这个设计,没有它,演练结束后你会面对一堆凭证字符串,却不知道是谁交出来的。

3.2 由 Gophish 管投放,由 webhook 管回收

Gophish 的核心职责是用户分组、发信和投递状态统计。实际部署中,我把它的 Campaign 作为“邮件投放任务”看待,不依赖它内置的 landing page 记录点击,而是让它作为上游调度器。

Evilginx3 捕获凭证后,通过 webhook 把事件推给你的接收端。最简单的接收端就是一个 Flask 或者 Node 服务,收到事件后调用 Gophish API 把结果写回:

curl -X GET \ https://phish-server.example.com/api/campaigns/3/results \ -H "Authorization: Bearer ${GOPHISH_API_TOKEN}"

这里要强调的是,建议你自己写一个简单的回填脚本,因为 Gophish 自带的结果字段不一定能表达“已捕获有效凭证”和“只是点了链接”的差异。把 Evilginx3 捕获到的凭证状态映射到 Gophish 的status字段上,报告会干净很多。

补充一句,Evilginx3 的凭证捕获日志默认是按时间戳存的,如果你在一场演练里同时打多个 lure,务必在配置里把每个 lures 的访问日志区分开,归并日志是演练结束后最耗时的活。

3.3 邮件投递质量决定了你能触达多少人

说实话,很多演练不是栽在页面环节,而是栽在邮箱网关。如果你用的是一个小域名、刚注册两周、没有 SPF/DKIM/DMARC 记录,大部分目标企业的邮件网关会直接隔离或者拒收。

所以,邮件投递缓存这一块,我一般做三件事:

  1. 提前 2-4 周注册并“养”发送域名,期间用真实业务口吻发一些正常邮件,让域名积累基础信誉;
  2. 配全 SPF、DKIM、DMARC,DKIM 签名务必正确,很多邮件网关对 DKIM 校验失败会直接退信;
  3. 分批次发送而不是一次性全量发,避免同一个 IP 在短时间内产生大量退信,损耗发送者信誉。

另外,邮件内容要尽量本地化,落到具体的人和具体的事上。安全团队早就把“账号异常登录”这类词加入关键词规则了,模板写得太模板化,邮件根本到不了员工的收件箱。

3.4 并发和去重:别把演练做成攻击

演练和真实攻击有一个重要区别:真实攻击可以无节制地扫描、重试、扩大目标;演练则必须控制影响面。我建议分批投放,例如每天只给 200-300 人发邮件,目标系统上出现异常的密度要控制在安全团队能够接受的范围,避免对真实业务系统和 IdP 造成压力。

这里还有一个去重细节:同一个员工可能在一天内点多次链接。webhook 回收时要以员工 ID 为单位做去重,记录“第一次点击时间”和“最后一次提交时间”,而不是把每次点击都算新事件。否则演练报告里的数据会虚高,蓝队也会觉得你们在刷量。

4. 反检测:让流量更像真实用户,而不是自动收集器

4.1 会被哪些低等级检测拦下来

很多演练样本不是被人的分析拦下来的,而是被基础设施层面的自动检查拦下来的。我总结过最常见的三类:

  • 沙箱和无头浏览器:安全分析平台用 Headless Chrome 或 Selenium 访问链接,检测页面里是否有钓鱼特征,然后丢给 urlscan 这类在线沙箱做截图;
  • 主动扫描器和威胁情报平台:对 lure 域名做主动探测,拉取证书、请求根路径、访问常见路径,比对域名年龄和信誉库;
  • TLS 指纹和协议栈检查:部分安全网关会对 HTTPS 流量做 JA3/JA4 指纹比对,如果指纹和常见浏览器不一致,命中可疑标记。

这些检测不会直接判定你“恶意”,但会让你的域名迅速进入低信誉名单,后续再发邮件就非常吃亏。

4.2 落地过的一组反检测手段

我常配置的一组规则如下,大概可以描述为“对不同的来访请求给出不同的回答”。

请求特征判定倾向我的响应策略
curl、wget、非浏览器 UA扫描器/脚本直接返回 404 或空白页
HeadlessChrome、phantomjs、webdriver沙箱/爬虫返回真实站点首页或 302 到无害页面
普通浏览器 UA、真实 JS 执行真实用户返回 lure 页面
高频目录枚举路径扫描器返回 404,并限制 IP 频率

具体到实现,非浏览器 UA 和路径枚举这类规则,我放在 Nginx 前置层处理,不需要 Evilginx3 参与。Nginx 配置里简单判一下$http_user_agent,匹配到已知爬虫特征就return 404;,能挡住至少七成的扫描流量。

另外一个成本不高但很有效的做法,是在 lure 页面里注入一段 JavaScript 检测脚本:

if (navigator.webdriver || !window.chrome || navigator.userAgent.includes("HeadlessChrome")) { window.location.href = "/normal-home"; }

这段脚本命中沙箱或自动化浏览器时,立刻把跳转改成普通页面,在线沙箱截到的图就会变成无害内容。

4.3 延迟渲染:让沙箱“等不起”

沙箱的一大弱点是等待耐心非常有限,大部分沙箱只会等 5-15 秒。利用这一点,可以在 lure 页面里把登录表单的渲染延迟 8-10 秒,前端先显示一个 loading 动画,再做表单渲染。真实用户会等一下,但沙箱大概率在表单生成前就完成了截图和 DOM 分析。

这个技巧在真实攻击里很常见,但在演练里同样有价值。它能让你的样本在 urlscan 里截不到关键表单,从而降低被标记的概率。

4.4 Nginx 前置反代:解决 TLS 指纹差异

Evilginx3 默认用 Go 的标准库处理 TLS,JA3 指纹和主流浏览器差别比较大,对防御水平较高、接了 TLS 指纹检测网关的企业,这个差异会成为可疑标记。

我在实战中通常在 Evilginx3 前面加一个 Nginx,由 Nginx 终结 TLS,再把流量转发给 Evilginx3 端口:

server { listen 443 ssl http2; server_name lure.company-sso.example.com; ssl_certificate /etc/letsencrypt/live/lure.company-sso.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/lure.company-sso.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; } }

这样能让 TLS 指纹更接近常见的 Nginx 站点,而不是暴露 Go HTTP 服务器的特征。同时,Nginx 前置层也方便挂对手动扫描器、路径枚举等自动化流量做拦截,不用在 Evilginx3 里写一堆分支逻辑。

4.5 节奏也是反检测的一部分

还有一个经常被忽略的点:新域名上线后不要立刻铺开发送。先注册域名,建好证书,放一个正常的静态页面养 1-2 周,期间用这个小域名发一些完全无害的测试邮件,等它在公开 DNS 和邮件信誉库里的记录有了一定基础,再做正式投放。

同时,lure 服务器的访问频率要控制。Evilginx3 的日志里如果出现同一个 IP 短时间反复请求多个 lure,说明你的域名可能已经被情报平台拉进扫描队列了。这时候要么换域名,要么暂停两天再继续。

5. 演练反过来指导防御:ATO 检测与加固清单

5.1 从演练数据里提炼 ATO 指标

一场演练结束,除了报告“多少人点了链接、多少人提交了密码”,更值钱的是那些被安全团队忽略的异常事件。我习惯把 Evilginx3 记录的访问日志和真实业务日志做交叉分析,重点关注这几个指标:

  • 同一个 IP 在短时间内登录了多个不同账号;
  • 同一个 UA 指纹出现在多个会话中;
  • 用户在“登录成功”后的访问行为,和平时的工作时段、常用设备差异很大;
  • 2FA 验证码被输入后又迅速重放,间隔通常只有几秒。

这些指标本身也是真实账户接管攻击的典型行为。把演练样本导入 SOC 的告警规则,可以有效补齐检测盲区。

5.2 蓝队怎么反向识别这种代理钓鱼

从防御角度看,判断一个访问是不是 Evilginx3 这类代理框架发出的,有几个切入点:

  1. 证书透明度日志的主动监控。在 crt.sh 上跑一个定时任务,对近似域名做模糊匹配,凡是新出现的证书绑定到陌生 IP 的,一律人工确认。lure 域名必须配证书,这一步能发现至少一半的钓鱼基础设施。

  2. 客户端指纹的异常度评分。给登录请求标注浏览器指纹可信度,数据中心 IP、非常见语言包、TLS 指纹异常组合在一起,风险分数会非常高。

  3. 登录后的 Cookie 重放检测。真实用户从一个网络环境切换到另一个,通常会有时间和地理上的逻辑一致性;Cookie 在几秒内出现在两个不同 IP 上,基本可以判定为会话被转发。

  4. 邮件网关的链接信誉策略。对邮件中出现的链接做实时信誉查询,结合域名年龄、发件域名一致性、DMARC 结果,在链接被点击前就做隔离。

5.3 演练后的加固清单

每次演练结束后,我会向客户交付一份参考清单,内容大致包括:

  • 对点击过 lure 且输入过真实密码的员工,立即强制重置密码并在一周后复查,防止演练产生的真实凭证被用于后续攻击;
  • 对高风险且频繁访问敏感系统的账号,启用条件访问策略,新设备登录必须走设备合规检查;
  • 邮件安全方面,把新建域名、低信誉域名链接启用 URL 重写和点击时隔离,对 DKIM 校验失败的邮件标记为可疑;
  • 身份侧启用异常登录的风险评分,对跨地域跳跃、新设备、深夜访问等行为做自动化阻断。

这套清单的价值在于,把一次模拟攻击的发现,真正转化成可量化的安全能力提升。演练不是目的,让演练结果推动防御改进才是关键。

最后再分享一点个人经验:Evilginx3 和 Gophish 的组合,最难的不是装起来,而是你愿意花多少时间去打磨细节。页面活性、动态渲染处理、邮件投递质量、日志对应关系,每一个环节偷懒,都会在演练中途某个时间点以穿帮的形式反馈给你。反检测的思路也一样,它不是让你变成隐形人,而是把你的样本从“一眼假”推进到“需要蓝队认真分析才能判定”的水平。这个水平线,才是演练的真实价值所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询