ブラウザ拡張機能 Pentesting Methodology
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
基本情報
Browser extensions are written in JavaScript and loaded by the browser in the background. It has its DOM but can interact with other sites’ DOMs. This means that it may compromise other sites’ confidentiality, integrity, and availability (CIA).
主なコンポーネント
Extension layouts look best when visualised and consists of three components. Let’s look at each component in depth.
 (1) (1).png)
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.
Extension Core
The extension core contains most of the extension privileges/access, but the extension core can only interact with web content via 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) used by Flash and other browser plug-ins.
境界
Caution
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.
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. The extension’s core is the manifest.json file at the root of the folder, which specifies layout, permissions, and other configuration options.
Example:
{
"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は、ユーザーが一致するページに移動するたびに読み込まれます。この場合は https://example.com/* の表現に一致し、*://*/*/business* の正規表現には一致しないすべてのページが対象です。これらはページ自身のスクリプトのように実行され、ページの Document Object Model (DOM) に任意にアクセスできます。
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],
より多くのURLを含めたり除外したりするには、include_globs と exclude_globs を使用することもできます。
以下は、拡張機能のストレージから message 値を取得するために the storage API を使用する際に、ページに explain ボタンを追加するサンプルのコンテンツスクリプトです。
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)
})
.png)
このボタンがクリックされると、コンテンツスクリプトはruntime.sendMessage() APIを利用して拡張ページにメッセージを送信します。これはコンテンツスクリプトが直接利用できる API が限られており、storage が数少ない例外の一つであるためです。これらの例外を超える機能については、メッセージを拡張ページに送信し、コンテンツスクリプトがそれと通信します。
Warning
ブラウザによってはコンテンツスクリプトの機能が若干異なる場合があります。Chromium ベースのブラウザでは機能一覧が Chrome Developers documentation にあり、Firefox については主に MDN が参照になります。
また、コンテンツスクリプトは background scripts と通信してアクションを実行し、応答を返すことができる点も重要です。
Chrome でコンテンツスクリプトを表示・デバッグするには、Chrome developer tools メニューを Options > More tools > Developer tools から開くか、Ctrl + Shift + I を押します。
Developer tools が表示されたら、Source tab をクリックし、続けて Content Scripts タブを選択します。これにより、各種拡張機能で動作しているコンテンツスクリプトを確認し、ブレークポイントを設定して実行フローを追跡できます。
注入されたコンテンツスクリプト
Tip
補足: Content Scripts は必須ではありません。
tabs.executeScriptを使ってスクリプトをウェブページに動的に、プログラム的に注入することも可能です。これによりより細かな制御が可能になります。
コンテンツスクリプトをプログラム的に注入する場合、拡張機能はスクリプトを注入するページに対する host permissions を持っている必要があります。これらの権限は、拡張の manifest 内で要求するか、または一時的に activeTab を通じて取得できます。
Example activeTab-based extension
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
- クリックで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_globs と exclude_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"],
},
])
background
コンテンツスクリプトから送信されたメッセージはbackground pageによって受信され、拡張機能の各コンポーネントを調整する中心的な役割を果たします。特に、background pageは拡張機能のライフタイムを通じて持続し、ユーザーとの直接的なやり取りなしに静かに動作します。独自のドキュメントオブジェクトモデル(DOM)を持ち、複雑な相互作用や状態管理を可能にします。
Key Points:
- Background Page Role: 拡張機能の神経中枢として機能し、拡張機能のさまざまな部分間の通信と調整を確実にします。
- Persistence: ユーザーには見えないが拡張機能の機能に不可欠な常時存在するエンティティです。
- Automatic Generation: 明示的に定義されていない場合、ブラウザは自動的にbackground pageを作成します。自動生成されたページには拡張機能のmanifestで指定されたすべての background scripts が含まれ、拡張機能のバックグラウンドタスクがシームレスに動作することを保証します。
Tip
ブラウザがバックグラウンドページを自動生成する(明示的に宣言されていない場合)ことにより、必要なすべての background scripts が統合され稼働するため、拡張機能のセットアップが簡素化されます。
Example background script:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "explain") {
chrome.tabs.create({ url: "https://example.net/explanation" })
}
})
It uses runtime.onMessage API to listen to messages. When an "explain" message is received, it uses tabs API to open a page in a new tab.
バックグラウンドスクリプトをデバッグするには、拡張機能の詳細からサービスワーカーを検査すると、バックグラウンドスクリプトが開かれた開発者ツールが起動します:
Options pages and other
ブラウザ拡張機能には様々な種類のページが含まれます:
- Action ページ は ドロップダウン(拡張機能のアイコンがクリックされたときに表示されます。
- 拡張機能が 新しいタブで読み込む ページ。
- Option Pages: このページはクリックすると拡張機能の上に表示されます。前の manifest では、私の場合このページに
chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjcaでアクセスできました、または次をクリックして:
.png)
これらのページは、必要に応じて動的にコンテンツを読み込むため、バックグラウンドページのように常駐するわけではない点に注意してください。それにもかかわらず、バックグラウンドページといくつかの能力を共有します:
- Communication with Content Scripts: バックグラウンドページと同様に、これらのページは Content Scripts からメッセージを受け取ることができ、拡張機能内での相互作用を促進します。
- Access to Extension-Specific APIs: これらのページは、拡張機能に定義された permissions に従って、拡張機能固有の API に幅広くアクセスできます。
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.
ブラウザ拡張は非常に 特権的 であるため、悪意のある拡張機能や侵害された拡張機能は攻撃者に機密情報を窃取したりユーザーを監視したりするための様々な手段を与える可能性があります。
Check how these settings work and how they could get abused in:
BrowExt - permissions & host_permissions
content_security_policy
A content 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:
script-src 'self'; object-src 'self';
CSP と potential bypasses の詳細については次を参照してください:
Content Security Policy (CSP) Bypass
web_accessible_resources
Webページが Browser Extension のページ(例えば .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
In public extensions the extension-id is accesible:
.png)
Although, if the manifest.json parameter use_dynamic_url is used, this id can be dynamic.
Tip
Note that even if a page is mentioned here, it might be protected against ClickJacking thanks to the Content Security Policy. So you also need to check it (frame-ancestors section) before confirming a ClickJacking attack is possible.
Being allowed to access these pages make these pages potentially vulnerable ClickJacking:
Tip
Allowing these pages to be loaded only by the extension and not by random URLs could prevent ClickJacking attacks.
Caution
Note that the pages from
web_accessible_resourcesand other pages of the extension are also capable of contacting background scripts. So if one of these pages is vulnerable to XSS it could open a bigger vulnerability.Moreover, note that you can only open pages indicated in
web_accessible_resourcesinside iframes, but from a new tab it’s possible to access any page in the extension knowing the extension ID. Therefore, if an XSS is found abusing same parameters, it could be abused even if the page isn’t configured inweb_accessible_resources.
externally_connectable
A per the docs, The "externally_connectable" manifest property declares which extensions and web pages can connect to your extension via runtime.connect and runtime.sendMessage.
- If the
externally_connectablekey 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:
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
- もし空として指定されている場合:
"externally_connectable": {}, いかなるアプリやwebページも接続できません。
ここで指定される拡張機能やURLが少ないほど、attack surfaceは小さくなります。
Caution
もし**
externally_connectableにvulnerable to XSS or takeoverなwebページが指定されていると、攻撃者はbackground scriptに直接メッセージを送信**でき、Content ScriptおよびそのCSPを完全にバイパスします。したがって、これは非常に強力なバイパスです。
さらに、クライアントがrogue extensionをインストールした場合、たとえそれがvulnerable extensionと通信する許可を持っていなくても、許可されたwebページにXSS dataを注入したり、
WebRequestやDeclarativeNetRequestAPIを悪用してターゲットドメイン上のリクエストを操作し、ページのJavaScript fileへのリクエストを変更する可能性があります。(ターゲットページのCSPがこれらの攻撃を防ぐ場合がある点に注意してください)。このアイデアはfrom this writeupに由来します。
Communication summary
Extension <–> WebApp
content scriptとwebページ間の通信には通常post messagesが使われます。したがって、webアプリケーション側では通常**window.postMessageへの呼び出しが見つかり、content script側ではwindow.addEventListener**のようなリスナーがあります。ただし、extensionがweb applicationに対してPost Messageを送信して通信することも(そのためweb側はそれを想定しておくべき)あるし、単にwebに新しいスクリプトを読み込ませることもあります。
Inside the extension
通常、拡張機能内でメッセージを送信するために**chrome.runtime.sendMessageが使用されます(通常はbackground scriptで処理されます)。それを受信して処理するためには、chrome.runtime.onMessage.addListener**を呼び出してリスナーを宣言します。
単一メッセージを送る代わりに永続的な接続を持つために**chrome.runtime.connect()を使用することも可能で、以下の例のようにmessagesをsendおよびreceive**するために使用できます:
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 })
</details>
バックグラウンドスクリプトから、特定のタブにあるコンテンツスクリプトに対して**`chrome.tabs.sendMessage`**を呼び出してメッセージを送ることも可能で、その際はメッセージを送る**タブのID**を指定する必要があります。
### 許可された `externally_connectable` から拡張機能へ
`externally_connectable` 設定で許可された **Web アプリおよび外部ブラウザ拡張機能** は、次を使用してリクエストを送信できます :
```javascript
chrome.runtime.sendMessage(extensionId, ...
必要に応じてextension IDを記載してください。
Native Messaging
バックグラウンドスクリプトはシステム内のバイナリと通信することが可能であり、この通信が適切に保護されていないとRCEsのような重大な脆弱性を招きやすい。 More on this later.
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
Web ↔︎ Content Script Communication
content scripts が動作する環境とホストページが存在する環境は互いに分離されており、隔離が確保されています。この隔離にもかかわらず、両者は共有リソースであるページの Document Object Model (DOM) にアクセスして操作することが可能です。ホストページが content script と通信する、または content script を介して extension と間接的にやり取りするためには、両者がアクセスできる通信チャネルとして DOM を利用する必要があります。
Post Messages
// 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
)
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 になります- Content Script はユーザーの操作があった場合にのみメッセージを受け取るようにするべきです
- origin domain: メッセージを受け入れるドメインを allowlist のみに制限するべきです
- 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:
Iframe
Another possible way of communication might be through Iframe URLs, you can find an example in:
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:
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:
;(async () => {
const response = await chrome.runtime.sendMessage({ greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()
extension(通常はbackground script)からリクエストを送信する。選択したタブのcontent scriptにメッセージを送る例:
// 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のevent listenerを設定する必要があります。これは content script または extension page からでも同じように見えます。
// 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() を実行したページ のみが有効にレスポンスを返すことができます。同じイベントに対するそれ以降のレスポンスは考慮されません。
新しい拡張機能を作成する際は、callbacks よりも promises を優先するべきです。callbacks を使う場合、sendResponse() は同期コンテキスト内で直接実行されるか、イベントハンドラが return true; で非同期処理を示している場合にのみ有効と見なされます。もしどのハンドラも return true; を返さないか、sendResponse() がメモリから解放(garbage-collected)されると、sendMessage() に紐づく callback がデフォルトで呼ばれます。
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 は、browser extension の background scripts からアプリケーションと通信するために runtime.connectNative() または runtime.sendNativeMessage() に渡される文字列です。path はバイナリへのパスで、type は有効な値が 1 つだけで stdio(stdin と stdout を使用)です。allowed_origins はアクセスできる拡張機能を示し(ワイルドカードは使用できません)。
Chrome/Chromium はこの json を Windows のレジストリや macOS と Linux のいくつかのパスで検索します(詳細は docs を参照)。
Tip
ブラウザ拡張は、この通信を使用できるように
nativeMessaingpermission を宣言する必要もあります。
以下は、バックグラウンドスクリプトがネイティブアプリケーションにメッセージを送信するコードの例です:
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:
- ブラウザ拡張機能がcontent scriptに対してワイルドカードパターンを持つ。
- Content scriptは
sendMessageを使ってpostMessageメッセージをbackground scriptに渡す。 - Background scriptは
sendNativeMessageを使ってメッセージをネイティブアプリケーションに渡す。 - ネイティブアプリケーションがメッセージを危険に処理し、コード実行に至る。
そしてその中で、任意のページからブラウザ拡張機能を悪用してRCEに至る例が説明されています。
メモリ/コード/クリップボード内の機密情報
ブラウザ拡張機能がメモリ内に機密情報を保存する場合、特にWindowsマシンではこれがダンプされ、その情報を検索される可能性があります。
したがって、ブラウザ拡張機能のメモリは安全とは見なされるべきではなく、資格情報やニーモニックフレーズなどの機密情報は保存されるべきではありません。
もちろん、コードに機密情報を入れないでください。コードは公開される可能性があります。
ブラウザからメモリをダンプするには、プロセスメモリをダンプするか、ブラウザ拡張機能の設定に移動して**Inspect pop-up**をクリックし -> **Memoryセクションで -> Take a snaphost を実行し、CTRL+F**でスナップショット内を検索して機密情報を探します。
さらに、ニーモニックキーやパスワードのような高度に機密性の高い情報はクリップボードにコピーできないようにすべき(少なくとも数秒後にクリップボードから削除する)べきです。なぜならクリップボードを監視するプロセスがそれらを取得できるからです。
ブラウザで拡張機能を読み込む方法
- ブラウザ拡張機能をダウンロードして解凍する
chrome://extensions/に移動し、Developer Modeを有効化する- **
Load unpacked**ボタンをクリックする
Firefoxでは**about:debugging#/runtime/this-firefoxにアクセスし、Load Temporary Add-on**ボタンをクリックします。
ストアからソースコードを取得する
Chrome拡張機能のソースコードは様々な方法で取得できます。以下に各オプションの詳細な説明と手順を示します。
コマンドラインで拡張機能をZIPとしてダウンロードする
Chrome拡張機能のソースコードはコマンドラインを使ってZIPファイルとしてダウンロードできます。これは、特定のURLからZIPファイルを取得するためにcurlを使用し、そのZIPファイルの内容をディレクトリに展開することを伴います。手順は次のとおりです:
"extension_id"を実際の拡張機能のIDに置き換える。- 以下のコマンドを実行する:
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"
Use the CRX Viewer website
Use the CRX Viewer extension
もうひとつ便利な方法は、Chrome Extension Source Viewer を使用することです。これはオープンソースプロジェクトで、Chrome Web Store からインストールできます。ビューアのソースコードはその GitHub repository で入手できます。
View source of locally installed extension
ローカルにインストールされた Chrome extension も調査できます。手順は次のとおりです:
chrome://version/にアクセスして、“Profile Path” フィールドを確認し、Chrome のローカルプロファイルディレクトリにアクセスします。- プロファイルディレクトリ内の
Extensions/サブフォルダに移動します。 - このフォルダにはインストール済みの拡張機能がすべて含まれており、通常ソースコードが読みやすい形式で保存されています。
拡張機能を特定するには、ID と名前を対応させます:
about:extensionsページで Developer Mode を有効にすると、各拡張の ID が表示されます。- 各拡張のフォルダ内にある
manifest.jsonファイルには読みやすいnameフィールドがあり、拡張機能の特定に役立ちます。
Use a File Archiver or Unpacker
Chrome Web Store から拡張機能をダウンロードします。ファイルは .crx 拡張子になっています。ファイル拡張子を .crx から .zip に変更し、WinRAR や 7-Zip などの任意のファイルアーカイバで ZIP の内容を展開します。
Use Developer Mode in Chrome
Chrome を開き、chrome://extensions/ に移動します。右上で “Developer mode” を有効にします。“Load unpacked extension…” をクリックし、拡張機能のディレクトリを指定します。これはソースコードをダウンロードするわけではありませんが、既にダウンロード済みまたは開発中の拡張のコードを表示・修正するのに便利です。
Chrome extension manifest dataset
脆弱なブラウザ拡張を見つけるために、https://github.com/palant/chrome-extension-manifests-dataset を使い、manifest ファイルに潜在的な脆弱性の兆候がないか確認できます。たとえば、25000 以上のユーザー、content_scripts、および permission nativeMessaing を持つ拡張をチェックするには:
# 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)
各ユーザーの Preferences を直接編集し有効な HMACs を偽造することで Chromium にバックドアを仕込み、プロンプトやフラグなしに任意の unpacked extension をブラウザが受け入れて有効化するステルス手法。
Forced Extension Load Preferences Mac Forgery Windows
Detecting Malicious Extension Updates (Static Version Diffing)
Supply-chain の妥協はしばしば以前は無害だった拡張機能への malicious updates として到来します。実用的でノイズの少ないアプローチは、静的解析(例えば、Assemblyline)を使って 新しい拡張パッケージを最後に知られている良好なバージョンと比較する ことです。目的はあらゆる変更ではなく、高信頼度の差分(high-signal deltas)を検出することです。
Workflow
- 両方のバージョン(古い方 + 新しい方)を同じ静的解析プロファイルに提出する。
- 新規または更新された background/service worker スクリプト をフラグ(永続化や特権ロジック)。
- 新規または更新された content scripts をフラグ(DOMアクセスやデータ収集)。
manifest.jsonに追加された 新しい permissions/host_permissions をフラグ。- コードから抽出された 新しいドメイン をフラグ(潜在的な C2 / exfil エンドポイント)。
- 静的解析の新規検出(例: base64 decode、cookie harvesting、network-request builders、難読化パターン)をフラグ。
- 変更されたスクリプトにおけるエントロピーの急上昇や外れ値 z-score といった 統計的異常 をフラグ。
Detecting script changes accurately
- 新しいスクリプトが追加された →
manifest.jsonの diff で検出。 - 既存のスクリプトが修正された(manifest は変わらない)→ 抽出されたファイルツリーからの ファイルごとのハッシュ を比較(例: Assemblyline の
Extract出力)。これにより既存の worker や content script に対するステルスな更新を捕捉できる。
Pre-disclosure detections
既知の IOC に基づく「簡単な検出」を避けるため、threat-intel-fed サービスを無効にし、ドメインやヒューリスティックなシグネチャ、スクリプト差分、エントロピー異常などの内在的シグナルに依存してください。これにより、公開報告前に悪意あるアップデートを発見する可能性が高まります。
Example high-confidence alert logic
- Low-noise コンボ: 新規ドメイン + 新規静的解析検出 + 更新された background/service worker + 更新または追加された content scripts。
- より広い捕捉: 新規ドメイン + 新規または更新された background/service worker(再現率は高いがノイズも増える)。
このワークフローで有用な Assemblyline サービス:
- Extract: 拡張機能を展開しファイルごとのハッシュを生成する。
- Characterize: ファイルの特性(例: エントロピー)を算出する。
- JsJAWS / FrankenStrings / URLCreator: JS のヒューリスティクス、文字列、ドメインを抽出し、バージョン間で差分を取るのに役立てる。
Security Audit Checklist
Browser Extensions は攻撃面が限定されているものの、いくつかは 脆弱性 を含んでいたり ハードニングの改善余地 があるかもしれません。以下は最も一般的な項目です:
-
permissionsを可能な限り制限する -
host_permissionsを可能な限り制限する - 強力な
content_security_policyを使用する -
externally_connectableは可能な限り制限する。不要であればデフォルトで残さず、明示的に{}を指定する。 - ここで URL が XSS や takeover に対して脆弱 だと記載されている場合、攻撃者は background scripts に直接メッセージを送れる 可能性がある。非常に強力なバイパス。
-
web_accessible_resourcesは可能な限り制限する。可能なら空にする。 -
web_accessible_resourcesが空でない場合、ClickJacking を確認する。 - 拡張機能からウェブページへ何らかの communication が行われている場合、その通信経路で引き起こされる XSS 脆弱性を 確認する。
- Post Messages を使用している場合は、Post Message vulnerabilities を確認する。
- Content Script が DOM にアクセスする場合、それがウェブによって 変更された際に XSS を導入しないか を確認する。
- この通信が Content Script -> Background script の通信 にも関与している場合は特に注意を払う。
- background script が native messaging 経由で通信している場合、その通信が安全でサニタイズされているかを確認する。
- 機密情報を拡張機能のコード内に保存してはならない。
- 機密情報を拡張機能のメモリ内に保存してはならない。
- 機密情報を保護されていないファイルシステム上に保存してはならない。
Browser Extension Risks
- アプリ https://crxaminer.tech/ は拡張機能が要求する permissions などのデータを解析し、その拡張機能の利用リスクレベルを示します。
Tools
Tarnish
- 指定した Chrome webstore のリンクから任意の Chrome extension を取得する。
- manifest.json ビューア: 拡張機能の manifest を JSON 整形して表示する。
- Fingerprint Analysis: web_accessible_resources の検出と Chrome extension fingerprinting 用の自動生成 JavaScript。
- Potential Clickjacking Analysis: web_accessible_resources 指令が設定された拡張機能の HTML ページを検出。ページの目的に応じて clickjacking に対して脆弱である可能性がある。
- Permission Warning(s) viewer: ユーザーが拡張機能をインストールしようとしたときに表示される Chrome の権限プロンプト警告の一覧を表示する。
- Dangerous Function(s): innerHTML や chrome.tabs.executeScript のような、攻撃者に悪用されうる危険な関数の位置を表示する。
- Entry Point(s): 拡張機能がユーザー/外部入力を受け取る箇所を示す。拡張機能の攻撃面を理解し、悪意あるデータを送るポイントを探すのに有用。
- Dangerous Function(s) と Entry Point(s) スキャナは生成されたアラートについて以下を提供:
- アラートの原因となった関連コードスニペットと行番号。
- 問題の説明。
- 該当ソースファイルを表示する “View File” ボタン。
- アラート対象ファイルのパス。
- アラート対象ファイルの完全な Chrome extension URI。
- そのファイルの種類(Background Page script、Content Script、Browser Action 等)。
- もし脆弱な行が JavaScript ファイル内にある場合、そのファイルが含まれている全ページのパスとそれらページの種類、および web_accessible_resource の状態。
- Content Security Policy (CSP) analyzer and bypass checker: 拡張機能の CSP の弱点を指摘し、ホワイトリストにある CDN 等による CSP バイパスの可能性も明示する。
- Known Vulnerable Libraries: Retire.js を使って既知の脆弱ライブラリの使用をチェックする。
- 拡張機能のダウンロードと整形済みバージョンのダウンロード。
- 元の拡張機能のダウンロード。
- 美化(beautified)された拡張機能のダウンロード(自動整形された HTML と JavaScript)。
- スキャン結果の自動キャッシュ:初回は時間がかかるが、拡張機能が更新されていなければ2回目以降はほぼ即時になる。
- Tarnish によって生成されたレポートのリンク可能な URL を提供。
Neto
Project Neto は Python 3 パッケージで、Firefox や Chrome といった一般的なブラウザ用のプラグインや拡張機能の隠れた機能を解析・抽出することを目的としている。manifest.json、ローカリゼーションフォルダ、Javascript や HTML ソースファイルなど、拡張機能内の関連リソースからこれらの機能を抽出するために、パッケージファイルの解凍プロセスを自動化する。
References
- Thanks to @naivenom for the help with this methodology
- 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/24/attack-surface-of-extension-pages/
- https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/
- 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/mv2/background-pages
- https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/
- https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0
- https://redcanary.com/blog/threat-detection/assemblyline-browser-extensions/
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。


