hacktricks/pentesting-web/browser-extension-pentesting-methodology
2024-02-05 20:18:17 +00:00
..
browext-clickjacking.md Translated ['pentesting-web/browser-extension-pentesting-methodology/REA 2023-12-31 04:43:12 +00:00
browext-permissions-and-host_permissions.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00
browext-xss-example.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00
README.md Translated ['forensics/basic-forensic-methodology/specific-software-file 2024-02-05 20:18:17 +00:00

浏览器扩展渗透测试方法论

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS红队专家

支持HackTricks的其他方式

基本信息

浏览器扩展是用JavaScript编写的并由浏览器在后台加载。它有自己的DOM但可以与其他网站的DOM进行交互。这意味着它可能会危及其他网站的机密性、完整性和可用性CIA

主要组件

扩展布局在可视化时效果最佳,由三个组件组成。让我们深入了解每个组件。

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

内容脚本

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

扩展核心

扩展核心包含大部分扩展权限/访问权限,但扩展核心只能通过XMLHttpRequest和内容脚本与Web内容进行交互。此外扩展核心无法直接访问主机机器。

本机二进制

扩展允许本机二进制文件以用户完整权限访问主机机器。本机二进制通过Flash和其他浏览器插件使用的标准Netscape插件应用程序编程接口NPAPI)与扩展核心进行交互。

边界

{% hint style="danger" %} 要获取用户的完整权限,攻击者必须说服扩展将内容脚本中的恶意输入传递给扩展核心,再从扩展核心传递给本机二进制文件。 {% endhint %}

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

此外,内容脚本通过在单独的JavaScript堆中运行与其关联的网页分离。内容脚本和网页具有对同一底层DOM的访问权限,但两者不会交换JavaScript指针从而防止JavaScript功能的泄漏。

manifest.json

Chrome扩展只是一个带有.crx文件扩展名的ZIP文件夹。扩展的核心是位于文件夹根目录的**manifest.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*正则表达式的页面。它们像页面自己的脚本一样执行,并且可以任意访问页面的文档对象模型DOM

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

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

这是一个示例内容脚本,当使用存储API从扩展的存储中检索message值时,将在页面上添加一个解释按钮。

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);
});

当单击此按钮时,内容脚本通过利用runtime.sendMessage() API向扩展页面发送消息。这是因为内容脚本在直接访问API方面存在限制storage是少数例外之一。对于超出这些例外的功能,消息将发送到内容脚本可以与之通信的扩展页面。

{% hint style="warning" %} 根据浏览器的不同内容脚本的功能可能略有不同。对于基于Chromium的浏览器功能列表可在Chrome开发者文档中找到而对于FirefoxMDN是主要来源。
值得注意的是,内容脚本具有与后台脚本通信的能力,使其能够执行操作并传递响应。 {% endhint %}

要在Chrome中查看和调试内容脚本可以从“选项”>“更多工具”>“开发者工具”或按Ctrl + Shift + I打开Chrome开发者工具菜单。

在显示开发者工具后,应单击源标签,然后单击内容脚本标签。这样可以观察来自各种扩展的运行内容脚本,并设置断点以跟踪执行流程。

注入的内容脚本

{% hint style="success" %} 请注意内容脚本不是强制性的,也可以通过**tabs.executeScript在网页中动态注入脚本并以编程方式注入它们。这实际上提供了更多细粒度的控制**。 {% endhint %}

要以编程方式注入内容脚本,扩展需要具有主机权限,以便将脚本注入到其中的页面。这些权限可以通过在扩展的清单中请求它们或通过activeTab在临时基础上获得。

示例基于activeTab的扩展

{% code title="manifest.json" %}

{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}

{% endcode %}

  • 点击时注入JS文件
// 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"]
});
});
  • 点击时注入函数:
//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,
});
});

具有脚本权限的示例

// 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

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

通过 service-worker.js

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

背景

由内容脚本发送的消息将被背景页接收背景页在协调扩展组件方面发挥着中心作用。值得注意的是背景页在整个扩展的生命周期中持续存在不需要直接用户交互。它拥有自己的文档对象模型DOM可以实现复杂的交互和状态管理。

关键要点

  • 背景页角色: 充当扩展的神经中枢,确保扩展各部分之间的通信和协调。
  • 持久性: 它是一个始终存在的实体,对用户不可见但对扩展功能至关重要。
  • 自动生成: 如果未明确定义,浏览器将自动生成一个背景页。这个自动生成的页面将包括扩展清单中指定的所有背景脚本,确保扩展的背景任务无缝运行。

{% hint style="success" %} 浏览器自动生成背景页(未明确声明时)提供的便利性确保了所有必要的背景脚本被集成和运行,简化了扩展的设置过程。 {% endhint %}

示例背景脚本:

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

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

要调试后台脚本,您可以转到扩展详细信息并检查服务工作者,这将使用后台脚本打开开发者工具:

选项页面和其他

浏览器扩展可以包含各种类型的页面:

  • 操作页面在单击扩展图标时显示在下拉菜单中。
  • 扩展将在新标签页中加载的页面
  • 选项页面:单击时显示在扩展顶部的页面。在我的情况下,我可以在chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca中访问此页面或单击:

请注意,这些页面不像后台页面那样持久,因为它们根据需要动态加载内容。尽管如此,它们与后台页面共享某些功能:

  • **与内容脚本通信:**类似于后台页面,这些页面可以从内容脚本接收消息,促进扩展内的交互。
  • **访问扩展特定的API**这些页面可以全面访问扩展特定的API前提是为扩展定义了权限。

permissionshost_permissions

permissionshost_permissionsmanifest.json中的条目,将指示浏览器扩展具有哪些权限(存储、位置...)和在哪些网页中。

由于浏览器扩展可能具有如此特权,一个恶意的扩展或被入侵的扩展可能允许攻击者以不同方式窃取敏感信息并监视用户

查看这些设置如何工作以及它们如何在以下情况中被滥用:

{% content-ref url="browext-permissions-and-host_permissions.md" %} browext-permissions-and-host_permissions.md {% endcontent-ref %}

content_security_policy

内容安全策略也可以在manifest.json中声明。如果定义了一个,它可能会存在漏洞

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

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

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

{% content-ref url="../content-security-policy-csp-bypass/" %} content-security-policy-csp-bypass {% endcontent-ref %}

web_accessible_resources

为了使网页能够访问浏览器扩展的页面,例如一个.html页面,该页面需要在manifest.json的**web_accessible_resources**字段中进行声明。
例如:

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

这些页面可以通过以下URL访问

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

在公共扩展中,扩展ID是可访问的

尽管,如果使用manifest.json参数**use_dynamic_url,这个ID可以是动态的**。

允许访问这些页面使这些页面潜在易受 ClickJacking 攻击

{% content-ref url="browext-clickjacking.md" %} browext-clickjacking.md {% endcontent-ref %}

{% hint style="success" %} 只允许这些页面由扩展加载而不是由随机URL加载可以防止 ClickJacking 攻击。 {% endhint %}

externally_connectable

根据文档"externally_connectable"清单属性声明了哪些扩展和网页可以通过runtime.connectruntime.sendMessage连接到您的扩展

  • 如果在您的扩展清单中声明**externally_connectable键,或者声明为"ids": ["*"]**所有扩展都可以连接,但没有网页可以连接
  • 如果指定了特定的ID,例如"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]只有这些应用程序可以连接。
  • 如果指定了匹配项这些Web应用程序将能够连接
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • 如果指定为空:"externally_connectable": {},则没有应用程序或网页能够连接。

在这里指定的扩展和 URL 越少攻击面就越小

{% hint style="danger" %} 如果一个易受 XSS 或接管攻击的网页在 externally_connectable 中指定,攻击者将能够直接向后台脚本发送消息,完全绕过内容脚本及其 CSP。

因此,这是一个非常强大的绕过方式。 {% endhint %}

Web ↔︎ 内容脚本通信

内容脚本操作的环境和主机页面存在的环境是相互隔离的,确保隔离性。尽管存在这种隔离,两者都可以与页面的文档对象模型DOM进行交互,这是一个共享资源。为了使主机页面能够与内容脚本进行通信,或者间接地通过内容脚本与扩展进行通信,需要利用双方都可以访问的DOM作为通信渠道。

发送消息

{% code title="content-script.js" %}

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);
port.postMessage(event.data.text);
}
}, false);

{% endcode %}

{% code title="example.js" %}

document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);

{% endcode %}

安全的Post Message通信应该检查接收到的消息的真实性可以通过以下方式进行检查

  • event.isTrusted仅当事件由用户操作触发时为True
  • 内容脚本可能只在用户执行某些操作时期望收到消息
  • 来源域:可能只允许白名单域的消息
  • 如果使用正则表达式,请非常小心
  • 来源received_message.source !== window可用于检查消息是否来自内容脚本正在监听的同一窗口

即使执行了上述检查,仍可能存在漏洞,请在以下页面检查潜在的Post Message绕过

{% content-ref url="../postmessage-vulnerabilities/" %} postmessage-vulnerabilities {% endcontent-ref %}

Iframe

另一种可能的通信方式可能是通过Iframe URLs,您可以在以下示例中找到:

{% content-ref url="browext-xss-example.md" %} browext-xss-example.md {% endcontent-ref %}

DOM

这并不是“确切地”一种通信方式,但是web和内容脚本将可以访问web DOM。因此,如果内容脚本从中读取一些信息,信任web DOMweb可能会修改这些数据因为不应信任web或者因为web容易受到XSS攻击从而危及内容脚本

您还可以在以下示例中找到一个基于DOM的XSS示例来危及浏览器扩展

{% content-ref url="browext-xss-example.md" %} browext-xss-example.md {% endcontent-ref %}

内存/代码中的敏感信息

如果浏览器扩展在其内存中存储敏感信息,这些信息可能会被转储特别是在Windows机器上并且可以对这些信息进行搜索

因此,浏览器扩展的内存不应被视为安全敏感信息,如凭据或助记短语不应存储

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

内容脚本**↔︎** 后台脚本通信

内容脚本可以使用函数runtime.sendMessage() tabs.sendMessage()发送一次性可序列化为JSON的消息。

要处理响应,请使用返回的Promise。尽管出于向后兼容性考虑,仍然可以将回调函数作为最后一个参数传递。

内容脚本发送请求如下所示:

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

扩展程序(通常是后台脚本)发送请求。内容脚本可以使用这些函数,只是需要指定要发送到哪个标签页。以下是向所选标签页的内容脚本发送消息的示例:

// 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 事件监听器来处理消息。这在内容脚本或扩展页面中看起来是一样的。

// 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() 函数关联的回调将默认触发。

在浏览器中加载扩展

  1. 下载 浏览器扩展并解压缩
  2. 转到 chrome://extensions/启用 开发者模式
  3. 点击 加载已解压的扩展 按钮

Firefox 中,转到 about:debugging#/runtime/this-firefox 并点击 加载临时附加组件 按钮。

从商店获取源代码

Chrome 扩展的源代码可以通过各种方法获取。以下是每个选项的详细说明和说明。

通过命令行下载 ZIP 格式的扩展

可以使用命令行将 Chrome 扩展的源代码下载为 ZIP 文件。这涉及使用 curl 从特定 URL 获取 ZIP 文件,然后将 ZIP 文件的内容提取到一个目录中。以下是步骤:

  1. 用实际的扩展 ID 替换 "extension_id"
  2. 执行以下命令:
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扩展

另一种方便的方法是使用Chrome扩展源代码查看器这是一个开源项目。可以从Chrome Web商店安装。查看器的源代码可在其GitHub存储库中找到。

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

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

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

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

  • about:extensions页面上启用开发者模式以查看每个扩展的ID。
  • 在每个扩展的文件夹中,manifest.json文件包含一个可读的name字段,可帮助您识别扩展。

使用文件压缩工具或解包工具

前往Chrome Web商店并下载扩展。文件将具有.crx扩展名。 将文件扩展名从.crx更改为.zip。 使用任何文件压缩工具如WinRAR、7-Zip等来提取ZIP文件的内容。

在Chrome中使用开发者模式

打开Chrome并转到chrome://extensions/。 在右上角启用“开发者模式”。 单击“加载已解压的扩展...”。 导航到扩展的目录。 这不会下载源代码,但对于查看和修改已下载或开发的扩展的代码很有用。

安全审计检查清单

尽管浏览器扩展具有有限的攻击面,但其中一些可能包含漏洞潜在的加固改进。以下是最常见的:

  • 尽量限制所请求的**permissions**
  • 尽量限制所请求的**host_permissions**
  • 使用强大的 content_security_policy
  • 尽量限制externally_connectable,如果不需要并且可能的话,不要默认留下,指定**{}**
  • 如果此处提到了易受XSS攻击或接管的URL则攻击者将能够直接向后台脚本发送消息。非常强大的绕过方式。
  • 尽量限制web_accessible_resources,如果可能,甚至为空。
  • 如果**web_accessible_resources**不是空的,请检查ClickJacking
  • 如果从扩展网页有任何通信,请检查由通信引起的XSS漏洞
  • 如果使用Post Messages请检查Post Message漏洞
  • 如果内容脚本访问DOM详细信息,请检查它们是否在被网页修改时会引入XSS
  • 特别强调如果此通信还涉及内容脚本 -> 后台脚本通信
  • 不应将敏感信息存储在浏览器扩展代码
  • 不应将敏感信息存储在浏览器扩展内存

工具

Tarnish

  • 从提供的Chrome Web商店链接中提取任何Chrome扩展。
  • manifest.json 查看器简单显示扩展清单的JSON格式化版本。
  • 指纹分析:检测web_accessible_resources并自动生成Chrome扩展指纹识别JavaScript。
  • 潜在的Clickjacking分析:检测具有设置web_accessible_resources指令的扩展HTML页面。根据页面用途这些页面可能容易受到点击劫持攻击。
  • 权限警告查看器显示用户尝试安装扩展时将显示的所有Chrome权限提示警告列表。
  • 危险函数显示可能被攻击者利用的危险函数位置例如innerHTML、chrome.tabs.executeScript等函数
  • 入口点:显示扩展接受用户/外部输入的位置。这对于了解扩展的表面积并寻找潜在的发送恶意数据到扩展的点很有用。
  • 生成的警报包括以下内容:
  • 引发警报的相关代码片段和行。
  • 问题描述。
  • “查看文件”按钮,可查看包含代码的完整源文件。
  • 警报文件的路径。
  • 警报文件的完整Chrome扩展URI。
  • 文件类型,例如后台页面脚本、内容脚本、浏览器操作等。
  • 如果易受攻击的行在JavaScript文件中则包括所有包含此行的页面的路径以及这些页面的类型和web_accessible_resource状态。
  • 内容安全策略CSP分析器和绕过检查器这将指出扩展CSP中的弱点并且还将阐明由于白名单CDN等原因绕过CSP的潜在方法。
  • 已知的易受攻击的库:使用Retire.js检查已知易受攻击的JavaScript库的使用情况。
  • 下载扩展和格式化版本。
  • 下载原始扩展。
  • 下载扩展的美化版本自动美化的HTML和JavaScript
  • 自动缓存扫描结果,第一次运行扩展扫描将花费大量时间。但第二次,假设扩展未更新,由于结果被缓存,几乎会立即完成。
  • 可链接的报告URL轻松地将其他人链接到由Tarnish生成的扩展报告。

Neto

Neto项目是一个Python 3包旨在分析和揭示浏览器插件和扩展的隐藏功能适用于Firefox和Chrome等知名浏览器。它自动解压打包文件以从扩展中提取这些功能manifest.json、本地化文件夹或JavaScript和HTML源文件。

参考资料

从零开始学习AWS黑客技术使用 htARTEHackTricks AWS红队专家

支持HackTricks的其他方式