mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-26 06:30:37 +00:00
621 lines
32 KiB
Markdown
621 lines
32 KiB
Markdown
# Browser Extension Pentesting Methodology
|
||
|
||
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|
||
|
||
## Basic Information
|
||
|
||
Browser extensions are written in JavaScript and loaded by the browser in the background. It has its [DOM](https://www.w3schools.com/js/js\_htmldom.asp) but can interact with other sites' DOMs. This means that it may compromise other sites' confidentiality, integrity, and availability (CIA).
|
||
|
||
## Main Components
|
||
|
||
Extension layouts look best when visualised and consists of three components. Let’s look at each component in depth.
|
||
|
||
<figure><img src="../../.gitbook/assets/image.png" alt=""><figcaption><p><a href="http://webblaze.cs.berkeley.edu/papers/Extensions.pdf">http://webblaze.cs.berkeley.edu/papers/Extensions.pdf</a></p></figcaption></figure>
|
||
|
||
### **Content Scripts**
|
||
|
||
Each content script has direct access to the DOM of a **single web page** and is thereby exposed to **potentially malicious input**. However, the content script contains no permissions other than the ability to send messages to the extension core.
|
||
|
||
To view and debug content scripts in Chrome, you can open the Chrome developer tools menu from Options > More tools > Developer tools OR (Press - Ctrl + Shift + I).
|
||
|
||
With developer tools displayed, click the **Source tab**, then click the **Content Scripts** tab. Here you can see the running content scripts of the various extensions and set breakpoints to monitor the flow of execution. In our case, we have shown via the Wappalyzer browser extension. 
|
||
|
||
<figure><img src="../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
### **Extension Core**
|
||
|
||
The extension core contains most of the extension privileges/access, but the extension core can only interact with web content via [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and content scripts. Also, the extension core does not have direct access to the host machine.
|
||
|
||
### **Native Binary**
|
||
|
||
The extension allows a native binary that can **access the host machine with the user’s full privileges.** The native binary interacts with the extension core through the standard Netscape Plugin Application Programming Interface ([NPAPI](https://en.wikipedia.org/wiki/NPAPI)) used by Flash and other browser plug-ins.
|
||
|
||
### Boundaries
|
||
|
||
{% hint style="danger" %}
|
||
To obtain the user's full privileges, an attacker must convince the extension to pass malicious input from the content script to the extension's core and from the extension's core to the native binary.
|
||
{% endhint %}
|
||
|
||
Each component of the extension is separated from each other by **strong protective boundaries**. Each component runs in a **separate operating system process**. Content scripts and extension cores run in **sandbox processes** unavailable to most operating system services.  
|
||
|
||
Moreover, content scripts separate from their associated web pages by **running in a separate JavaScript heap**. The content script and web page have **access to the same underlying DOM**, but the two **never exchange JavaScript pointers**, preventing the leaking of JavaScript functionality.
|
||
|
||
## **`manifest.json`**
|
||
|
||
A Chrome extension is just a ZIP folder with a [.crx file extension](https://www.lifewire.com/crx-file-2620391). 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`
|
||
|
||
Content scripts are **loaded** whenever the user **navigates to a matching page**, in our case any page matching the **`https://example.com/*`** expression and not matching the **`*://*/*/business*`** regex. They execute **like the page’s own scripts** and have arbitrary access to the page’s [Document Object Model (DOM)](https://developer.mozilla.org/en-US/docs/Web/API/Document\_Object\_Model). 
|
||
|
||
```json
|
||
"content_scripts": [
|
||
{
|
||
"js": [
|
||
"script.js"
|
||
],
|
||
"matches": [
|
||
"https://example.com/*",
|
||
"https://www.example.com/*"
|
||
],
|
||
"exclude_matches": ["*://*/*business*"],
|
||
}
|
||
],
|
||
```
|
||
|
||
In order to include or exclude more URLs it's also possible to use **`include_globs`** and **`exclude_globs`**.
|
||
|
||
This is an example content script which will add an explain button to the page when [the storage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) to retrieve the `message` value from extension’s storage.
|
||
|
||
```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);
|
||
});
|
||
```
|
||
|
||
<figure><img src="../../.gitbook/assets/image (7).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
When this button is clicked the content script **uses** [**runtime.sendMessage() API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage) **to send a message to the extension pages**. That’s because a content script only has direct access to a handful of APIs such as `storage`. Everything else has to be done by extension pages that content scripts can send messages to.
|
||
|
||
{% hint style="warning" %}
|
||
The **content script capabilities** differ slightly depending on browser. For Chromium-based browsers you can find the list in the [Chrome Developers documentation](https://developer.chrome.com/docs/extensions/mv3/content\_scripts/#capabilities), for Firefox [MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content\_scripts#webextension\_apis) is the ultimate source.\
|
||
Remember that the Content Script can also **communicate with the background scripts** so they perform actions and send back the response
|
||
{% endhint %}
|
||
|
||
### Injected content scripts
|
||
|
||
{% hint style="success" %}
|
||
Note that **Content Scripts aren't mandatory** as it's also possible to **dynamically** **inject** scripts and to **programatically inject them** in web pages via **`tabs.executeScript`**. This actually provides more **granular controls**.
|
||
{% endhint %}
|
||
|
||
To inject a content script programmatically, your extension needs [host permissions](https://developer.chrome.com/docs/extensions/reference/permissions) for the page it's trying to inject scripts into. Host permissions can either be granted by **requesting them** as part of your extension's manifest or temporarily via [**activeTab**](https://developer.chrome.com/docs/extensions/reference/manifest/activeTab)**.**
|
||
|
||
#### Example activeTab-based extension
|
||
|
||
{% code title="manifest.json" %}
|
||
```json
|
||
{
|
||
"name": "My extension",
|
||
...
|
||
"permissions": [
|
||
"activeTab",
|
||
"scripting"
|
||
],
|
||
"background": {
|
||
"service_worker": "background.js"
|
||
},
|
||
"action": {
|
||
"default_title": "Action Button"
|
||
}
|
||
}
|
||
```
|
||
{% endcode %}
|
||
|
||
* **Inject a JS file on click:**
|
||
|
||
```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"]
|
||
});
|
||
});
|
||
```
|
||
|
||
* **Inject a function** on click:
|
||
|
||
```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,
|
||
});
|
||
});
|
||
```
|
||
|
||
#### Example with scripting permissions
|
||
|
||
```javascript
|
||
// service-workser.js
|
||
chrome.scripting.registerContentScripts([{
|
||
id : "test",
|
||
matches : [ "https://*.nytimes.com/*" ],
|
||
excludeMatches : [ "*://*/*business*" ],
|
||
js : [ "contentScript.js" ],
|
||
}]);
|
||
|
||
// ANother example
|
||
chrome.tabs.executeScript(tabId, { file: "content_script.js" });
|
||
```
|
||
|
||
In order to include or exclude more URLs it's also possible to use **`include_globs`** and **`exclude_globs`**.
|
||
|
||
### Content Scripts `run_at`
|
||
|
||
The `run_at` field controls **when JavaScript files are injected into the web page**. The preferred and default value is `"document_idle"`. 
|
||
|
||
The possible values are:
|
||
|
||
* **`document_idle`**: Whenever possible
|
||
* **`document_start`**: After any files from `css`, but before any other DOM is constructed or any other script is run.
|
||
* **`document_end`**: Immediately after the DOM is complete, but before subresources like images and frames have loaded.
|
||
|
||
#### Via `manifest.json`
|
||
|
||
```json
|
||
{
|
||
"name": "My extension",
|
||
...
|
||
"content_scripts": [
|
||
{
|
||
"matches": ["https://*.nytimes.com/*"],
|
||
"run_at": "document_idle",
|
||
"js": ["contentScript.js"]
|
||
}
|
||
],
|
||
...
|
||
}
|
||
|
||
```
|
||
|
||
Via **`service-worker.js`**
|
||
|
||
```javascript
|
||
chrome.scripting.registerContentScripts([{
|
||
id : "test",
|
||
matches : [ "https://*.nytimes.com/*" ],
|
||
runAt : "document_idle",
|
||
js : [ "contentScript.js" ],
|
||
}]);
|
||
```
|
||
|
||
### `background`
|
||
|
||
When content scripts send a message its destination is the **background page**. The background page is a special page that is **always present** unless specified otherwise in the extension manifest. It is invisible to the user, despite being a regular page with its own DOM and everything. Its function is typically coordinating all other parts of the extension.
|
||
|
||
{% hint style="success" %}
|
||
If a background page isn’t declared explicitly, the browser will helpfully **generate one** automatically and make sure all the **declared background scripts are loaded** into it, like in the previous manifest.json example.
|
||
{% endhint %}
|
||
|
||
Example background script:
|
||
|
||
```js
|
||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) =>
|
||
{
|
||
if (request == "explain")
|
||
{
|
||
chrome.tabs.create({ url: "https://example.net/explanation" });
|
||
}
|
||
})
|
||
```
|
||
|
||
It uses [runtime.onMessage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage) to listen to messages. When an `"explain"` message is received, it uses [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) to open a page in a new tab. 
|
||
|
||
### Options pages and other
|
||
|
||
Browser extensions can contain various kinds of pages: 
|
||
|
||
* **Action pages** are displayed in a **drop-down when the extension ico**n is clicked.
|
||
* Pages that the extension will **load in a new tab**. 
|
||
* **Option Pages**: This page displays on top of the extension when clicked. In the previous manifest In my case I was able to access this page in `chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca` or clicking:
|
||
|
||
<figure><img src="../../.gitbook/assets/image (8).png" alt="" width="375"><figcaption></figcaption></figure>
|
||
|
||
|
||
|
||
Unlike the background page, these pages aren’t persistent but rather load when needed. Yet all of them can **receive messages from content scripts**. And all of them have **full access to extension-specific APIs**, as far as the extension’s permissions allow.
|
||
|
||
Altogether the relevant contexts for browser extensions look like this:
|
||
|
||
<figure><img src="../../.gitbook/assets/image (9).png" alt="" width="563"><figcaption></figcaption></figure>
|
||
|
||
### `permissions` & `host_permissions`
|
||
|
||
**`permissions`** and **`host_permissions`** are entries from the `manifest.json` that will indicate **which permissions** the browser extensions has (storage, location...) and in **which web pages**.
|
||
|
||
As browser extensions can be so **privileged**, a malicious one or one being compromised could allow the attacker **different means to steal sensitive information and spy on the user**.
|
||
|
||
Check how these settings work and how thye could get abused in:
|
||
|
||
{% content-ref url="browext-permissions-and-host_permissions.md" %}
|
||
[browext-permissions-and-host\_permissions.md](browext-permissions-and-host\_permissions.md)
|
||
{% endcontent-ref %}
|
||
|
||
### `content_security_policy`
|
||
|
||
A c**ontent security policy** can be declared also inside the `manifest.json`. If there is one defined, it could be **vulnerable**.
|
||
|
||
The default setting for browser extension pages is rather restrictive:
|
||
|
||
```bash
|
||
script-src 'self'; object-src 'self';
|
||
```
|
||
|
||
For more info about CSP and potential bypasses check:
|
||
|
||
{% content-ref url="../content-security-policy-csp-bypass/" %}
|
||
[content-security-policy-csp-bypass](../content-security-policy-csp-bypass/)
|
||
{% endcontent-ref %}
|
||
|
||
### `web_accessible_resources`
|
||
|
||
in order for a webpage to access a page of a Browser Extension, a `.html` page for example, this page needs to be mentioned in the **`web_accessible_resources`** field of the `manifest.json`.\
|
||
For example:
|
||
|
||
```javascript
|
||
{
|
||
...
|
||
"web_accessible_resources": [
|
||
{
|
||
"resources": [ "images/*.png" ],
|
||
"matches": [ "https://example.com/*" ]
|
||
},
|
||
{
|
||
"resources": [ "fonts/*.woff" ],
|
||
"matches": [ "https://example.com/*" ]
|
||
}
|
||
],
|
||
...
|
||
}
|
||
```
|
||
|
||
These pages are accesible in URL like:
|
||
|
||
```
|
||
chrome-extension://<extension-id>/message.html
|
||
```
|
||
|
||
In public extensions the **extension-id is accesible**:
|
||
|
||
<figure><img src="../../.gitbook/assets/image (722).png" alt="" width="375"><figcaption></figcaption></figure>
|
||
|
||
Although, if the `manifest.json` parameter **`use_dynamic_url`** is used, this **id can be dynamic**.
|
||
|
||
Being allowed to access these pages make these pages **potentially vulnerable ClickJacking**:
|
||
|
||
{% content-ref url="browext-clickjacking.md" %}
|
||
[browext-clickjacking.md](browext-clickjacking.md)
|
||
{% endcontent-ref %}
|
||
|
||
{% hint style="success" %}
|
||
Allowing these pages to be loaded only by the extension and not by random URLs could prevent CLickJacking attacks.
|
||
{% endhint %}
|
||
|
||
### `externally_connectable`
|
||
|
||
A per the [**docs**](https://developer.chrome.com/docs/extensions/reference/manifest/externally-connectable), The `"externally_connectable"` manifest property declares **which extensions and web pages can connect** to your extension via [runtime.connect](https://developer.chrome.com/docs/extensions/reference/runtime#method-connect) and [runtime.sendMessage](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage).
|
||
|
||
* If the **`externally_connectable`** key is **not** declared in your extension's manifest or it's declared as **`"ids": ["*"]`**, **all extensions can connect, but no web pages can connect**.
|
||
* If **specific IDs are specified**, like in `"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]`, **only those applications** can connect.
|
||
* If **matches** are specified, those web apps will be able to connect:
|
||
|
||
```json
|
||
"matches": [
|
||
"https://*.google.com/*",
|
||
"*://*.chromium.org/*",
|
||
```
|
||
|
||
* If it's specified as empty: **`"externally_connectable": {}`**, no app or web will be able to connect.
|
||
|
||
The **less extensions and URLs** indicated here, the **smaller the attack surface** will be.
|
||
|
||
{% hint style="danger" %}
|
||
If a web page **vulnerable to XSS or takeover** is indicated in **`externally_connectable`**, an attacker will be able to **send messages directly to the background script**, completely bypassing the Content Script and its CSP.
|
||
|
||
Therefore, this is a **very powerful bypass**.
|
||
{% endhint %}
|
||
|
||
## Web **↔︎** Content Script Communication
|
||
|
||
Although the execution environments of **content scripts and the pages** that host them are **isolated** from each other, they **share access to the page's DOM**. If the page wishes to communicate with the content script, or with the extension via the content script, it must do so through the **shared DOM**.
|
||
|
||
### Post Messages
|
||
|
||
{% code title="content-script.js" %}
|
||
```javascript
|
||
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" %}
|
||
```javascript
|
||
document.getElementById("theButton").addEventListener("click", () => {
|
||
window.postMessage(
|
||
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
|
||
}, false);
|
||
```
|
||
{% endcode %}
|
||
|
||
A secure Post Message communication should check the authenticity of the received message, this can be done checking:
|
||
|
||
* **`event.isTrusted`**: This is True only if the event was triggered by a users action
|
||
* The content script might expecting a message only if the user performs some action
|
||
* **origin domain**: It can be checked against an allowlist of domains.
|
||
* If a regex is used, be very careful
|
||
* **Source**: `received_message.source !== window` can be used to check if the message was **from the same window** where the Content Script is listening.
|
||
|
||
The previous checks, even if performed, could be vulnerable, so check in the following page **potential Post Message bypasses**:
|
||
|
||
{% content-ref url="../postmessage-vulnerabilities/" %}
|
||
[postmessage-vulnerabilities](../postmessage-vulnerabilities/)
|
||
{% endcontent-ref %}
|
||
|
||
### Iframe
|
||
|
||
Another possible way of communication might be through **Iframe URLs**, you can find an example in:
|
||
|
||
{% content-ref url="browext-xss-example.md" %}
|
||
[browext-xss-example.md](browext-xss-example.md)
|
||
{% endcontent-ref %}
|
||
|
||
### 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 dat**a (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:
|
||
|
||
{% content-ref url="browext-xss-example.md" %}
|
||
[browext-xss-example.md](browext-xss-example.md)
|
||
{% endcontent-ref %}
|
||
|
||
## Sensitive Information in Memory/Code
|
||
|
||
If a Browser Extension stores **sensitive information inside it's memory**, this could be **dumped** (specially in Windows machines) and **searched** for this information.
|
||
|
||
Therefore, the memory of the Browser Extension **shouldn't be considered secure** and **sensitive information** such as credentials or mnemonic phrases **shouldn't be stored**.
|
||
|
||
Of course, do **not put sensitive information in the code**, as it will be **public**.
|
||
|
||
## Content Script **↔︎** Background Script Communication
|
||
|
||
A Content Script can use the functions [**runtime.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage) **or** [**tabs.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/tabs#method-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);
|
||
})();
|
||
```
|
||
|
||
Sending a request from the **extension** (usually a **background script**) to a content script is similar, except that you need to specify which tab to send it to. This example demonstrates sending a message to the content script in the selected tab.
|
||
|
||
```javascript
|
||
(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);
|
||
})();
|
||
```
|
||
|
||
On the **receiving end**, you need to set up an [**runtime.onMessage**](https://developer.chrome.com/docs/extensions/reference/runtime#event-onMessage) **event listener** to handle the message. This looks the same from a content script or extension page.
|
||
|
||
```javascript
|
||
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"});
|
||
}
|
||
);
|
||
```
|
||
|
||
In the above example, **`sendResponse()`** was called synchronously. If you want to **asynchronously** use `sendResponse()`, add `return true;` to the `onMessage` event handler.
|
||
|
||
> If multiple pages are listening for `onMessage` events, **only the first to call `sendResponse()`** for a particular event will succeed in sending the response. All other responses to that event will be ignored.
|
||
|
||
For new extensions you should prefer promises over callbacks. If you're using callbacks, the `sendResponse()` callback is only valid if used synchronously, or if the event handler returns `true` to indicate that it will respond asynchronously. The `sendMessage()` function's callback will be invoked automatically if no handlers return true or if the `sendResponse()` callback is garbage-collected.
|
||
|
||
## Loading an Extension in the Browser
|
||
|
||
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.
|
||
|
||
## Getting the source code from the store
|
||
|
||
From [**here**](https://gist.github.com/paulirish/78d6c1406c901be02c2d):
|
||
|
||
### Option 1: Command-line download extension as zip and extract
|
||
|
||
{% code overflow="wrap" %}
|
||
```bash
|
||
extension_id=jifpbeccnghkjeaalbbjmodiffmgedin # change this 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"
|
||
```
|
||
{% endcode %}
|
||
|
||
Thx to crxviewer for the [magic download URL](https://github.com/Rob--W/crxviewer/blob/6113c25e3569e1ec59365ad9a177aa97e2bcda61/src/cws\_pattern.js#L27-L74).
|
||
|
||
### Option 2: Use the CRX Viewer website
|
||
|
||
[https://robwu.nl/crxviewer/](https://robwu.nl/crxviewer/)
|
||
|
||
### Option 3: Use the CRX Viewer extension
|
||
|
||
The [Chrome extension source viewer](https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin?hl=en) is open source ([github repo](https://github.com/Rob--W/crxviewer)) and makes this super easy.
|
||
|
||
### Option 3: View source of locally installed extension
|
||
|
||
1. Find your Chrome local profile directory. Open `chrome://version/` and find the "Profile Path:\` field. Open that folder up.
|
||
2. Open the `Extensions/` subfolder
|
||
3. All your extensions are here, with typically readable source.
|
||
|
||
#### Mapping between locally installed extension IDs and names
|
||
|
||
* On `about:extensions`, turn on Developer Mode and you'll see IDs under each entry
|
||
* Inside the `Extensions/` folders, the manifest.json has a readable `name` field
|
||
|
||
## Security Audit Checklist
|
||
|
||
Even though Browser Extensions have a **limited attack surface**, some of them might contain **vulnerabilities** or **potential hardening improvements**. The following ones are the most common ones:
|
||
|
||
* [ ] **Limit** as much as possible requested **`permissions`**
|
||
* [ ] **Limit** as much as possible **`host_permissions`**
|
||
* [ ] Use a **strong** **`content_security_policy`**
|
||
* [ ] **Limit** as much as possible the **`externally_connectable`**, if none is needed and possible, do not leave it by default, specify **`{}`**
|
||
* [ ] If **URL vulnerable to XSS or to takeover** is mentioned here, an attacker will be able to **send messages to the background scripts directly**. Very powerful bypass.
|
||
* [ ] **Limit** as much as possible the **`web_accessible_resources`**, even empty if possible.
|
||
* [ ] If **`web_accessible_resources`** is not none, check for [**ClickJacking**](browext-clickjacking.md)
|
||
* [ ] If any **communication** occurs from the **extension** to the **web page**, [**check for XSS**](browext-xss-example.md) **vulnerabilities** caused in the communication.
|
||
* [ ] If Post Messages are used, check for [**Post Message vulnerabilities**](../postmessage-vulnerabilities/)**.**
|
||
* [ ] If the **Content Script access DOM details**, check that they **aren't introducing a XSS** if they get **modified** by the web
|
||
* [ ] Make a special emphasis if this communication is also involved in the **Content Script -> Background script communication**
|
||
* [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **code**
|
||
* [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **memory**
|
||
|
||
## Tools
|
||
|
||
### [**Tarnish**](https://thehackerblog.com/tarnish/)
|
||
|
||
* Pulls any Chrome extension from a provided Chrome webstore link.
|
||
* [**manifest.json**](https://developer.chrome.com/extensions/manifest) **viewer**: simply displays a JSON-prettified version of the extension’s manifest.
|
||
* **Fingerprint Analysis**: Detection of [web\_accessible\_resources](https://developer.chrome.com/extensions/manifest/web\_accessible\_resources) and automatic generation of Chrome extension fingerprinting JavaScript.
|
||
* **Potential Clickjacking Analysis**: Detection of extension HTML pages with the [web\_accessible\_resources](https://developer.chrome.com/extensions/manifest/web\_accessible\_resources) directive set. These are potentially vulnerable to clickjacking depending on the purpose of the pages.
|
||
* **Permission Warning(s) viewer**: which shows a list of all the Chrome permission prompt warnings which will be displayed upon a user attempting to install the extension.
|
||
* **Dangerous Function(s)**: shows the location of dangerous functions which could potentially be exploited by an attacker (e.g. functions such as innerHTML, chrome.tabs.executeScript).
|
||
* **Entry Point(s)**: shows where the extension takes in user/external input. This is useful for understanding an extension’s surface area and looking for potential points to send maliciously-crafted data to the extension.
|
||
* Both the Dangerous Function(s) and Entry Point(s) scanners have the following for their generated alerts:
|
||
* Relevant code snippet and line that caused the alert.
|
||
* Description of the issue.
|
||
* A “View File” button to view the full source file containing the code.
|
||
* The path of the alerted file.
|
||
* The full Chrome extension URI of the alerted file.
|
||
* The type of file it is, such as a Background Page script, Content Script, Browser Action, etc.
|
||
* If the vulnerable line is in a JavaScript file, the paths of all of the pages where it is included as well as these page’s type, and [web\_accessible\_resource](https://developer.chrome.com/extensions/manifest/web\_accessible\_resources) status.
|
||
* **Content Security Policy (CSP) analyzer and bypass checker**: This will point out weaknesses in your extension’s CSP and will also illuminate any potential ways to bypass your CSP due to whitelisted CDNs, etc.
|
||
* **Known Vulnerable Libraries**: This uses [Retire.js](https://retirejs.github.io/retire.js/) to check for any usage of known-vulnerable JavaScript libraries.
|
||
* Download extension and formatted versions.
|
||
* Download the original extension.
|
||
* Download a beautified version of the extension (auto prettified HTML and JavaScript).
|
||
* Automatic caching of scan results, running an extension scan will take a good amount of time the first time you run it. However the second time, assuming the extension hasn’t been updated, will be almost instant due to the results being cached.
|
||
* Linkable Report URLs, easily link someone else to an extension report generated by tarnish.
|
||
|
||
### [Neto](https://github.com/elevenpaths/neto)
|
||
|
||
Project Neto is a Python 3 package conceived to analyse and unravel hidden features of browser plugins and extensions for well-known browsers such as Firefox and Chrome. It automates the process of unzipping the packaged files to extract these features from relevant resources in a extension like `manifest.json`, localization folders or Javascript and HTML source files.
|
||
|
||
## References
|
||
|
||
* **Thanks to** [**@naivenom**](https://twitter.com/naivenom) **for the help with this methodology**
|
||
* [https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing](https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing)
|
||
* [https://palant.info/2022/08/10/anatomy-of-a-basic-extension/](https://palant.info/2022/08/10/anatomy-of-a-basic-extension/)
|
||
* [https://palant.info/2022/08/24/attack-surface-of-extension-pages/](https://palant.info/2022/08/24/attack-surface-of-extension-pages/)
|
||
* [https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/](https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/)
|
||
* [https://help.passbolt.com/assets/files/PBL-02-report.pdf](https://help.passbolt.com/assets/files/PBL-02-report.pdf)
|
||
* [https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts)
|
||
* [https://developer.chrome.com/docs/extensions/mv2/background-pages](https://developer.chrome.com/docs/extensions/mv2/background-pages)
|
||
* [https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/](https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/)
|
||
|
||
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
||
</details>
|