Cómo proteger las aplicaciones Python de la inyección de scripts maliciosos





Las aplicaciones de Python utilizan muchos scripts. Esto es lo que usan los ciberdelincuentes para ponernos un "cerdo", donde menos esperamos verlo.



Una de las ventajas de Python es su facilidad de uso: para ejecutar un script, solo necesita guardarlo en un .pyarchivo y ejecutar el comando python (, python my_file.py). También es fácil romper nuestro archivo, como los módulos, my_app.pyy my_lib.pycontinuar conectando los módulos para usar el diseño import...from: import my_lib from my_app.py.



Sin embargo, esta simplicidad y ligereza tiene una desventaja: cuanto más fácil sea para usted ejecutar código desde diferentes ubicaciones, más oportunidades tendrá un atacante de interferir.



Coloque el código Python en un lugar seguro



El modelo de seguridad de Python se basa en tres principios:



  1. , sys.path , .
  2. , (main), sys.path.
  3. python , -c -m.


Suponga que desea ejecutar una aplicación Python que se ha instalado "correctamente" en su máquina. En este caso, la única ubicación (además de la carpeta con Python instalado) que se agregará automáticamente a su sys.path por defecto es la carpeta con el script principal o el archivo ejecutable.



Por ejemplo, si pipestá dentro /usr/biny ejecuta /usr/bin/pip, sys.pathsolo se agregará /usr/bin. Solo root puede escribir archivos en / usr / bin, por lo que esta ubicación se considera segura.



Sin embargo, el acuerdo nos obligan a hacerlo: /path/to/python -m pip. Esto evita problemas $PATHy conflictos con la documentación de Windows.



En general, todo estará bien de todos modos, si solo usted tiene la autoridad para agregar y editar archivos en los directorios desde los cuales Python toma los módulos para importar.



Carpeta de descargas: ubicación vulnerable



Hay muchas formas de obligar a los navegadores (y a veces a otro software) a colocar archivos con nombres arbitrarios en la carpeta Descargas sin el conocimiento del usuario. Esta vulnerabilidad es aprovechada por un ataque de suplantación de DLL. En nuestro caso, hablaremos de scripts de Python, pero la idea es la misma.



Los navegadores se han vuelto más serios al respecto y poco a poco están tratando de asegurarse de que los sitios visitados por el usuario no puedan volcar archivos en secreto en su carpeta de Descargas.



Sin embargo, este problema será muy difícil de resolver por completo: por ejemplo, todavía está disponible una opción Content-Disposition HTTP header’s filename*que permite a los sitios elegir un directorio para colocar los archivos descargados.



Cómo funciona el ataque



Ejecutar la instalación: python -m pip. Está descargando un paquete de Python de un sitio web completamente confiable, que por alguna razón, sin embargo, ofrece descargas directas en lugar de PyPI. Tal vez sea algún tipo de versión interna, tal vez sea una versión preliminar, no hay diferencia. Entonces va para ti totally-legit-package.whl:



~$ cd Downloads
~/Downloads$ python -m pip install ./totally-legit-package.whl


Parece estar bien, pero resulta que hace dos semanas, en un sitio completamente diferente al que visitaste, algunos JavaScript XSS descargaron pip.py con malware en tu carpeta de Descargas sin tu conocimiento.



¡Auge!



¡Choque!



~$ mkdir attacker_dir
~$ cd attacker_dir
~/attacker_dir$ echo 'print("lol ur pwnt")' > pip.py
~/attacker_dir$ python -m pip install requests
lol ur pwnt


Comportamiento extraño PYTHONPATH



Algunos párrafos arriba, escribí:



la única ubicación (que no sea la carpeta instalada de Python) que se agregará automáticamente a su sys.path de forma predeterminada es el script principal o la carpeta ejecutable.


Entonces, ¿qué hace aquí "predeterminado"? ¿Qué otras ubicaciones puedes agregar?



En principio, $PYTHONPATHcualquier cosa se puede escribir en una variable de entorno . Pero no escribirías tu ubicación actual $PYTHONPATH, ¿verdad?



Desafortunadamente, existe una situación en la que podría hacer esto accidentalmente.

Dibujemos una aplicación de Python "vulnerable" a modo de ilustración:



# tool.py
try:
    import optional_extra
except ImportError:
    print("extra not found, that's fine")


Creemos 2 directorios: install_diry attacker_dir. Vamos a fallar install_dir, luego ejecutar cd attacker_diry colocar nuestro código malicioso allí por el nombre tool.py:



# optional_extra.py
print("lol ur pwnt")


Todo lo que queda es ejecutarlo:



~/attacker_dir$ python ../install_dir/tool.py
extra not found, that's fine


Hasta ahora todo va bien.



Pero ahora veremos un error común. Mucha gente recomienda agregar una cosa$PYTHONPATH más :



export PYTHONPATH="/new/useful/stuff:$PYTHONPATH";


A primera vista, esto tiene sentido: si agrega el proyecto X a $PYTHONPATH, tal vez, de acuerdo con el proyecto Y, también se agregó algo allí, o tal vez no; en cualquier caso, no querrá sobrescribir los cambios asociados con otros proyectos. Esto es especialmente importante cuando está escribiendo documentación que muchas personas usarán.



Y ahora nos enfrentamos a un comportamiento "extraño" $PYTHONPATH. Si $PYTHONPATHestaba vacío o no se instaló antes del primer lanzamiento , aparecerá una línea vacía en él, que será reconocida como el directorio actual. Revisemos esto:



~/attacker_dir$ export PYTHONPATH="/a/perfectly/safe/place:$PYTHONPATH";
~/attacker_dir$ python ../install_dir/tool.py
lol ur pwnt


Para mayor seguridad, hagámoslo $PYTHONPATHvacío e intentemos de nuevo:



~/attacker_dir$ export PYTHONPATH="";
~/attacker_dir$ python ../install_dir/tool.py
lol ur pwnt


¡Exactamente, no es seguro en absoluto!



Y una cosa más: resulta que las situaciones en las que una variable está $PYTHONPATHvacía y cuando una variable $PYTHONPATHno está configurada son dos historias diferentes:



os.environ.get("PYTHONPATH") == ""</code>  <code>os.environ.get("PYTHONPATH") == None.


Si desea asegurarse de limpiar $PYTHONPATH, use el comando unset:



~/attacker_dir$ python ../install_dir/tool.py
extra not found, that's fine


En general, escribir en una variable $PYTHONPATHera la forma más común de configurar el entorno para ejecutar aplicaciones Python. Afortunadamente, pasó de moda con la llegada de los entornos virtuales y virtualenv. Si tiene una configuración antigua en la que escribe algo $PYTHONPATH, pero puede prescindir de ella, ahora es el momento de eliminarla.



Si por alguna razón no puede hacer esto, use un truco de vida :



export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}new_entry_1"
export PYTHONPATH="${PYTHONPATH:+${PYTHONPATH}:}new_entry_2"


En bash y zsh, esto dará esta salida:



$ echo "${PYTHONPATH}"
new_entry_1:new_entry_2


Bueno, ahora no hay dos puntos adicionales ni entradas vacías.



Finalmente, si todavía está trabajando con $PYTHONPATH, use rutas absolutas. ¡Es siempre!



El problema es mucho mas serio



Hay varias opciones para el desarrollo peligroso de eventos asociados con el lanzamiento de archivos Python desde la carpeta Descargas:



  • Ejecutando Python ~/Downloads/anything.py(incluso si anything.pyes seguro en sí mismo ). Su carpeta de Descargas se agregará a sys.path.
  • Una vez iniciado, jupyter notebook ~/Downloads/anything.ipynbtambién se agregará la carpeta Descargas sys.path.


Por lo tanto, es mejor eliminar los archivos .py y .ipynb de esta carpeta antes de ejecutar.



La ejecución cd Downloadsy el lanzamiento posterior python -ccon instrucciones importdentro o el lanzamiento interactivo pythoncon importación posterior no resuelven completamente el problema si los archivos importados están en la carpeta Descargas.



Desafortunadamente, esta ~/Downloads/no es la única ubicación que puede contener archivos maliciosos. Por ejemplo, si está administrando un servidor en el que los usuarios pueden cargar archivos, asegúrese de que nada ni nadie pueda iniciar cd public_uploadsantes de ejecutar el comando python.



En este caso, podría valer la pena considerar que el código que maneja la carga de archivos se agrega a sus nombres .uploaded. Esto ayudará a evitar la ejecución de un script no planificado .py.



Precauciones



Si tiene aplicaciones escritas en Python que desea usar mientras está en su carpeta de Descargas, establezca una regla para ingresar la ruta al script ( / path / to / venv / bin / pip), no al módulo ( / path / to / venv / bin / python -m pip).



En general, simplemente evite usar Descargas como su directorio de trabajo actual y mueva cualquier script que desee usar a una ubicación más adecuada antes de comenzar.



Es importante comprender de dónde obtiene Python el código que ejecutará. ¡Dejar que alguien ejecute incluso una línea del script de Python de la izquierda equivale a darle el control total de su computadora!



Por necesidad



Al leer un artículo de este tipo con "consejos y trucos para la vida" sobre seguridad, es muy fácil suponer que el autor es muy inteligente, conoce un montón de pequeñas cosas que otros deberían saber y piensa constantemente en ello. Pero, de hecho, esto no es del todo cierto. Lo explicaré.



A lo largo de los años, he observado con envidiable frecuencia cómo los usuarios no entendían de dónde descargaba el código Python. Por ejemplo, la gente pone su primer programa usando Twisted en un archivo llamado twisted.py. ¡Pero en este caso, la importación de la biblioteca es simplemente imposible!



Los ciberdelincuentes se regocijarían mucho más si pudieran atacar con éxito a los administradores o desarrolladores de sistemas. Si piratea a un usuario, obtendrás los datos de ese usuario. Pero si piratea a un administrador o desarrollador y lo hace bien, entonces puede obtener acceso a miles de usuarios cuyos sistemas están bajo el control de un administrador, o incluso a millones de usuarios que utilizan software de desarrollador.



Por lo tanto, "tener cuidado todo el tiempo" no es una receta confiable. Los profesionales de TI que son responsables de productos con un gran número de usuarios deben ser más cuidadosos. Como mínimo, deberían estar informados sobre las peculiaridades del trabajo de determinadas herramientas de desarrollo.



Error o característica ...



Nada de lo que he descrito anteriormente es en realidad un "error" o una "vulnerabilidad" real. No creo que los desarrolladores de Python o Jupyter hayan hecho nada por error. El sistema funciona de la forma en que está diseñado y eso probablemente se pueda explicar. Personalmente, no tengo idea de cómo se puede cambiar algo en él sin reducir el poder de Python por el que lo valoramos tanto.



Uno de mis inventos favoritos es SawStop, un sistema de seguridad único para sierras de mesa. Le permite detener la rotación de la hoja de sierra en 5 milisegundos, al entrar en contacto con el cuerpo humano. Antes de la invención de este sistema, las sierras de mesa eran herramientas extremadamente peligrosas, pero desempeñaban una función de producción importante. Se han hecho muchas cosas muy útiles e importantes con las sierras de mesa. Sin embargo, también es cierto que las sierras de mesa representaron una proporción desproporcionada de accidentes en los talleres de carpintería. SawStop ahora ahorra muchos dedos cada año.



Por lo tanto, al detallar las complejidades de las secuencias de comandos de Python, también espero reflexionar un poco sobre algunos ingenieros de seguridad. ¿Se puede hacer que SawStop ejecute código arbitrario para intérpretes interactivos? ¿Qué solución podría prevenir algunos de los problemas anteriores?



Manténganse a salvo amigos.






Publicidad



VDSina ofrece servidores seguros en Linux o Windows: elija uno de los sistemas operativos preinstalados o instálelo desde su imagen.






All Articles