浏览器扩展 Pentesting 方法论

Reading time: 37 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

基本信息

浏览器扩展使用 JavaScript 编写,并由浏览器在后台加载。它有自己的 DOM,但可以与其他站点的 DOM 交互。这意味着它可能危及其他站点的保密性、完整性和可用性 (CIA)。

主要组件

扩展的布局在可视化时最清晰,通常由三个组件组成。让我们深入了解每个组件。

http://webblaze.cs.berkeley.edu/papers/Extensions.pdf

内容脚本

每个内容脚本可以直接访问单个网页的 DOM,因此容易受到潜在的恶意输入的影响。然而,内容脚本除了向扩展核心发送消息的能力外,不包含其他权限。

扩展核心

扩展核心包含大多数扩展的特权/访问权限,但扩展核心只能通过 XMLHttpRequest 和内容脚本与网页内容交互。此外,扩展核心没有对主机的直接访问权限。

本地二进制

扩展允许一个本地二进制,该二进制可以以用户的全部权限访问主机。本地二进制通过标准的 Netscape Plugin Application Programming Interface (NPAPI) 与扩展核心交互,该接口被 Flash 和其他浏览器插件使用。

边界

caution

要获取用户的全部权限,攻击者必须诱使扩展将恶意输入从内容脚本传递到扩展核心,并再从扩展核心传递到本地二进制。

扩展的每个组件之间通过强大的保护边界相互隔离。每个组件在单独的操作系统进程中运行。内容脚本和扩展核心运行在大多数操作系统服务无法访问的沙箱进程中。

此外,内容脚本通过运行在独立的 JavaScript 堆与其关联的网页分离。内容脚本和网页可以访问相同的底层 DOM,但两者从不交换 JavaScript 指针,从而防止 JavaScript 功能泄露。

manifest.json

A Chrome extension is just a ZIP folder with a .crx file extension. The extension's core is the manifest.json file at the root of the folder, which specifies layout, permissions, and other configuration options.

Example:

json
{
"manifest_version": 2,
"name": "My extension",
"version": "1.0",
"permissions": ["storage"],
"content_scripts": [
{
"js": ["script.js"],
"matches": ["https://example.com/*", "https://www.example.com/*"],
"exclude_matches": ["*://*/*business*"]
}
],
"background": {
"scripts": ["background.js"]
},
"options_ui": {
"page": "options.html"
}
}

content_scripts

内容脚本在用户导航到匹配的页面时会被加载,在我们的例子中指任意匹配 https://example.com/* 且不匹配 *://*/*/business* 正则的页面。它们执行 像页面自身的脚本一样,并对页面的 Document Object Model (DOM) 拥有任意访问权限。

json
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],

为了包含或排除更多 URL,也可以使用 include_globsexclude_globs

这是一个示例 content script,当使用 the storage API 从扩展的存储中检索 message 值时,会向页面添加一个 explain 按钮。

js
chrome.storage.local.get("message", (result) => {
let div = document.createElement("div")
div.innerHTML = result.message + " <button>Explain</button>"
div.querySelector("button").addEventListener("click", () => {
chrome.runtime.sendMessage("explain")
})
document.body.appendChild(div)
})

当点击此按钮时,内容脚本(content script)会通过使用 runtime.sendMessage() API 向扩展页面发送消息。这是因为内容脚本在直接访问 APIs 时受到限制,storage 是为数不多的例外之一。对于这些例外之外的功能,内容脚本会向扩展页面发送消息以进行通信。

warning

根据浏览器不同,内容脚本(content script)的能力可能会略有差异。对于基于 Chromium 的浏览器,能力列表可见于 Chrome Developers documentation,而对于 Firefox,主要参考来源是 MDN
另外值得注意的是,内容脚本可以与 background scripts(后台脚本)通信,从而执行操作并将响应返回。

在 Chrome 中查看和调试内容脚本,可以通过菜单中的 Options > More tools > Developer tools 打开 Chrome 开发者工具,或按 Ctrl + Shift + I。

打开开发者工具后,点击 Source tab,然后选择 Content Scripts 选项卡。这可以观察来自各个扩展的运行中内容脚本,并设置断点以跟踪执行流程。

注入的内容脚本

tip

请注意,Content Scripts 并非强制要求,也可以通过 动态 注入 脚本或使用 tabs.executeScript 在网页中 以编程方式注入它们。这实际上提供了更细粒度的控制

要以编程方式注入内容脚本,扩展必须对要注入脚本的页面具有 host permissions。这些权限可以通过在扩展的 manifest 中 请求它们 来获得,或者通过 activeTab 临时获得。

基于 activeTab 的扩展示例

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • 在点击时 Inject JS 文件:
javascript
// content-script.js
document.body.style.backgroundColor = "orange"

//service-worker.js - Inject the JS file
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"],
})
})
  • 在点击时注入函数:
javascript
//service-worker.js - Inject a function
function injectedFunction() {
document.body.style.backgroundColor = "orange"
}

chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: injectedFunction,
})
})

具有脚本权限的示例

javascript
// service-workser.js
chrome.scripting.registerContentScripts([
{
id: "test",
matches: ["https://*.example.com/*"],
excludeMatches: ["*://*/*business*"],
js: ["contentScript.js"],
},
])

// Another example
chrome.tabs.executeScript(tabId, { file: "content_script.js" })

为了包含或排除更多 URL,也可以使用 include_globsexclude_globs

内容脚本 run_at

run_at 字段控制 何时将 JavaScript 文件注入到网页中。首选且默认值是 "document_idle"

可能的取值有:

  • document_idle: 尽可能
  • document_start: 在任何来自 css 的文件之后,但在构建任何其他 DOM 或运行任何其他脚本之前。
  • document_end: 在 DOM 完成后立即,但在诸如图片和框架等子资源加载之前。

通过 manifest.json

json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}

通过 service-worker.js

javascript
chrome.scripting.registerContentScripts([
{
id: "test",
matches: ["https://*.example.com/*"],
runAt: "document_idle",
js: ["contentScript.js"],
},
])

background

内容脚本发送的消息由 背景页 接收,背景页在协调扩展组件方面起着核心作用。值得注意的是,背景页在扩展的整个生命周期内持续存在,悄无声息地运行且无需与用户直接交互。它拥有自己的文档对象模型 (DOM),能够实现复杂的交互和状态管理。

Key Points:

  • Background Page Role: 充当扩展的中枢神经,确保扩展各部分之间的通信与协调。
  • Persistence: 它是一个始终存在的实体,对用户不可见但对扩展功能至关重要。
  • Automatic Generation: 如果未明确定义,浏览器会自动创建一个背景页。该自动生成的页面将包含扩展 manifest 中指定的所有 background scripts,从而确保扩展后台任务的无缝运行。

tip

浏览器在未显式声明时自动生成背景页的便利性可确保所有必要的 background scripts 被集成并可运行,从而简化扩展的设置过程。

Example background script:

js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "explain") {
chrome.tabs.create({ url: "https://example.net/explanation" })
}
})

它使用 runtime.onMessage API 来监听消息。当收到一个 "explain" 消息时,它使用 tabs API 在新标签页中打开一个页面。

要调试后台脚本,你可以前往 扩展详情并检查 service worker,这会打开包含后台脚本的开发者工具:

Options pages and other

Browser extensions can contain various kinds of pages:

  • Action pages 会在点击扩展图标时以 下拉 形式显示。
  • 扩展会 在新标签页加载 的页面。
  • Option Pages:当点击时,此页面会显示在扩展之上。在我之前的 manifest 中,我可以通过 chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca 访问此页面,或者点击:

注意这些页面不像后台页面那样是持久的,它们会在需要时动态加载内容。尽管如此,它们与后台页面共享某些能力:

  • Communication with Content Scripts: 与后台页面类似,这些页面可以接收来自 content scripts 的消息,从而实现扩展内的交互。
  • Access to Extension-Specific APIs: 这些页面可以访问扩展特定的 API,受扩展声明的 permissions 限制。

permissions & host_permissions

permissionshost_permissionsmanifest.json 中的条目,用于指示浏览器扩展拥有哪些权限(storage、location...)以及适用于哪些网页。

由于浏览器扩展可能具有很高的特权,恶意扩展或被攻破的扩展可能让攻击者有不同方式窃取敏感信息并监视用户。

查看这些设置如何工作以及它们可能如何被滥用,参考:

BrowExt - permissions & host_permissions

content_security_policy

可以在 manifest.json 中声明 content security policy。如果定义了不当的策略,它可能是 易被利用的

浏览器扩展页面的默认设置相当严格:

bash
script-src 'self'; object-src 'self';

有关 CSP 和潜在绕过方法的更多信息,请查看:

Content Security Policy (CSP) Bypass

web_accessible_resources

为了让网页访问浏览器扩展的某个页面,例如 .html 页面,该页面需要在 manifest.jsonweb_accessible_resources 字段中列出。
例如:

javascript
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}

这些页面可通过如下 URL 访问:

chrome-extension://<extension-id>/message.html

在公开扩展中,extension-id 是可访问的

不过,如果使用了 manifest.json 参数 use_dynamic_url,该 id 可能是动态的

tip

注意即使某个页面在此处被提及,它也可能因为 Content Security Policy受到 ClickJacking 防护。因此在确认 ClickJacking 攻击可行之前,你还需要检查它(frame-ancestors 部分)。

允许访问这些页面会使这些页面可能易受 ClickJacking 攻击

BrowExt - ClickJacking

tip

仅允许扩展加载这些页面而不允许任意 URL 加载,可能可以防止 ClickJacking 攻击。

caution

注意,来自 web_accessible_resources 的页面以及扩展的其他页面也能够联系 background scripts。因此,如果这些页面中的某个页面存在 XSS,可能会导致更严重的漏洞。

此外,请注意你只能在 iframe 中打开 web_accessible_resources 中列出的页面,但如果在新标签页中并且知道 extension ID,就可以访问扩展中的任意页面。因此,如果发现利用相同参数的 XSS,即使该页面未在 web_accessible_resources 中配置,仍然可能被利用。

externally_connectable

根据 docs"externally_connectable" manifest 属性声明了可以通过 runtime.connectruntime.sendMessage 与你的扩展连接的哪些扩展和网页

  • 如果 externally_connectable在你扩展的 manifest 中声明,或声明为 "ids": ["*"]所有扩展都可以连接,但没有网页可以连接
  • 如果指定了特定 ID,例如 "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]只有那些应用可以连接。
  • 如果指定了 matches,那些 web 应用将能够连接:
json
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • 如果指定为空: "externally_connectable": {},没有应用或网页能够连接。

这里列出的扩展和 URLs越少,攻击面就越小。

caution

如果在 externally_connectable 中指明了一个对 XSS or takeover 易受攻击的网页,攻击者将能够 直接向 background script 发送消息,完全绕过 Content Script 及其 CSP。

因此,这是一个非常强大的 bypass

此外,如果客户端安装了一个 rogue extension,即便它不被允许与易受攻击的扩展通信,它仍可能在被允许的网页注入 XSS data,或滥用 WebRequestDeclarativeNetRequest APIs 来操纵目标域上的请求,从而更改页面对 JavaScript file 的请求。(注意目标页面上的 CSP 可能会阻止这些攻击)。这个想法来自 from this writeup.

通信摘要

Extension <--> WebApp

在 content script 与网页之间通信通常使用 post messages。因此在 web 应用中通常会看到对函数 window.postMessage 的调用,而在 content script 中会有像 window.addEventListener 的监听器。注意,extension 也可以通过发送 Post Message 与 web 应用通信(因此网页应当预期到它),或者只是让网页加载一个新的脚本。

Extension 内部

通常使用函数 chrome.runtime.sendMessage 在扩展内部发送消息(通常由 background script 处理),为了接收和处理该消息,会声明一个监听器,调用 chrome.runtime.onMessage.addListener

也可以使用 chrome.runtime.connect() 建立持久连接,而不是发送单条消息;可以用它来发送接收****消息,例如下面的示例:

chrome.runtime.connect() 示例
javascript
var port = chrome.runtime.connect()

// Listen for messages from the web page
window.addEventListener(
"message",
(event) => {
// Only accept messages from the same window
if (event.source !== window) {
return
}

// Check if the message type is "FROM_PAGE"
if (event.data.type && event.data.type === "FROM_PAGE") {
console.log("Content script received: " + event.data.text)
// Forward the message to the background script
port.postMessage({ type: "FROM_PAGE", text: event.data.text })
}
},
false
)

// Listen for messages from the background script
port.onMessage.addListener(function (msg) {
console.log("Content script received message from background script:", msg)
// Handle the response message from the background script
})

也可以从 background script 向位于特定 tab 的 content script 发送消息,调用 chrome.tabs.sendMessage,你需要指定要发送消息的 tab 的 ID

从被 externally_connectable 允许的来源到 extension

externally_connectable 配置中被允许的 Web apps and external browser extensions 可以使用以下方式发送请求:

javascript
chrome.runtime.sendMessage(extensionId, ...

Where it's needed to mention the extension ID.

Native Messaging

background scripts 有可能与系统中的 binaries 通信,如果这种通信没有得到妥善保护,可能会导致严重漏洞,例如 RCEsMore on this later.

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

Web ↔︎ Content Script 通信

运行 content scripts 的环境与宿主页面所在的环境相互分离,从而保证了隔离性。尽管存在这种隔离,双方都可以与页面的文档对象模型 (DOM) 交互,该模型是一个共享资源。宿主页面若要与 content script 进行通信,或通过 content script 间接与扩展通信,就必须使用双方都可访问的 DOM 作为通信通道。

Post Messages

content-script.js
// This is like "chrome.runtime.sendMessage" but to maintain the connection
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 === "FROM_PAGE") {
console.log("Content script received: " + event.data.text)
// Forward the message to the background script
port.postMessage(event.data.text)
}
},
false
)
example.js
document.getElementById("theButton").addEventListener(
"click",
() => {
window.postMessage(
{ type: "FROM_PAGE", text: "Hello from the webpage!" },
"*"
)
},
false
)

A secure Post Message communication should check the authenticity of the received message, this can be done checking:

  • event.isTrusted: 只有当事件由用户操作触发时才为 True
  • The content script might expecting a message only if the user performs some action
  • origin domain: 可能只允许来自白名单域的消息
  • If a regex is used, be very careful
  • Source: received_message.source !== window 可以用来检查消息是否来自 Content Script 正在监听的同一窗口

The previous checks, even if performed, could be vulnerable, so check in the following page potential Post Message bypasses:

PostMessage Vulnerabilities

Iframe

Another possible way of communication might be through Iframe URLs, you can find an example in:

BrowExt - XSS Example

DOM

This isn't "exactly" a communication way, but the web and the content script will have access to the web DOM. So, if the content script is reading some information from it, trusting the web DOM, the web could modify this data (because the web shouldn't be trusted, or because the web is vulnerable to XSS) and compromise the Content Script.

You can also find an example of a DOM based XSS to compromise a browser extension in:

BrowExt - XSS Example

Content Script ↔︎ Background Script Communication

A Content Script can use the functions runtime.sendMessage() or tabs.sendMessage() to send a one-time JSON-serializable message.

To handle the response, use the returned Promise. Although, for backward compatibility, you can still pass a callback as the last argument.

Sending a request from a content script looks like this:

javascript
;(async () => {
const response = await chrome.runtime.sendMessage({ greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()

extension(通常是 background script)发送请求。示例:如何向所选 tab 中的 content script 发送 message:

javascript
// From https://stackoverflow.com/questions/36153999/how-to-send-a-message-between-chrome-extension-popup-and-content-script
;(async () => {
const [tab] = await chrome.tabs.query({
active: true,
lastFocusedWindow: true,
})
const response = await chrome.tabs.sendMessage(tab.id, { greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()

接收端,你需要设置一个runtime.onMessage 事件监听器来处理该消息。无论是在内容脚本(content script)还是扩展页面(extension page),这看起来是相同的。

javascript
// From https://stackoverflow.com/questions/70406787/javascript-send-message-from-content-js-to-background-js
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" })
})

在示例中,sendResponse() 以同步方式执行。要将 onMessage 事件处理程序修改为异步执行 sendResponse(),必须包含 return true;

重要的一点是,当多个页面都设置为接收 onMessage 事件时,对于某个特定事件,第一个执行 sendResponse() 的页面 将是唯一能够有效发送响应的页面。对此事件的任何后续响应都不会被考虑。

在编写新扩展时,应优先使用 promises 而非回调。关于使用回调,只有在同步上下文中直接执行 sendResponse(),或事件处理程序通过返回 true 表示异步操作时,sendResponse() 才被视为有效。如果没有任何处理程序返回 true,或 sendResponse() 被从内存中移除(垃圾回收),则与 sendMessage() 相关联的回调将被默认触发。

Native Messaging

浏览器扩展也允许通过 stdin 与系统中的二进制程序通信。应用程序必须安装一个 json 来指明这一点,格式如下:

json
{
"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/"]
}

其中 name 是传递给 runtime.connectNative()runtime.sendNativeMessage() 的字符串,用于从浏览器扩展的 background scripts 与该应用程序通信。path 是二进制文件的路径,只有一种有效的 type:stdio(使用 stdin 和 stdout),allowed_origins 指明可以访问它的扩展(不能使用通配符)。

Chrome/Chromium 会在 Windows 注册表的某些位置以及 macOS 和 Linux 的一些路径中搜索此 json(更多信息见 docs)。

tip

浏览器扩展还需要声明 nativeMessaing permission,才能使用此通信。

下面是一些 background script 代码向 native application 发送消息的示例:

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

In this blog post, a vulnerable pattern abusing native messages is proposed:

  1. Browser extension has a wildcard pattern for content script.
  2. Content script passes postMessage messages to the background script using sendMessage.
  3. Background script passes the message to native application using sendNativeMessage.
  4. Native application handles the message dangerously, leading to code execution.

And inside of it an example of going from any page to RCE abusing a browser extension is explained.

内存/代码/剪贴板中的敏感信息

如果 Browser Extension 在 内存中存储敏感信息,这些信息可能会被 dumped(尤其是在 Windows 机器上)并被搜索以获取相应信息。

因此,Browser Extension 的内存 不应被视为安全,像凭证或助记词等 敏感信息 不应被存储

当然,不要在代码中放置敏感信息,因为它将是 公开的

要从浏览器 dump memory,你可以 dump the process memory,或者进入 Browser Extension 的 settings,点击 Inspect pop-up -> 在 Memory 部分 -> Take a snaphost 并使用 CTRL+F 在快照中搜索敏感信息。

在浏览器中加载扩展

  1. Download the Browser Extension & unzipped
  2. Go to chrome://extensions/ and enable the Developer Mode
  3. Click the Load unpacked button

In Firefox you go to about:debugging#/runtime/this-firefox and click Load Temporary Add-on button.

从商店获取源码

The source code of a Chrome extension can be obtained through various methods. Below are detailed explanations and instructions for each option.

通过命令行下载 Extension 为 ZIP

可以使用命令行将 Chrome extension 的源代码下载为 ZIP 文件。该过程涉及使用 curl 从特定 URL 获取 ZIP 文件,然后将 ZIP 内容解压到目录中。步骤如下:

  1. Replace "extension_id" with the actual ID of the extension.
  2. Execute the following commands:
bash
extension_id=your_extension_id   # Replace with the actual extension ID
curl -L -o "$extension_id.zip" "https://clients2.google.com/service/update2/crx?response=redirect&os=mac&arch=x86-64&nacl_arch=x86-64&prod=chromecrx&prodchannel=stable&prodversion=44.0.2403.130&x=id%3D$extension_id%26uc"
unzip -d "$extension_id-source" "$extension_id.zip"

使用 CRX Viewer 网站

https://robwu.nl/crxviewer/

使用 CRX Viewer extension

另一种便捷的方法是使用 Chrome Extension Source Viewer,这是一个开源项目。它可以从 Chrome Web Store 安装。查看器的源代码可在其 GitHub repository 中获取。

查看本地已安装扩展的源代码

Chrome 本地安装的扩展也可以被检查。方法如下:

  1. 通过访问 chrome://version/ 并定位 "Profile Path" 字段来访问你的 Chrome 本地配置文件目录。
  2. 在配置文件目录中导航到 Extensions/ 子文件夹。
  3. 该文件夹包含所有已安装的扩展,通常其源代码以可读格式存在。

要识别扩展,你可以将它们的 ID 映射到名称:

  • about:extensions 页面启用 Developer Mode 以查看每个扩展的 ID。
  • 在每个扩展的文件夹中,manifest.json 文件包含可读的 name 字段,帮助你识别该扩展。

使用文件归档工具或解包工具

前往 Chrome Web Store 并下载扩展。文件将具有 .crx 扩展名。将文件扩展名从 .crx 更改为 .zip。使用任何文件归档工具(例如 WinRAR、7-Zip 等)来解压 ZIP 文件的内容。

在 Chrome 中使用 Developer Mode

打开 Chrome 并转到 chrome://extensions/。在右上角启用 "Developer mode"。点击 "Load unpacked extension..."。导航到你的扩展所在目录。这不会下载源代码,但对于查看和修改已下载或正在开发的扩展代码很有用。

Chrome extension manifest dataset

为了尝试发现易受攻击的浏览器扩展,你可以使用 thehttps://github.com/palant/chrome-extension-manifests-dataset 并检查它们的 manifest 文件以寻找潜在的易受攻击迹象。例如,要检查拥有超过 25000 名用户、包含 content_scripts 并具有权限 nativeMessaing 的扩展:

bash
# Query example from https://spaceraccoon.dev/universal-code-execution-browser-extensions/
node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')"

Post-exploitation: Forced extension load & persistence (Windows)

Stealthy technique to backdoor Chromium by directly editing per-user Preferences and forging valid HMACs, causing the browser to accept and activate an arbitrary unpacked extension without prompts or flags.

Forced Extension Load Preferences Mac Forgery Windows

安全审计检查清单

尽管 浏览器扩展 (Browser Extensions) 的攻击面通常较小,但部分扩展可能包含漏洞或存在可改进的加固空间。以下是最常见的检查项:

  • 尽可能限制请求的 permissions
  • 尽可能限制 host_permissions
  • 使用强健的 content_security_policy
  • 尽可能限制 externally_connectable,如果不需要则不要默认开放,指定 {}
  • 如果提到的 URL 对 XSS 或 takeover 存在漏洞,攻击者将能够直接向 background scripts 发送消息。非常强力的绕过方式。
  • 尽可能限制 web_accessible_resources,如果可能则留空。
  • 如果 web_accessible_resources 不是空,检查是否存在 ClickJacking
  • 如果扩展与网页之间有任何通信,检查通信中是否存在导致的 XSS 漏洞
  • 如果使用 Post Messages,检查 Post Message vulnerabilities
  • 如果 Content Script 访问 DOM 细节,检查当页面修改这些细节时是否会引入 XSS
  • 特别关注 Content Script -> Background script 的通信是否也涉及上述问题
  • 如果 background script 使用 native messaging 通信,检查通信是否安全且已做清理/校验
  • 不应将敏感信息存储在扩展的代码中
  • 不应将敏感信息存储在扩展的内存中
  • 不应将敏感信息以未保护形式存储在文件系统中

浏览器扩展风险

  • 应用 https://crxaminer.tech/ 会分析扩展请求的权限等数据,以评估使用该扩展的风险等级。

工具

Tarnish

  • 从给定的 Chrome webstore 链接拉取任意 Chrome extension。
  • manifest.json viewer:以格式化的 JSON 显示扩展的 manifest。
  • 指纹分析:检测 web_accessible_resources 并自动生成 Chrome extension 指纹化的 JavaScript。
  • 潜在 Clickjacking 分析:检测将 HTML 页面通过 web_accessible_resources 指令暴露的扩展页面。根据页面用途,这些页面可能容易遭受 clickjacking。
  • Permission Warning(s) viewer:显示用户尝试安装扩展时会看到的所有 Chrome 权限提示警告列表。
  • 危险函数定位:显示可能被攻击者利用的危险函数位置(例如 innerHTML、chrome.tabs.executeScript 等)。
  • 入口点(Entry Point(s)):显示扩展接收用户/外部输入的位置,有助于理解扩展的攻击面并寻找可发送恶意数据的点。
  • Dangerous Function(s) 和 Entry Point(s) 扫描器对其生成的告警会提供:
    • 导致告警的相关代码片段及行号。
    • 问题描述。
    • “View File” 按钮以查看包含该代码的完整源文件。
    • 被告警文件的路径。
    • 被告警文件在 Chrome 扩展中的完整 URI。
    • 文件类型(例如 Background Page script、Content Script、Browser Action 等)。
    • 如果易受影响的行在 JavaScript 文件中,还会列出所有包含该文件的页面路径、这些页面的类型,以及它们的 web_accessible_resource 状态。
  • Content Security Policy (CSP) 分析器与绕过检查器:指出扩展 CSP 的薄弱点,并揭示由于白名单 CDN 等因素可能导致的 CSP 绕过路径。
  • 已知易受攻击库:使用 Retire.js 检查是否使用了已知存在漏洞的 JavaScript 库。
  • 下载扩展以及格式化后的版本。
  • 下载原始扩展。
  • 下载美化后的扩展(自动 prettified 的 HTML 和 JavaScript)。
  • 自动缓存扫描结果:首次运行扩展扫描可能较慢,但如果扩展未更新,第二次运行会因为缓存而几乎即时。
  • 可链接的报告 URL,便于将 Tarnish 生成的扩展报告分享给他人。

Neto

Project Neto 是一个为 Python 3 设计的包,用于分析并揭示浏览器插件和扩展(如 Firefox 和 Chrome)中的隐藏特性。它自动解压打包文件,从扩展中的相关资源(如 manifest.json、本地化文件夹或 Javascript 和 HTML 源文件)提取这些特性。

参考资料

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks