ブラウザ拡張機能のペンテスト手法
Reading time: 47 minutes
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
基本情報
ブラウザ拡張機能はJavaScriptで書かれ、ブラウザによってバックグラウンドで読み込まれます。独自のDOMを持っていますが、他のサイトのDOMと相互作用することができます。これは、他のサイトの機密性、完全性、可用性(CIA)を危険にさらす可能性があることを意味します。
主なコンポーネント
拡張機能のレイアウトは視覚化すると最も良く見え、3つのコンポーネントで構成されています。それぞれのコンポーネントを詳しく見ていきましょう。
 (1) (1).png)
コンテンツスクリプト
各コンテンツスクリプトは単一のウェブページのDOMに直接アクセスでき、潜在的に悪意のある入力にさらされます。しかし、コンテンツスクリプトは拡張機能のコアにメッセージを送信する能力以外の権限を持っていません。
拡張機能コア
拡張機能コアは、ほとんどの拡張機能の特権/アクセスを含んでいますが、拡張機能コアはXMLHttpRequestおよびコンテンツスクリプトを介してのみウェブコンテンツと相互作用できます。また、拡張機能コアはホストマシンに直接アクセスすることはできません。
ネイティブバイナリ
拡張機能は、ユーザーの完全な権限でホストマシンにアクセスできるネイティブバイナリを許可します。ネイティブバイナリは、Flashや他のブラウザプラグインで使用される標準のNetscapeプラグインアプリケーションプログラミングインターフェース(NPAPI)を介して拡張機能コアと相互作用します。
境界
caution
ユーザーの完全な権限を取得するために、攻撃者は拡張機能に悪意のある入力をコンテンツスクリプトから拡張機能のコアに、そして拡張機能のコアからネイティブバイナリに渡すように説得しなければなりません。
拡張機能の各コンポーネントは、強力な保護境界によって互いに分離されています。各コンポーネントは別々のオペレーティングシステムプロセスで実行されます。コンテンツスクリプトと拡張機能コアは、ほとんどのオペレーティングシステムサービスに利用できないサンドボックスプロセスで実行されます。
さらに、コンテンツスクリプトは別のJavaScriptヒープで実行されることによって、関連するウェブページから分離されています。コンテンツスクリプトとウェブページは同じ基盤となるDOMにアクセスできますが、2つは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
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
を使用することも可能です。
これは、ストレージ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)
})
.png)
このボタンがクリックされると、コンテンツスクリプトによって拡張ページにメッセージが送信されます。これは、runtime.sendMessage() APIを利用することによります。コンテンツスクリプトはAPIへの直接アクセスに制限があり、storage
が数少ない例外の一つです。これらの例外を超える機能については、メッセージが拡張ページに送信され、コンテンツスクリプトが通信できるようになります。
warning
ブラウザによって、コンテンツスクリプトの機能は若干異なる場合があります。Chromiumベースのブラウザの場合、機能リストはChrome Developers documentationで入手可能で、Firefoxの場合はMDNが主な情報源となります。
また、コンテンツスクリプトはバックグラウンドスクリプトと通信する能力があり、これによりアクションを実行し、応答を返すことができます。
Chromeでコンテンツスクリプトを表示およびデバッグするには、オプション > その他のツール > デベロッパーツールからChromeデベロッパーツールメニューにアクセスするか、Ctrl + Shift + Iを押します。
デベロッパーツールが表示されたら、ソースタブをクリックし、次にコンテンツスクリプトタブをクリックします。これにより、さまざまな拡張機能から実行中のコンテンツスクリプトを観察し、実行フローを追跡するためのブレークポイントを設定できます。
注入されたコンテンツスクリプト
tip
コンテンツスクリプトは必須ではないことに注意してください。動的にスクリプトを注入したり、プログラム的に注入することも可能です。これは実際により細かい制御を提供します。
コンテンツスクリプトをプログラム的に注入するには、拡張機能がスクリプトを注入するページに対してホスト権限を持っている必要があります。これらの権限は、拡張機能のマニフェスト内で要求するか、activeTabを通じて一時的に取得することができます。
activeTabベースの拡張機能の例
{
"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は拡張機能のライフタイム全体にわたって持続し、直接的なユーザーの操作なしに静かに動作します。独自のDocument Object Model (DOM)を持ち、複雑な相互作用と状態管理を可能にします。
重要なポイント:
- Background Pageの役割: 拡張機能の神経中枢として機能し、拡張機能のさまざまな部分間の通信と調整を確保します。
- 持続性: ユーザーには見えないが、拡張機能の機能に不可欠な常に存在するエンティティです。
- 自動生成: 明示的に定義されていない場合、ブラウザは自動的にbackground pageを作成します。この自動生成されたページには、拡張機能のマニフェストに指定されたすべてのバックグラウンドスクリプトが含まれ、拡張機能のバックグラウンドタスクのシームレスな操作を確保します。
tip
明示的に宣言されていない場合にブラウザがbackground pageを自動生成することによって提供される便利さは、すべての必要なバックグラウンドスクリプトが統合され、動作することを保証し、拡張機能のセットアッププロセスを簡素化します。
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
にアクセスするか、次のようにクリックすることでこのページにアクセスできました:
.png)
これらのページは、必要に応じて動的にコンテンツを読み込むため、バックグラウンドページのように永続的ではないことに注意してください。それにもかかわらず、これらはバックグラウンドページと特定の機能を共有します:
- コンテンツスクリプトとの通信:バックグラウンドページと同様に、これらのページはコンテンツスクリプトからメッセージを受信でき、拡張機能内での相互作用を促進します。
- 拡張機能固有のAPIへのアクセス:これらのページは、拡張機能に定義された権限に従って、拡張機能固有のAPIへの包括的なアクセスを享受します。
permissions
& host_permissions
permissions
と host_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がアクセス可能です:
.png)
ただし、manifest.json
パラメータ**use_dynamic_url
が使用されている場合、このidは動的**になる可能性があります。
tip
ここにページが記載されていても、Content Security PolicyのおかげでClickJackingから保護されている可能性があることに注意してください。したがって、ClickJacking攻撃が可能かどうかを確認する前に、それをチェックする必要があります(frame-ancestorsセクション)。
これらのページにアクセスできることは、これらのページが潜在的に脆弱なClickJackingであることを意味します:
tip
これらのページが拡張機能によってのみ読み込まれ、ランダムなURLによって読み込まれないようにすることで、ClickJacking攻撃を防ぐことができます。
caution
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 が少ないほど、攻撃面は小さくなります。
caution
externally_connectable
に XSSまたはテイクオーバーに脆弱なウェブページ が示されている場合、攻撃者は バックグラウンドスクリプトに直接メッセージを送信でき、コンテンツスクリプトとその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()
の例
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
})
特定のタブにあるコンテンツスクリプトにメッセージを送信することも可能で、chrome.tabs.sendMessage
を呼び出す際にメッセージを送信するタブのIDを指定する必要があります。
許可された externally_connectable
から拡張機能へ
externally_connectable
設定で許可されたWebアプリと外部ブラウザ拡張機能は、次のようにリクエストを送信できます:
chrome.runtime.sendMessage(extensionId, ...
必要な場合は拡張IDを言及してください。
ネイティブメッセージング
バックグラウンドスクリプトは、システム内のバイナリと通信することが可能であり、この通信が適切に保護されていない場合、RCEなどの重大な脆弱性にさらされる可能性があります。この後で詳しく説明します。
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
Web ↔︎ Content Script Communication
コンテンツスクリプトが動作する環境とホストページが存在する環境は分離されており、隔離が確保されています。この隔離にもかかわらず、両者はページのドキュメントオブジェクトモデル (DOM)、つまり共有リソースと相互作用する能力を持っています。ホストページがコンテンツスクリプトと通信するため、またはコンテンツスクリプトを介して拡張機能と間接的に通信するためには、両者がアクセス可能な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
)
安全な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/"]
}
name
は runtime.connectNative()
または runtime.sendNativeMessage()
に渡される文字列で、ブラウザ拡張のバックグラウンドスクリプトからアプリケーションと通信するために使用されます。 path
はバイナリへのパスで、1つの有効な type
は stdio(stdin と stdout を使用)であり、 allowed_origins
はアクセスできる拡張機能を示します(ワイルドカードは使用できません)。
Chrome/Chromium は、この JSON をいくつかの Windows レジストリや macOS および Linux のいくつかのパスで検索します(詳細は docs を参照)。
tip
ブラウザ拡張は、この通信を使用できるようにするために nativeMessaing
権限を宣言する必要があります。
これは、ネイティブアプリケーションにメッセージを送信するバックグラウンドスクリプトコードの例です:
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
このブログ投稿では、ネイティブメッセージを悪用する脆弱なパターンが提案されています:
- ブラウザ拡張機能は、コンテンツスクリプトのためのワイルドカードパターンを持っています。
- コンテンツスクリプトは、
sendMessage
を使用してバックグラウンドスクリプトにpostMessage
メッセージを渡します。 - バックグラウンドスクリプトは、
sendNativeMessage
を使用してネイティブアプリケーションにメッセージを渡します。 - ネイティブアプリケーションはメッセージを危険に扱い、コード実行につながります。
その中で、ブラウザ拡張機能を悪用して任意のページからRCEに移行する例が説明されています。
メモリ/コード/クリップボード内の機密情報
ブラウザ拡張機能がメモリ内に機密情報を保存している場合、これはダンプされる可能性があり(特にWindowsマシンで)、この情報が検索される可能性があります。
したがって、ブラウザ拡張機能のメモリは安全とは見なされるべきではなく、機密情報(資格情報やニーモニックフレーズなど)は保存されるべきではありません。
もちろん、コード内に機密情報を置かないでください、それは公開されるからです。
ブラウザからメモリをダンプするには、プロセスメモリをダンプするか、ブラウザ拡張機能の設定に行き、**Inspect pop-up
**をクリック -> **Memory
**セクション -> **Take a snapshot
を選択し、CTRL+F
**でスナップショット内の機密情報を検索します。
さらに、ニーモニックキーやパスワードのような非常に機密性の高い情報は、クリップボードにコピーされることを許可すべきではありません(または少なくとも数秒以内にクリップボードから削除するべきです)なぜなら、その後クリップボードを監視しているプロセスがそれらを取得できるからです。
ブラウザに拡張機能を読み込む
- ブラウザ拡張機能をダウンロードし、解凍します。
chrome://extensions/
に移動し、Developer Mode
を有効にします。- **
Load unpacked
**ボタンをクリックします。
Firefoxでは、**about:debugging#/runtime/this-firefox
に移動し、Load Temporary Add-on
**ボタンをクリックします。
ストアからソースコードを取得する
Chrome拡張機能のソースコードは、さまざまな方法で取得できます。以下に各オプションの詳細な説明と手順を示します。
コマンドラインを使用してZIPとして拡張機能をダウンロード
Chrome拡張機能のソースコードは、コマンドラインを使用してZIPファイルとしてダウンロードできます。これは、curl
を使用して特定のURLからZIPファイルを取得し、その後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"
CRX Viewerウェブサイトを使用する
CRX Viewer拡張機能を使用する
もう一つの便利な方法は、オープンソースプロジェクトであるChrome Extension Source Viewerを使用することです。これはChrome Web Storeからインストールできます。ビューワーのソースコードはそのGitHubリポジトリで入手可能です。
ローカルにインストールされた拡張機能のソースを表示する
ローカルにインストールされたChrome拡張機能も検査できます。方法は以下の通りです:
chrome://version/
にアクセスし、「Profile Path」フィールドを見つけてChromeのローカルプロファイルディレクトリに移動します。- プロファイルディレクトリ内の
Extensions/
サブフォルダに移動します。 - このフォルダにはすべてのインストールされた拡張機能が含まれており、通常は読みやすい形式のソースコードがあります。
拡張機能を特定するには、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
、および権限nativeMessaging
を確認するには:
# 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')"
セキュリティ監査チェックリスト
ブラウザ拡張機能は限られた攻撃面を持っていますが、その中には脆弱性や強化の可能性が含まれている場合があります。以下は最も一般的なものです:
-
要求される
permissions
をできるだけ制限する -
host_permissions
をできるだけ制限する -
強力な
content_security_policy
を使用する -
externally_connectable
をできるだけ制限する。必要がない場合はデフォルトで残さず、{}
を指定する - ここにXSSまたは乗っ取りに脆弱なURLが記載されている場合、攻撃者はバックグラウンドスクリプトに直接メッセージを送信できる。非常に強力なバイパスです。
-
web_accessible_resources
をできるだけ制限する。可能であれば空にする。 -
web_accessible_resources
がない場合、ClickJackingを確認する - 拡張機能からウェブページへの通信が発生する場合、XSS 脆弱性が通信によって引き起こされていないか確認する。
- Post Messagesが使用されている場合、Post Messageの脆弱性を確認する。
- Content ScriptがDOMの詳細にアクセスする場合、ウェブによって変更されることでXSSを導入していないか確認する
- この通信がContent Script -> バックグラウンドスクリプト通信にも関与している場合は特に強調する
- バックグラウンドスクリプトがネイティブメッセージングを介して通信している場合、通信が安全でサニタイズされているか確認する
- 機密情報はブラウザ拡張機能のコード内に保存すべきではない
- 機密情報はブラウザ拡張機能のメモリ内に保存すべきではない
- 機密情報は****ファイルシステムに無防備に保存すべきではない
ブラウザ拡張機能のリスク
- アプリ https://crxaminer.tech/ は、ブラウザ拡張機能が要求する権限などのデータを分析し、ブラウザ拡張機能の使用リスクレベルを提供します。
ツール
Tarnish
- 提供された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
プロジェクトNetoは、FirefoxやChromeなどの有名なブラウザのブラウザプラグインや拡張機能の隠れた機能を分析し、解明するために考案されたPython 3パッケージです。manifest.json
、ローカリゼーションフォルダ、JavaScriptおよびHTMLソースファイルなどの関連リソースからこれらの機能を抽出するために、パッケージ化されたファイルを解凍するプロセスを自動化します。
参考文献
- この方法論に関する助けを提供してくれた @naivenom に感謝します
- 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
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を提出してハッキングトリックを共有してください。