Demostración en vídeo de este post.
Hace unos días, Anaconda ha publicado en la PyCON2022 un proyecto en el que han estado trabajando últimamente y que finalmente, está publicado en su repositorio de GitHub, se trata de PyScript.
A grandes rasgos, es un proyecto que pretende la ejecución de código Python en el navegador, simplemente con una página HTML, de la misma manera que lo hace Javascript. Aunque la idea es interesante, de hecho no es novedosa. Se basa en otras tecnologías existentes como Pyodide y WASM (Web Assembly) pero aún así, ha sido uno de los proyectos más sonados en dicho evento, algo que ha despertado mi curiosidad y he querido documentarme un poco más para probarlo.
Después de fijarme un poco en código y leer la documentación que se encuentra actualmente disponible, da a entender que es posible llevar los scripts que se pueden ejecutar en una terminal con Python al navegador web, lo cual no es del todo cierto. En primer lugar, como se ha dicho anteriormente, depende de otras tecnologías como Pyodide que ya tiene sus limitaciones, además, si se pensaba que con este proyecto se puede sustituir a Javascript, nada más lejos: PyScript se basa en Javascript. De hecho, para poder utilizar la etiqueta <py-script> e incluir código Python es necesario descargar un fichero JS y opcionalmente una hoja de estilos CSS.
Si bien es cierto que hay muchas rutinas en Python que son compatibles y que se pueden ejecutar, hay otras que simplemente no tienen cabida cuando se lanzan en un navegador web, en primer lugar porque es el propio navegador el que se encarga de limitar lo que pueden hacer los scripts en su entorno, lo que tradicionalmente se conoce como «sandboxing» y que es ampliamente conocido por los desarrolladores de Javascript y en segundo lugar, porque el ecosistema de Python es simplemente enorme y hay librerías que no se podrán usar con PyScript dada su infraestructura.
En primer lugar, para entender cómo funciona este proyecto se observan las peticiones HTTP y los recursos que descarga PyScript cuando se ejecuta en el navegador web. Como era de esperarse, una de las primeras cosas que hace consiste en crear un runtime de pyodide, algo que durante las pruebas realizadas, ha tardado cerca de unos 3 segundos, lo cual para ser un entorno local puede parecer bastante y de hecho, se aprecia que la ejecución de instrucciones Python sencillas, sin ninguna complejidad y completamente inocuas, tardan en torno a los 4.5 segundos en ejecutarse y enseñar la salida correspondiente en el navegador. En la siguiente imagen se puede apreciar cada una de las peticiones HTTP que ejecuta la librería
Como se puede ver, el script descarga pyodide_py.tar, descomprime y ejecuta pyodide.asm.js. A continuación, descarga el fichero distutils.tar que contiene los WHL necesarios que ayudarán a tener un entorno de ejecución mínimo.
Durante las pruebas, se puede apreciar que su instalación no tiene ninguna dificultad, basta simplemente con descargar el script pyscript.js en el servidor web local o utilizar la versión que se encuentra disponible en GitHub y a continuación, solo hace falta utilizar la tag <script> en la página HTML. Se pueden incluir dichos elementos en un servidor web Apache o Nginx, por ejemplo, y funciona sin prácticamente ninguna configuración especial.
Esta página HTML se enseñará cuando se cargue el script de la imagen anterior y aunque en la documentación disponible del proyecto se indica que las pruebas solo se han realizado sobre Chrome, también funciona sobre Firefox sin problema.
El módulo «OS» en Python se encuentra disponible en diferentes versiones del lenguaje, tanto en la rama 2.7 como en la 3.x, por lo tanto tiene sentido que se ejecute con normalidad. Merece la pena comprobar qué error se produce si se intenta cargar una librería que no se encuentra disponible en el SDK de Python por defecto
Como se esperaba, se produce un error. Sin embargo en este punto es necesario preguntarse: ¿cómo puedo importar librerías en el entorno en cuanto se carga la página HTML? Bien, de momento lo que se encuentra disponible en la documentación oficial y los ejemplos disponibles, no aparece ninguna referencia a la instalación de librerías externas, como por ejemplo Scapy.
Es cierto que el proyecto incluye librerías habituales en el mundo de Data Science como es el caso de Numpy, Pandas o Matploitlib, las cuales se pueden cargar con la tag <py-env> pero no se aprecia ninguna referencia a la posibilidad de importar otras librerías externas.
Ahora, como cualquier pentester, una de las primeras cosas que se piensa es: ¿cómo se puede utilizar esta tecnología para conseguir algún tipo de «ventaja» desde el punto de vista de la seguridad ofensiva?. Lo primero que hay que tener en cuenta es que seguimos con el modelo cliente-servidor. Aunque Python sea muy potente, no deja de ser un entorno que depende de una plataforma base, ya sea el sistema operativo del servidor o el navegador de un cliente y por lo tanto, no tiene más remedio que «cumplir» con lo que dicta el entorno. Dicho esto, ya nos hacemos una idea de las fuertes restricciones que pueden haber en el contexto de ejecución de un navegador, lo mismo que ocurre con Javascript, sin embargo es algo que se analizará con más detenimiento en la segunda parte de este post.
Un saludo y Happy Hack!
Adastra.