Browser Extension Pentesting Methodology

Support HackTricks

基本情報

ブラウザ拡張機能はJavaScriptで書かれ、ブラウザによってバックグラウンドで読み込まれます。拡張機能は独自のDOMを持っていますが、他のサイトのDOMと相互作用することができます。これは、他のサイトの機密性、完全性、および可用性(CIA)を危険にさらす可能性があることを意味します。

主なコンポーネント

拡張機能のレイアウトは視覚化されると最も良く見え、3つのコンポーネントで構成されています。それぞれのコンポーネントを詳しく見ていきましょう。

コンテンツスクリプト

各コンテンツスクリプトは単一のウェブページのDOMに直接アクセスでき、潜在的に悪意のある入力にさらされます。しかし、コンテンツスクリプトは拡張機能のコアにメッセージを送信する能力以外の権限を持っていません。

拡張機能コア

拡張機能コアは、ほとんどの拡張機能の特権/アクセスを含んでいますが、拡張機能コアはXMLHttpRequestおよびコンテンツスクリプトを介してのみウェブコンテンツと相互作用できます。また、拡張機能コアはホストマシンに直接アクセスすることはできません。

ネイティブバイナリ

拡張機能は、ユーザーの完全な権限でホストマシンにアクセスできるネイティブバイナリを許可します。ネイティブバイナリは、Flashや他のブラウザプラグインで使用される標準のNetscapeプラグインアプリケーションプログラミングインターフェース(NPAPI)を介して拡張機能コアと相互作用します。

境界

ユーザーの完全な権限を取得するには、攻撃者は拡張機能に悪意のある入力をコンテンツスクリプトから拡張機能のコアに、そして拡張機能のコアからネイティブバイナリに渡すように説得しなければなりません。

拡張機能の各コンポーネントは、強力な保護境界によって互いに分離されています。各コンポーネントは別々のオペレーティングシステムプロセスで実行されます。コンテンツスクリプトと拡張機能コアは、ほとんどのオペレーティングシステムサービスに利用できないサンドボックスプロセスで実行されます。

さらに、コンテンツスクリプトは別のJavaScriptヒープで実行されることによって、関連するウェブページから分離されています。コンテンツスクリプトとウェブページは同じ基盤となるDOMにアクセスできますが、2つはJavaScriptポインタを交換することは決してありません。これにより、JavaScript機能の漏洩を防ぎます。

manifest.json

Chrome拡張機能は、単に.crxファイル拡張子を持つZIPフォルダーです。拡張機能のコアは、フォルダーのルートにある**manifest.json**ファイルで、レイアウト、権限、およびその他の設定オプションを指定します。

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

コンテンツスクリプトは、ユーザーが一致するページに移動するたびに読み込まれ、この場合は**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_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が数少ない例外の一つです。これらの例外を超える機能については、メッセージが拡張ページに送信され、コンテンツスクリプトが通信できるようになります。

ブラウザによって、コンテンツスクリプトの機能は若干異なる場合があります。Chromiumベースのブラウザの場合、機能リストはChrome Developers documentationで入手可能で、Firefoxの場合はMDNが主な情報源となります。 また、コンテンツスクリプトはバックグラウンドスクリプトと通信する能力があり、これによりアクションを実行し、応答を返すことができます。

Chromeでコンテンツスクリプトを表示およびデバッグするには、オプション > その他のツール > デベロッパーツールからChromeデベロッパーツールメニューにアクセスするか、Ctrl + Shift + Iを押します。

デベロッパーツールが表示されたら、ソースタブをクリックし、次にコンテンツスクリプトタブをクリックします。これにより、さまざまな拡張機能から実行中のコンテンツスクリプトを観察し、実行フローを追跡するためのブレークポイントを設定できます。

注入されたコンテンツスクリプト

コンテンツスクリプトは必須ではないことに注意してください。動的にスクリプトを注入したり、プログラム的に注入することも可能です。これは実際により詳細な制御を提供します。

コンテンツスクリプトをプログラム的に注入するには、拡張機能がスクリプトを注入するページに対してホスト権限を持っている必要があります。これらの権限は、拡張機能のマニフェスト内で要求するか、activeTabを通じて一時的に取得することができます。

activeTabベースの拡張機能の例

manifest.json
{
"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_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" ],
}]);

background

コンテンツスクリプトによって送信されたメッセージは、バックグラウンドページによって受信され、拡張機能のコンポーネントを調整する中心的な役割を果たします。特に、バックグラウンドページは拡張機能のライフタイムを通じて持続し、直接的なユーザーの操作なしに静かに動作します。独自のドキュメントオブジェクトモデル(DOM)を持ち、複雑な相互作用と状態管理を可能にします。

重要なポイント:

  • バックグラウンドページの役割: 拡張機能の神経中枢として機能し、拡張機能のさまざまな部分間の通信と調整を確保します。

  • 持続性: ユーザーには見えないが、拡張機能の機能に不可欠な常に存在するエンティティです。

  • 自動生成: 明示的に定義されていない場合、ブラウザは自動的にバックグラウンドページを作成します。この自動生成されたページには、拡張機能のマニフェストに指定されたすべてのバックグラウンドスクリプトが含まれ、拡張機能のバックグラウンドタスクのシームレスな操作を確保します。

明示的に宣言されていない場合にブラウザがバックグラウンドページを自動生成することによって提供される便利さは、すべての必要なバックグラウンドスクリプトが統合され、機能することを保証し、拡張機能のセットアッププロセスを簡素化します。

Example background script:

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への包括的なアクセスを享受します。

permissions & host_permissions

**permissionshost_permissions**は、manifest.jsonのエントリで、ブラウザ拡張機能がどの権限(ストレージ、位置情報など)を持っているか、どのウェブページであるかを示します。

ブラウザ拡張機能は非常に特権的であるため、悪意のあるものや侵害されたものは、攻撃者に機密情報を盗んだり、ユーザーを監視したりするためのさまざまな手段を提供する可能性があります

これらの設定がどのように機能し、どのように悪用される可能性があるかを確認してください:

BrowExt - permissions & host_permissions

content_security_policy

コンテンツセキュリティポリシーは、manifest.json内にも宣言できます。定義されている場合、それは脆弱である可能性があります。

ブラウザ拡張機能ページのデフォルト設定はかなり制限されています:

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

CSPおよび潜在的なバイパスに関する詳細は、以下を確認してください:

Content Security Policy (CSP) Bypass

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

公共拡張機能では、extension-idがアクセス可能です:

ただし、manifest.jsonパラメータ**use_dynamic_urlが使用されている場合、このidは動的**になる可能性があります。

ここにページが記載されていても、Content Security PolicyのおかげでClickJackingに対して保護されている可能性があることに注意してください。したがって、ClickJacking攻撃が可能であることを確認する前に、これを(frame-ancestorsセクション)チェックする必要があります。

これらのページにアクセスできることは、これらのページが潜在的に脆弱なClickJackingであることを意味します:

BrowExt - ClickJacking

これらのページが拡張機能によってのみ読み込まれ、ランダムなURLからは読み込まれないようにすることで、ClickJacking攻撃を防ぐことができます。

web_accessible_resourcesからのページや拡張機能の他のページもバックグラウンドスクリプトに連絡することができることに注意してください。したがって、これらのページのいずれかがXSSに対して脆弱である場合、より大きな脆弱性を引き起こす可能性があります。

さらに、**web_accessible_resourcesに示されたページはiframe内でのみ開くことができますが、新しいタブからは拡張機能IDを知っていれば拡張機能内の任意のページにアクセスすることが可能です。したがって、同じパラメータを悪用するXSSが見つかった場合、ページがweb_accessible_resources**に設定されていなくても悪用される可能性があります。

externally_connectable

docsによると、"externally_connectable"マニフェストプロパティは、どの拡張機能とウェブページがあなたの拡張機能に接続できるかを宣言します。これはruntime.connectおよびruntime.sendMessageを介して行われます。

  • externally_connectableキーが拡張機能のマニフェストに宣言されていない場合、または**"ids": ["*"]**として宣言されている場合、すべての拡張機能が接続できますが、ウェブページは接続できません

  • 特定のIDが指定されている場合、例えば"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]のように、そのアプリケーションのみが接続できます。

  • matchesが指定されている場合、これらのウェブアプリは接続できるようになります:

"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • 空として指定されている場合: "externally_connectable": {}、アプリやウェブは接続できません。

ここで示されている 拡張機能とURLが少ないほど攻撃面が小さくなります。

もしウェブページが externally_connectableXSSまたはテイクオーバーに脆弱 として示されている場合、攻撃者は バックグラウンドスクリプトに直接メッセージを送信 でき、コンテンツスクリプトとそのCSPを完全にバイパスすることができます。

したがって、これは 非常に強力なバイパス です。

さらに、クライアントが不正な拡張機能をインストールした場合、たとえそれが脆弱な拡張機能と通信することが許可されていなくても、許可されたウェブページにXSSデータを注入 したり、WebRequest または DeclarativeNetRequest APIを悪用して、ターゲットドメインのリクエストを操作し、JavaScriptファイルのリクエストを変更することができます。(ターゲットページのCSPがこれらの攻撃を防ぐ可能性があることに注意してください)。このアイデアは この書き込みから 来ています。

コミュニケーションの概要

拡張機能 <--> ウェブアプリ

コンテンツスクリプトとウェブページ間で通信するために、通常はポストメッセージが使用されます。したがって、ウェブアプリケーションでは通常 window.postMessage 関数への呼び出しが見られ、コンテンツスクリプトでは window.addEventListener のようなリスナーが見られます。ただし、拡張機能は ポストメッセージを送信してウェブアプリケーションと通信 することもできるため、ウェブはそれを期待する必要があります。または、ウェブに新しいスクリプトを読み込ませることもできます。

拡張機能内

通常、chrome.runtime.sendMessage 関数が拡張機能内でメッセージを送信するために使用され(通常は background スクリプトによって処理されます)、それを受信して処理するためにリスナーが 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 });

</details>

バックグラウンドスクリプトから特定のタブにあるコンテンツスクリプトにメッセージを送信することも可能で、**`chrome.tabs.sendMessage`**を呼び出す必要があります。この際、メッセージを送信する**タブのID**を指定する必要があります。

### 許可された`externally_connectable`から拡張機能へ

`externally_connectable`設定で許可された**Webアプリと外部ブラウザ拡張機能**は、次のようにリクエストを送信できます:
```javascript
chrome.runtime.sendMessage(extensionId, ...

必要な場合は拡張IDを言及する必要があります。

ネイティブメッセージング

バックグラウンドスクリプトがシステム内のバイナリと通信することが可能であり、この通信が適切に保護されていない場合、RCEなどの重大な脆弱性にさらされる可能性がありますこの後で詳しく説明します

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

Web ↔︎ コンテンツスクリプト通信

コンテンツスクリプトが動作する環境とホストページが存在する環境は分離されており、隔離が確保されています。この隔離にもかかわらず、両者はページのドキュメントオブジェクトモデル (DOM)、つまり共有リソースと相互作用する能力を持っています。ホストページがコンテンツスクリプトと通信するため、またはコンテンツスクリプトを介して拡張機能と間接的に通信するためには、両者がアクセス可能なDOMを通信チャネルとして利用する必要があります。

ポストメッセージ

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

安全なPost Message通信は、受信したメッセージの信頼性を確認する必要があります。これは以下を確認することで行えます:

  • event.isTrusted: これは、イベントがユーザーのアクションによってトリガーされた場合のみTrueになります。

  • コンテンツスクリプトは、ユーザーが何らかのアクションを実行した場合にのみメッセージを期待しているかもしれません。

  • origin domain: メッセージを期待するのは、許可リストにあるドメインのみです。

  • 正規表現が使用される場合は、非常に注意が必要です。

  • Source: received_message.source !== windowを使用して、メッセージがコンテンツスクリプトがリスニングしている同じウィンドウからのものであるかを確認できます。

前述のチェックは、実施されていても脆弱である可能性があるため、次のページで潜在的なPost Messageバイパスを確認してください:

Iframe

別の通信方法としてIframe URLsを通じて行うことが考えられます。例は以下にあります:

DOM

これは「正確には」通信方法ではありませんが、ウェブとコンテンツスクリプトはウェブDOMにアクセスできます。したがって、コンテンツスクリプトがそこから情報を読み取っている場合、ウェブDOMを信頼していると、ウェブはこのデータを変更する可能性があります(ウェブは信頼されるべきではないため、またはウェブがXSSに対して脆弱であるため)し、コンテンツスクリプトを危険にさらす可能性があります。

DOMベースのXSSを使用してブラウザ拡張を危険にさらす例も以下にあります:

コンテンツスクリプト ↔︎ バックグラウンドスクリプト通信

コンテンツスクリプトは、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() は同期的に実行されました。sendResponse() の非同期実行のために onMessage イベントハンドラーを修正するには、return true; を組み込むことが不可欠です。

重要な考慮事項は、複数のページが onMessage イベントを受信するように設定されているシナリオでは、特定のイベントに対して sendResponse() を最初に実行したページ だけが、効果的に応答を提供できるということです。同じイベントに対するその後の応答は考慮されません。

新しい拡張機能を作成する際は、コールバックよりもプロミスを優先すべきです。コールバックの使用に関しては、sendResponse() 関数は、同期コンテキスト内で直接実行される場合、またはイベントハンドラーが true を返すことによって非同期操作を示す場合にのみ有効と見なされます。どのハンドラーも true を返さない場合や、sendResponse() 関数がメモリから削除された場合(ガーベジコレクションされた場合)、sendMessage() 関数に関連付けられたコールバックがデフォルトでトリガーされます。

ネイティブメッセージング

ブラウザ拡張機能は、システム内のバイナリと 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/"]
}

nameruntime.connectNative() または runtime.sendNativeMessage() に渡される文字列で、ブラウザ拡張のバックグラウンドスクリプトからアプリケーションと通信します。 path はバイナリへのパスで、1つの有効な type は stdio(stdin と stdout を使用)であり、 allowed_origins はアクセスできる拡張機能を示します(ワイルドカードは使用できません)。

Chrome/Chromium は、この JSON をいくつかの Windows レジストリや macOS および Linux のいくつかのパスで検索します(詳細は docs を参照)。

ブラウザ拡張は、この通信を使用するために nativeMessaing 権限を宣言する必要があります。

これは、ネイティブアプリケーションにメッセージを送信するバックグラウンドスクリプトコードの例です:

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

In このブログ投稿では、ネイティブメッセージを悪用する脆弱なパターンが提案されています:

  1. ブラウザ拡張機能はコンテンツスクリプトのためのワイルドカードパターンを持っています。

  2. コンテンツスクリプトはsendMessageを使用してバックグラウンドスクリプトにpostMessageメッセージを渡します。

  3. バックグラウンドスクリプトはsendNativeMessageを使用してネイティブアプリケーションにメッセージを渡します。

  4. ネイティブアプリケーションはメッセージを危険に扱い、コード実行につながります。

そして、その中でブラウザ拡張機能を悪用して任意のページからRCEに移行する例が説明されています

メモリ/コード/クリップボード内の機密情報

ブラウザ拡張機能がメモリ内に機密情報を保存している場合、これはダンプされる可能性があり(特にWindowsマシンで)、この情報が検索される可能性があります。

したがって、ブラウザ拡張機能のメモリは安全とは見なされるべきではなく、資格情報やニーモニックフレーズなどの機密情報は保存されるべきではありません

もちろん、コード内に機密情報を置かないでください、それは公開されることになります

ブラウザからメモリをダンプするには、プロセスメモリをダンプするか、ブラウザ拡張機能の設定に行き、**Inspect pop-up**をクリック -> **Memory**セクション -> **Take a snapshotを選択し、CTRL+F**でスナップショット内の機密情報を検索します。

さらに、ニーモニックキーやパスワードのような非常に機密性の高い情報は、クリップボードにコピーされることを許可すべきではありません(または少なくとも数秒以内にクリップボードから削除するべきです)なぜなら、その後クリップボードを監視しているプロセスがそれらを取得できるからです。

ブラウザに拡張機能を読み込む

  1. ブラウザ拡張機能をダウンロードし、解凍します。

  2. chrome://extensions/に移動し、Developer Modeを有効にします

  3. **Load unpacked**ボタンをクリックします。

Firefoxでは、**about:debugging#/runtime/this-firefoxに移動し、Load Temporary Add-on**ボタンをクリックします。

ストアからソースコードを取得する

Chrome拡張機能のソースコードは、さまざまな方法で取得できます。以下に各オプションの詳細な説明と手順を示します。

コマンドラインを介してZIPとして拡張機能をダウンロード

Chrome拡張機能のソースコードは、コマンドラインを使用してZIPファイルとしてダウンロードできます。これは、特定のURLからZIPファイルを取得するためにcurlを使用し、その後ZIPファイルの内容をディレクトリに抽出することを含みます。手順は以下の通りです:

  1. "extension_id"を拡張機能の実際の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 Extension Source Viewerを使用することです。これはChrome Web Storeからインストールできます。ビューワーのソースコードはそのGitHubリポジトリで入手可能です。

ローカルにインストールされた拡張機能のソースを表示する

ローカルにインストールされたChrome拡張機能も検査できます。方法は以下の通りです:

  1. chrome://version/にアクセスし、「Profile Path」フィールドを見つけて、Chromeのローカルプロファイルディレクトリにアクセスします。

  2. プロファイルディレクトリ内のExtensions/サブフォルダに移動します。

  3. このフォルダには、通常は読みやすい形式のソースコードを持つすべてのインストールされた拡張機能が含まれています。

拡張機能を特定するには、IDを名前にマッピングできます:

  • about:extensionsページで開発者モードを有効にして、各拡張機能のIDを表示します。

  • 各拡張機能のフォルダ内のmanifest.jsonファイルには、拡張機能を特定するのに役立つ読みやすいnameフィールドが含まれています。

ファイルアーカイバまたはアンパッカーを使用する

Chrome Web Storeにアクセスして拡張機能をダウンロードします。ファイルは.crx拡張子を持っています。ファイルの拡張子を.crxから.zipに変更します。任意のファイルアーカイバ(WinRAR、7-Zipなど)を使用してZIPファイルの内容を抽出します。

Chromeで開発者モードを使用する

Chromeを開き、chrome://extensions/に移動します。右上で「開発者モード」を有効にします。「未パッケージの拡張機能を読み込む...」をクリックします。拡張機能のディレクトリに移動します。これはソースコードをダウンロードするものではありませんが、すでにダウンロードまたは開発された拡張機能のコードを表示および変更するのに便利です。

Chrome拡張機能マニフェストデータセット

脆弱なブラウザ拡張機能を見つけるために、https://github.com/palant/chrome-extension-manifests-datasetを使用して、マニフェストファイルに潜在的な脆弱性の兆候がないか確認できます。たとえば、25000人以上のユーザーを持つ拡張機能、content_scripts、および権限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')"

セキュリティ監査チェックリスト

ブラウザ拡張機能は限られた攻撃面を持っていますが、その中には脆弱性強化の可能性が含まれている場合があります。以下は最も一般的なものです:

ツール

  • 提供されたChromeウェブストアリンクから任意のChrome拡張機能を取得します。

  • manifest.json ビューワー:拡張機能のマニフェストのJSON整形バージョンを表示します。

  • フィンガープリンター分析web_accessible_resources の検出とChrome拡張機能フィンガープリンティングJavaScriptの自動生成。

  • 潜在的なClickjacking分析web_accessible_resources ディレクティブが設定された拡張機能のHTMLページの検出。これらはページの目的に応じてClickjackingに脆弱である可能性があります。

  • 権限警告ビューワー:ユーザーが拡張機能をインストールしようとしたときに表示されるすべてのChrome権限プロンプト警告のリストを表示します。

  • 危険な関数:攻撃者によって悪用される可能性のある危険な関数の場所を示します(例:innerHTML、chrome.tabs.executeScriptなどの関数)。

  • エントリポイント:拡張機能がユーザー/外部入力を受け取る場所を示します。これは拡張機能の表面積を理解し、悪意のあるデータを拡張機能に送信する潜在的なポイントを探すのに役立ちます。

  • 危険な関数とエントリポイントスキャナーは、生成されたアラートに対して以下を持っています:

  • アラートを引き起こした関連コードスニペットと行。

  • 問題の説明。

  • コードを含む完全なソースファイルを表示するための「ファイルを表示」ボタン。

  • アラートが発生したファイルのパス。

  • アラートが発生したファイルの完全なChrome拡張機能URI。

  • バックグラウンドページスクリプト、コンテンツスクリプト、ブラウザアクションなど、ファイルの種類。

  • 脆弱な行がJavaScriptファイルにある場合、それが含まれているすべてのページのパスとこれらのページの種類、web_accessible_resource ステータス。

  • コンテンツセキュリティポリシー(CSP)アナライザーおよびバイパスチェッカー:これにより、拡張機能のCSPの弱点が指摘され、ホワイトリストに登録されたCDNなどによるCSPのバイパスの潜在的な方法が明らかになります。

  • 既知の脆弱なライブラリ:これはRetire.jsを使用して、既知の脆弱なJavaScriptライブラリの使用をチェックします。

  • 拡張機能とフォーマットされたバージョンをダウンロード。

  • 元の拡張機能をダウンロード。

  • 拡張機能の美化されたバージョンをダウンロード(自動整形されたHTMLとJavaScript)。

  • スキャン結果の自動キャッシュ。拡張機能のスキャンを初めて実行する際にはかなりの時間がかかります。しかし、拡張機能が更新されていない限り、2回目は結果がキャッシュされているため、ほぼ瞬時に完了します。

  • リンク可能なレポートURL。誰かにTarnishによって生成された拡張機能レポートへのリンクを簡単に提供できます。

プロジェクトNetoは、FirefoxやChromeなどの有名なブラウザのブラウザプラグインや拡張機能の隠れた機能を分析し解明するために考案されたPython 3パッケージです。これは、manifest.json、ローカリゼーションフォルダー、またはJavaScriptおよびHTMLソースファイルなどの関連リソースからこれらの機能を抽出するために、パッケージ化されたファイルを解凍するプロセスを自動化します。

参考文献

Last updated