Python Library Hijacking for Privilege Escalation
When root runs a Python script that imports a module, Python searches sys.path directories in order. If you can write to any directory that appears before the legitimate module location, or inject a path via PYTHONPATH, your malicious module loads instead of the real one. No exploit required — pure misconfiguration.
Prerequisites Check
Confirm Python is present and check the version:
Check if a root cron job or sudo rule runs Python scripts:
Check Python's default sys.path to understand the search order:
Technique 1 — Writable sys.path Directory
If any directory in Python's sys.path that appears before the module's real location is writable, plant a fake module there.
Step 1 — Find the target script's imports:
Step 2 — Find where the real module lives:
Step 3 — Find writable directories earlier in sys.path:
If /tmp or a custom application directory appears before /usr/lib/python3.10/, you can plant there.
Step 4 — Plant the malicious module:
/writable/path/hashlib.py
import os
import subprocess
# Execute payload
os.setuid(0)
os.setgid(0)
subprocess.run(["/bin/bash", "-p"])
# Import the real module to avoid breaking the script
import importlib
import sys
del sys.modules['hashlib']
sys.path.insert(0, '/usr/lib/python3.10')
import hashlib
Technique 2 — PYTHONPATH via sudo env_keep
If PYTHONPATH is preserved by sudo, you can inject any directory into Python's search path before system libraries:
If present:
/tmp/subprocess.py
import os
def run(*args, **kwargs):
os.setuid(0)
os.setgid(0)
os.system("/bin/bash -p")
Choose a module that the script imports but that is generic enough to work — os, subprocess, sys, json. Be aware that breaking os or subprocess may crash the script before executing your payload. Chain carefully — execute the payload first, then optionally import the real module.
For the full sudo env_keep technique see https://hackindex.io/platforms/linux/privilege-escalation/ld-preload-environment-hijacking.
Technique 3 — Writable .pth File in site-packages
.pth files in Python's site-packages directory are executed at Python startup — each line is either added to sys.path or executed if it starts with import. A writable .pth file in site-packages is code execution on every Python invocation.
Find writable .pth files:
If writable, append an import statement:
This fires every time Python starts — including when root runs any Python script.
Technique 4 — Writable Script Itself
If you can write to the Python script that root runs, inject code directly at the top:
Wait for cron or the service to trigger the script.
Technique 5 — Writable Package Directory
If the entire site-packages directory is writable, create a new module with a name matching an import in the target script:
If writable and the script imports 'requests'
/usr/local/lib/python3.10/site-packages/requests.py
import os
os.setuid(0)
os.setgid(0)
os.system("/bin/bash -p")
Finding the Right Module to Hijack
Choose a module that:
Is imported early in the script (before any privilege checks)
Is not a built-in (built-ins cannot be overridden this way)
Is not critical to spawning your shell (avoid hijacking
osif you need it in your payload)
Safe targets: requests, json, hashlib, base64, random, application-specific modules.
Risky targets: os, sys, subprocess — be careful to re-import the real version after your payload runs.
needrestart PYTHONPATH Hijack
needrestart runs as root after package operations to decide which services need restarting. Older versions inspect running processes by launching the Python interpreter they reference, and that launch honours the PYTHONPATH taken from the inspected process environment. A local user who runs any process with a crafted PYTHONPATH pointing at a writable directory gets a module from that directory imported and executed as root the next time needrestart runs. needrestart fires automatically after apt install / apt upgrade on Debian and Ubuntu, so the trigger is often reachable through a sudo-allowed apt command or a scheduled update.
needrestart 3.5
A version below 3.8 is vulnerable. The mechanism is the module-search abuse from this page applied to a root-run interpreter: drop a malicious module in an attacker-controlled directory, export PYTHONPATH to that directory inside a long-running process, and let needrestart import it as root. The public exploit automates building the module, the process, and the trigger; on a host where you can invoke needrestart through sudo apt, the chain is reliable.
The module name must match one the interpreter imports while needrestart scans the process. Qualys documented this alongside related needrestart issues in November 2024; their advisory lists the exact module the interpreter loads on each distribution.
References
-
Qualys needrestart Advisorywww.qualys.com/2024/11/19/needrestart/needrestart.txt (opens in new tab)
needrestart local root issues disclosed November 2024.
-
Python Documentation Search order for module imports and path manipulation.
-
Python Documentation .pth file processing and site-packages path injection.
Was this helpful?
Your feedback helps improve this page.