mirror of
https://github.com/carlospolop/hacktricks
synced 2025-02-16 14:08:26 +00:00
150 lines
11 KiB
Markdown
150 lines
11 KiB
Markdown
|
# Browser HTTP Request Smuggling
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><strong>Support HackTricks and get benefits!</strong></summary>
|
||
|
|
||
|
Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
|
|
||
|
Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
|
||
|
Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
|
|
||
|
**Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||
|
|
||
|
**Share your hacking tricks submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**
|
||
|
|
||
|
</details>
|
||
|
|
||
|
## CL.0/H2.0 browser-compatible desync
|
||
|
|
||
|
This vulnerability occurs when the **Content Length** (CL) header is being completely **ignored** by the **backend server**. Then, the back-end treats the **body** as the **start of the second request's method**. Ignoring the CL is equivalent to treating it as having a value of 0, so this is a CL.0 desync - a [known](https://i.blackhat.com/USA-20/Wednesday/us-20-Klein-HTTP-Request-Smuggling-In-2020-New-Variants-New-Defenses-And-New-Challenges.pdf) but lesser-explored attack class.
|
||
|
|
||
|
![](<../../.gitbook/assets/image (3).png>)
|
||
|
|
||
|
The attack was possible because the back-end server simply **wasn't expecting a POST request**.
|
||
|
|
||
|
{% hint style="warning" %}
|
||
|
Note that this vulnerability is being **triggered** by a completely **valid**, specification-compliant **HTTP request**. This meant the **front-end has zero chance of protecting** against it, and it could even be triggered by a browser.
|
||
|
{% endhint %}
|
||
|
|
||
|
The only **difference** between **CL.0** and **H2.0** is that the second one is using **HTTP2** (which has an implicit content-length header) but the **backend isn't using that either**.
|
||
|
|
||
|
## Client-Side Desync
|
||
|
|
||
|
Traditional desync attacks **poison** the **connection** between a **front-end and back-end** server, and are therefore impossible on websites that don't use a front-end/back-end architecture. These are **server-side desync** from now on. Most **server-side desyncs** can only be triggered by a **custom HTTP client issuing a malformed request.**
|
||
|
|
||
|
The ability for a **browser to cause a desync** enables a whole new class of threat called **client-side desync** (CSD).\
|
||
|
A CSD attack starts with the **victim visiting the attacker's website**, which then makes their browser send **two cross-domain requests to the vulnerable website**. The **first** request is crafted to **desync the browser's connection** and make the **second request trigger** a harmful response, typically giving the attacker control of the victim's account.
|
||
|
|
||
|
### Detect
|
||
|
|
||
|
A CSD vector is a HTTP request with **two** **key** properties.
|
||
|
|
||
|
First, the **server must ignore the request's Content-Length (CL)**. This typically happens because the request either **triggered a server error**, or the server simply **wasn't expecting a POST request** to the chosen endpoint. Try targeting **static files** and **server-level redirects**, and triggering errors via **overlong-URLs**, and **semi-malformed** ones like /%2e%2e.
|
||
|
|
||
|
Secondly, the request must be **triggerable in a web-browser cross-domain**. Browsers severely restrict control over cross-domain requests, so you have limited control over headers, and if your request has a body you'll need to use the HTTP POST method. Ultimately you only **control** the **URL**, plus a few odds and ends like the **Referer header**, the **body**, and **latter part of the Content-Type.**
|
||
|
|
||
|
#### CL ignore testing
|
||
|
|
||
|
The way to test this missconfig is to **send 2 requests and smuggle one** in the **middle**. If the **smuggled** connection **affected** the response of the **second** **request**, it means that it's **vulnerable**:
|
||
|
|
||
|
![](<../../.gitbook/assets/image (1).png>)
|
||
|
|
||
|
{% hint style="warning" %}
|
||
|
Note that you **cannot** test this vuln by just sending a **Content-Length bigger** than the one sent and **looking for a timeout** because some servers **respond** even if they **didn't receive the whole body**.
|
||
|
{% endhint %}
|
||
|
|
||
|
It's important to note whether the **target website supports HTTP**/2. CSD attacks typically exploit HTTP/1.1 connection reuse and web **browsers prefer to use HTTP/2** whenever possible, so if the target **website supports HTTP/2 your attacks are unlikely to work**. There's one **exception**; some **forward proxies don't support HTTP/2** so you can exploit anyone using them. This includes corporate proxies, certain intrusive VPNs and even some security tools.
|
||
|
|
||
|
### Confirm
|
||
|
|
||
|
First, select a site to launch the attack from. This site must be **accessed over HTTPS** and located on a **different domain than the target**.
|
||
|
|
||
|
Next, ensure that you **don't have a proxy configured**, then browse to your attack site. Open the **developer tools** and switch to the **Network tab**. To help with debugging potential issues later, I recommend making the following adjustments:
|
||
|
|
||
|
* Select the **"Preserve log"** checkbox.
|
||
|
* Right-click on the column headers and **enable the "Connection ID" column**.
|
||
|
|
||
|
Switch to the developer console and execute JavaScript to replicate your attack sequence using fetch(). This may look something like:
|
||
|
|
||
|
```javascript
|
||
|
fetch('https://example.com/', {
|
||
|
method: 'POST',
|
||
|
body: "GET /hopefully404 HTTP/1.1\r\nX: Y", // malicious prefix
|
||
|
mode: 'no-cors', // ensure connection ID is visible
|
||
|
credentials: 'include' // poison 'with-cookies' pool
|
||
|
}).then(() => {
|
||
|
location = 'https://example.com/' // use the poisoned connection
|
||
|
})
|
||
|
```
|
||
|
|
||
|
I've set the fetch mode **'no-cors'** to ensure Chrome **displays the connection ID** in the Network tab. I've also set **credentials: 'include'** as Chrome has [**two separate connection pools**](https://www.chromium.org/developers/design-documents/network-stack/preconnect) - one for requests with cookies and one for requests without. You'll usually want to exploit **navigations**, and those **use the 'with-cookies' pool**, so it's worth getting into the habit of always poisoning that pool.
|
||
|
|
||
|
When you execute this, you should see **two requests** in the Network tab with the **same connection ID**, and the **second** one should trigger a **404**:
|
||
|
|
||
|
![](../../.gitbook/assets/image.png)
|
||
|
|
||
|
If this works as expected, congratulations - you've found yourself a client-side desync!
|
||
|
|
||
|
### Exploitation - Store
|
||
|
|
||
|
One option is to identify functionality on the target site that lets you **store text data**, and craft the prefix so that your victim's cookies, authentication headers, or password end up being **stored somewhere you can retrieve them**. This attack flow works [almost identically to server-side request smuggling](https://portswigger.net/web-security/request-smuggling/exploiting#capturing-other-users-requests), so I won't dwell on it.
|
||
|
|
||
|
### Exploitation - **Chain\&pivot**
|
||
|
|
||
|
Under normal circumstances, many classes of **server-side attack** can only be launched by an attacker with direct access to the target website as they **rely on HTTP requests that browsers refuse to send**, like **tampering** with **HTTP headers** - web cache poisoning, most server-side request smuggling, host-header attacks, User-Agent based [SQLi](https://portswigger.net/web-security/sql-injection), CSRF JSON Content-type and numerous others.
|
||
|
|
||
|
The simplest path to a successful attack came from two key techniques usually used for server-side desync attacks: [**JavaScript resource poisoning via Host-header redirects**](https://portswigger.net/web-security/request-smuggling/exploiting#using-http-request-smuggling-to-turn-an-on-site-redirect-into-an-open-redirect), and using the [**HEAD method**](https://portswigger.net/web-security/request-smuggling/advanced/request-tunnelling#non-blind-request-tunnelling-using-head) to splice together a response with harmful HTML. Both techniques needed to be **adapted** to overcome some novel challenges associated with operating in the **victim's browser**.
|
||
|
|
||
|
## Exploit Examples
|
||
|
|
||
|
### Stacked HEAD example
|
||
|
|
||
|
* Coloured exploit
|
||
|
|
||
|
![](<../../.gitbook/assets/image (2).png>)
|
||
|
|
||
|
* **JS exploit**
|
||
|
|
||
|
```javascript
|
||
|
fetch('https://www.capitalone.ca/assets', {
|
||
|
method: 'POST',
|
||
|
|
||
|
// use a cache-buster to delay the response
|
||
|
body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
|
||
|
credentials: 'include',
|
||
|
mode: 'cors' // throw an error instead of following redirect
|
||
|
}).catch(() => {
|
||
|
location = 'https://www.capitalone.ca/'
|
||
|
})va
|
||
|
```
|
||
|
|
||
|
Explanation:
|
||
|
|
||
|
* **Abuse of CL.0** in /assets (it redirects to /assets/ and doesn't check the CL)
|
||
|
* **Smuggle** a **HEAD** request (because HEAD responses still contains a content-length)
|
||
|
* **Smuggle** a **GET** request whose **content** is going be **reflected** in the response with the payload.
|
||
|
* Because of the **content-length of the HEAD** req, the **response** of this request will be the **body of the HEAD req**
|
||
|
* Set **cors mode**. Normally this isn't done, but in this case the **response** of the server to de **initial** **POST** is a **redirect** that if **followed** the **exploit won't work**. Therefore, **cors mode** is used to **trigger** an **error** and **redirect** the victim with the **`catch`**.
|
||
|
|
||
|
### **Host header redirect + client-side cache poisoning**
|
||
|
|
||
|
## **References**
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><strong>Support HackTricks and get benefits!</strong></summary>
|
||
|
|
||
|
Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||
|
|
||
|
Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
|
||
|
Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
|
|
||
|
**Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||
|
|
||
|
**Share your hacking tricks submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**
|
||
|
|
||
|
</details>
|