Demostración en vídeo de este post
En el post anterior que puedes leer en el siguiente enlace, se han explicado las características de PyScript y los motivos por los que resulta un proyecto interesante, aunque aún en desarrollo y habría que ver cómo evoluciona en los próximos meses. Una de las limitaciones que tiene es que no parece contar con rutinas que permitan la carga de otras librerías diferentes a las que ya se encuentran definidas en el proyecto, algo que a la fecha de redactar este post no se encuentra documentado en ningún sitio en el proyecto. No obstante, el «core» de Python puede ser suficiente para las operaciones que se ejecutarían en el contexto de un navegador web. Precisamente con el core del lenguaje se pueden hacer muchas cosas, como por ejemplo realizar peticiones HTTP a sitios web o establecer conexiones TCP. Se trata de operaciones que habitualmente se llevan a cabo en procesos de pentesting con Python y por supuesto, se pueden probar con PyScript.
En este caso, no se cuenta con librerías como requests, pero se puede utilizar el módulo «urllib» y aunque son bien conocidas sus limitaciones puede valer para hacer las pruebas. El siguiente bloque de código se incluye en la etiqueta <py-script> para comprobar si efectivamente, es posible establecer una conexión.
Como cualquier petición HTTP, requiere el establecimiento de una conexión TCP con el host destino, en el caso de la imagen anterior es httbin.org. Al intentar ejecutar este código con PyScript, ocurre lo mismo que con un runtime de Pyodide en cualquier navegador web, es decir, el navegador impide el establecimiento de la conexión tal como se enseña en la siguiente imagen.
Esto ocurre al intentar ejecutar una petición HTTP pero, ¿qué pasa si se intenta establecer una conexión TCP directa? pues más o menos lo mismo como se verá a continuación.
Se utiliza el siguiente código, el cual corresponde al establecimiento de una bindshell con Python. Es una rutina sencilla que funciona sobre cualquier sistema operativo que tenga Python instalado y en dicho escenario, el puerto indicado se abrirá sin problema, pero en este caso es el navegador web es el que debe realizar dicha operación.
Aunque el código indicado anteriormente es correcto, la operación no se puede llevar a cabo debido a las restricciones que impone el navegador web sobre el runtime de Pyodide, es decir, que no será posible abrir el puerto aunque el usuario con el que se está ejecutando el proceso del navegador tenga permisos para hacerlo.
¿Con una reverse shell ocurre lo mismo? con la explicación anterior se puede deducir que sí, pero como siempre, se debe probar por si «hay suerte» y se consigue algún resultado positivo. El siguiente código representa el payload de una reverse shell en Python muy simple, utilizando unicamente los elementos disponibles en la librería estándar de Python.
Antes de ejecutar este script en la página HTML, sería necesario levantar el puerto 4444 en la máquina local, usar Netcat será suficiente y lo más sencillo. A continuación, tal como se puede comprobar en la siguiente imagen, se produce un error ya que en este caso, aunque se establece una conexión HTTP, el runtime de pyodide se encarga de realizar una petición WS (Web Sockets) al destino y evidentemente, Netcat no está preparado para tratar adecuadamente dicha petición, simplemente enseña por la terminal la petición HTTP y sus correspondientes cabeceras.
En este punto parece claro que si se desea establecer una reverse shell es necesario crear un servidor que admita peticiones con WebSockets y un cliente personalizado, algo que por supuesto, es posible hacer en Python con la librería «websockets». En el lado del servidor sería tan simple como instalar con «pip install websockets» y junto con el módulo «asyncio» levantar el handler correspondiente, sin embargo, también habría que crear el cliente, algo que con Javascript se puede hacer sin ningún problema ya que está preparado para ello, pero en este caso concreto la API core de Python no cuenta con librerías especificas para crear este cliente, es necesario instalar el paquete «websockets» y como ya se ha mencionado anteriormente, de momento PyScript no permite la instalación de otros módulos distintos a los que ya vienen incluidos en el propio proyecto.
En conclusión, aunque puede parecer se contará con todo el poder de Python al navegador web y será posible ejecutar rutinas complejas, con simplemente un fichero HTML, en la práctica no resulta tan sencillo. En este post se han enumerado algunas de las pruebas realizadas, sin embargo en el vídeo que tienes justo al inicio de este post también se explican algunas otras pruebas que han dado resultados muy parecidos. Como decía antes, hay que ver cómo evoluciona el proyecto y la aceptación que tiene por parte de los desarrolladores de Python, es posible que nos encontremos ante una alternativa interesante, pero desde luego aún es pronto para saberlo.
Un saludo y Happy Hack!
Adastra.