Python es un lenguaje muy versátil que ha tomado mucha relevancia en los últimos años. Accesible para los desarrolladores menos experimentados, con cientos de librerías disponibles públicamente y siendo apoyado por una gran comunidad, la cantidad de desarrolladores que lo utilizan ha crecido considerablemente.

En este contexto, si bien Python no es uno de los lenguajes más tradicionales para desarrollar malware, ha sido adoptado por muchos cibercriminales para tal fin. Un ejemplo de esto es Machete, un backdoor utilizado con fines de espionaje dirigido principalmente a organizaciones gubernamentales de Latinoamérica.

Python es un lenguaje interpretado, esto quiere decir que su código puede ser ejecutado en cualquier plataforma para la cual exista un intérprete. A simple vista, esto podría parecer algo beneficioso para los cibercriminales, pero en verdad representa un obstáculo. Esto se debe a que para que el malware pueda ser ejecutado en la PC de la víctima, esta debe tener instalado dicho intérprete. Algunos sistemas operativos Linux traen una instalación de Python por defecto; sin embargo, esto no es así en los sistemas operativos Windows, que son justamente los más apuntados por los cibercriminales.

Por lo tanto, los desarrolladores de malware necesitan una alternativa para poder ejecutar su código malicioso escrito en Python en los sistemas operativos Windows, siendo la solución elegida para este inconveniente la utilización de software de empaquetado; capaz de tomar un script .py y generar un ejecutable .exe.

Al ser autocontenido, este ejecutable contendrá toda la funcionalidad maliciosa del script y podrá ser ejecutado sin la necesidad de tener instalado el intérprete de Python. En este sentido, los software más utilizados por los cibercriminales para este fin son:

  • Py2exe
  • PyInstaller

Cabe destacar que ambos programas son legítimos y son utilizados principalmente por la comunidad de desarrolladores Python que quieren poder distribuir fácilmente sus desarrollos. Es decir, existe mucho software benigno que también utiliza estos programas.

¿Cómo funcionan los compiladores?

El objetivo de estos programas es tomar un archivo .py y generar un ejecutable .exe capaz de funcionar en Windows sin inconvenientes. Para lograr esto, el primer paso que realizan es compilar el script de Python .py a bytecode de Python .pyc. Este código .pyc puede ser ejecutado por los intérpretes de Python. Luego, el software también empaquetará dentro del ejecutable .exe el código correspondiente a un intérprete que funcione en Windows. Por último, el script .py podría utilizar dependencias y librerías específicas, las cuales también deberán ser compiladas e incluidas en el .exe resultante.

Proceso de compilación y empaquetado realizado por herramientas como Py2Exe y PyInstaller

Sin embargo, para poder analizar malware que ha sido sometido a este proceso, será necesario realizar el proceso inverso. Se comenzará extrayendo el script compilado .pyc contenido dentro del ejecutable malicioso con algún software como unpy2exe o pyinsxtractor. Luego, se procederá a descompilar el archivo .pyc extraído mediante el uso de algún descompilador como Uncompyle6. De esta manera obtendremos el script malicioso (.py) en texto plano, pudiendo así analizarlo de forma más cómoda y eficiente.

Proceso de extracción y descompilado junto con las herramientas que pueden ser utilizadas en cada paso

Software necesario para descompilar

unpy2exe: Se trata de un script de Python que puede ser descargado desde su repositorio de github o instalado mediante el comando “pip install unpy2exe”. Como consideración adicional, si se quiere utilizar este script en un .exe generado a partir de código Python 2.x, se lo deberá ejecutar con Python 2.x. Por el contrario, si se lo quiere utilizar en un .exe generado a partir de código Python 3.x, se lo deberá ejecutar con Python 3.x.

pyinstxtractor: Se trata de un script de Python y puede ser descargado desde su repositorio de github. Para ser ejecutado requiere una version de Python mayor o igual a la 2.7 y, al igual que para unpy2exe, es recomendable ejecutarlo con la misma version de Python que la utilizada por el ejecutable que se quiere analizar.

Uncompyle6: Desarrollado en Python, este script es capaz de descompilar archivos .pyc para la mayoría de las versiones. Se lo puede descargar desde su sitio oficial o mediante el comando “pip install uncompyle6”. En caso de tener algún inconveniente descompilando las versiones más recientes de Python +3.7, existe otro proyecto derivado de este llamado Decompile3, el cual está focalizado en estas versiones.

Descompilando malware empaquetado con Py2exe

El proceso para descompilar malware empaquetado con Py2exe es muy sencillo. Comenzaremos ejecutando unpy2exe pasándole como parámetro el ejecutable malicioso que queremos descompilar y este se encargará de extraer el código compilado.

Ejecución de unpy2exe para extraer el archivo .pyc de example.exe

Una vez ejecutado el comando, podremos observar que se ha creado un nuevo archivo con extensión .pyc. Este archivo es el bytecode correspondiente al código Python compilado.

Archivo .pyc extraído del ejecutable por la herramienta unpy2exe

En este punto resta descompilar el archivo .pyc para obtener el código fuente del malware en texto plano. Para realizar esto necesitaremos ejecutar uncompyle6 pasándole como parámetro el archivo .pyc extraído en el paso anterior.

Código obtenido al descompilar el archivo .pyc extraído en el paso anterior con Uncompyle6

Como podemos observar, el código Python del ejecutable malicioso fue extraído y descompilado exitosamente.

Descompilando malware empaquetado con PyInstaller

El proceso para descompilar malware empaquetado con PyInstaller es similar al de los ejecutables empaquetados por Py2exe, pero requiere algunos pasos adicionales.

Comenzaremos ejecutando pyinstxtractor.py pasándole como parametro el ejecutable malicioso.

Ejecución de pyinstxtractor para extraer el archivo .pyc de example.exe. Puede observarse el “Possible entry point”, entre otra información sobre el ejecutable

Aquí podemos observar que pyinstxtractor extrajo satisfactoriamente los archivos .pyc del ejecutable malicioso. Entre la información que nos brinda hay un ítem que dice “Possible entry point” y el valor de este será de utilidad en el siguiente paso.

Nos dirigiremos a la carpeta “*nombre del ejecutable*.exe_extracted”, la cual fue creada por pyinstxtractor, y allí podremos ver todos los archivos extraídos.

Archivos extraídos del ejecutable por pyinstxtractor. Puede observarse un archivo sin extensión cuyo nombre se corresponde con el “possible entry point”

Entre todos los archivos extraídos podemos observar uno sin extensión cuyo nombre es igual al mencionado en el ítem “Possible entry point” por pyinstxtractor. Generalmente, este archivo contendrá el bytecode correspondiente al código principal del ejecutable malicioso.

Sin embargo, habrá que modificar el header de este archivo para agregar el magic number correspondiente al formato .pyc. Para hacer esto lo abriremos con el software HxD.

Archivo abierto con HxD para poder observar su contenido en formato binario. Aquí puede observarse que falta el magic number correspondiente al header del archivo .pyc

Aquí podemos ver el contenido del archivo y que falta el magic number al comienzo de este. Por lo tanto, se deberá agregar tal valor en la primera posición del archivo para completar el header. El magic number puede variar según la versión de Python utilizada por ese bytecode y el mismo puede ser copiado y pegado a partir de otro de los archivos .pyc extraídos por pyinstxtractor (para asegurarnos que estamos agregando el correcto).

Archivo .pyc con el header ya corregido y completo

Una vez realizado esto, se debe guardar el archivo y cambiar su extensión a .pyc para proceder a descompilarlo utilizando Uncompyle6. Simplemente basta con ejecutarlo pasándole como parámetro el archivo .pyc recién editado.

Código obtenido al descompilar con Uncompyle6 el archivo .pyc reparado en el paso anterior

Como podemos observar, el código Python del ejecutable malicioso fue extraído y descompilado exitosamente.

Post-descompilación

Los cibercriminales más experimentados suelen ser conscientes de que generalmente es posible obtener el código fuente de las amenazas desarrolladas con lenguajes interpretados o de scripting (JS, PS, VBS, Python, etc). Por este motivo, con el fin de dificultar las tareas de los analistas, muchos de ellos optan por ofuscar el código antes de compilarlo. Luego, si al finalizar todos estos pasos nos encontramos con un código incomprensible, será necesario analizarlo más cuidadosamente en búsqueda de patrones que se correspondan con la sintaxis del lenguaje para revelar la lógica oculta del mismo.

Quizás te interese: Estructuras de control de flujo en código Assembly