the_open_door
café
ESEN
← Volver al índice

Python Library Hijacking

Arsenal completo de comandos en la cheatsheet

Cuando un script de Python hace import modulo, el intérprete busca ese módulo recorriendo en orden la lista sys.path. El primer directorio que contenga un modulo.py gana. Si un atacante puede colocar un fichero con el nombre del módulo en un directorio que se busque antes que la librería estándar, el script importará el módulo del atacante en su lugar. Eso es un secuestro de librería.

Por qué es escalada. El abuso es valioso cuando el script vulnerable se ejecuta con privilegios (por un sudo, un cron de root, un SUID). El módulo secuestrado se importa y su código de nivel superior se ejecuta en ese contexto privilegiado, antes incluso de que el script use nada del módulo.

Las dos vías para ganar la búsqueda.

  1. Directorio del script escribible. sys.path[0] es el directorio del propio script. Si podemos escribir ahí, un shutil.py nuestro se importa antes que el de la librería estándar.
  2. PYTHONPATH. Esta variable de entorno se antepone a sys.path. Si un sudo permite conservarla (etiqueta SETENV en sudoers), apuntarla a un directorio escribible basta:
# el script privilegiado hace:  from shutil import make_archive
cat > /tmp/shutil.py <<'EOF'
import os
os.system('chmod u+s /bin/bash')
EOF

sudo PYTHONPATH=/tmp /opt/scripts/admin_tasks.sh   # se importa /tmp/shutil.py como root
bash -p                                            # shell con euid 0

Matiz. Al importar, Python ejecuta primero todo el cuerpo del módulo y solo después resuelve los nombres (make_archive). Aunque nuestro shutil.py no defina esa función y el script acabe con error, el código malicioso (el chmod) ya se ejecutó. El error posterior es irrelevante para el objetivo.

Por qué secure_path no salva. En el caso de sudo, secure_path reescribe el PATH de comandos, no la ruta de módulos de Python; por eso PYTHONPATH se cuela pese a un Defaults env_reset, si la regla lleva SETENV.

Cómo detectarlo. En el lado de sudo, la firma es un sudo PYTHONPATH=/tmp ... o sudo PYTHONPATH=<ruta> <script> en /var/log/auth.log: pasar PYTHONPATH por sudo es anómalo por sí mismo. En el host, auditd/Sysmon for Linux ven al intérprete privilegiado abriendo un .py desde una ruta escribible por usuarios (/tmp/shutil.py, el directorio del script) en vez de la librería estándar, y a continuación un chmod +s /bin/bash o un sh hijo del script. En reposo, un módulo con nombre de librería estándar (shutil.py, os.py, random.py) en un directorio de escritura de usuario o junto a un script privilegiado es el indicador.

Defensa. No conceder SETENV salvo necesidad real; en los scripts privilegiados, fijar sys.path de forma defensiva o usar rutas absolutas e intérpretes con -E/-I que ignoren el entorno. Relacionado con el abuso de SUID, el secuestro del PATH y el saneamiento del entorno antes de elevar privilegios.

Máquinas donde se aplica