La gran mayoría de los programas maliciosos graves de los últimos 30 años estaban escritos en lenguaje ensamblador o en lenguajes compilados como C, C++ y Delphi. En la última década, sin embargo, las aplicaciones de este tipo se han diversificado cada vez más y, en consecuencia, se escriben cada vez más en lenguajes interpretados como Python.
Un mínimo nivel de exigencia, la facilidad de uso, la alta velocidad de desarrollo y la enorme colección de bibliotecas han hecho que Python resulte atractivo para la mayoría de los programadores, incluidos los desarrolladores de malware. Python se está convirtiendo en una herramienta cada vez más favorita a la hora de crear utilidades para troyanos, exploits, robo de información y similares. A medida que la popularidad de Python sigue creciendo de forma constante y el umbral de ingreso a la cultura del malware escrito en C sigue siendo muy alto, está claro que Python se utilizará cada vez más en los ciberataques, incluyendo la escritura de troyanos de todo tipo.
Tabla de Contenido
Los Tiempos Cambian: Python cada vez más Popular
En comparación con un lenguaje compilado estándar (como C), hay una serie de retos a la hora de escribir malware en Python. En primer lugar, Python debe estar instalado en el sistema operativo para poder interpretar y ejecutar el código. Sin embargo, como se demostrará más adelante, las aplicaciones escritas en Python pueden convertirse fácilmente en un ejecutable normal utilizando varios métodos.
En segundo lugar, el malware escrito en Python suele ser de gran tamaño, consume mucha memoria y, en consecuencia, requiere más recursos informáticos. Por otro lado, el malware grave, que puede encontrarse en el mundo moderno online, suele ser pequeño, discreto, consume poca memoria y utiliza una potencia de procesamiento limitada. Una muestra compilada escrita en C puede ocupar unos 200 KB, mientras que una instancia comparable escrita en Python, una vez convertida en un archivo ejecutable, puede ocupar unos 20 MB. Así, cuando se utilizan lenguajes interpretados, se consumen muchos más recursos de CPU y RAM.
Sin embargo, a 2021 y en adelante, tanto la tecnología digital como la de la información han avanzado mucho. Cada día Internet es más rápido, los ordenadores tienen más RAM y discos duros más grandes, los procesadores son más eficientes, etc. En consecuencia, Python también es cada vez más popular y viene preinstalado en macOS y en la mayoría de las distribuciones de linux.
¿No hay intérprete? No hay problema.
Microsoft Windows sigue siendo un objetivo principal para la mayoría de los ataques de malware, pero Python no está instalado por defecto en este sistema operativo. En consecuencia, un script malicioso debe convertirse en un ejecutable para ser más efectivo y difundido. Hay muchas maneras de “compilar” Python. Echemos un vistazo a las utilidades más populares.
Compilar Python para Windows
PyInstaller
PyInstaller puede convertir scripts de Python en archivos ejecutables independientes para Windows, Linux, macOS “congelando” (freezes) el código. Este método es uno de los más populares para convertir el código en formato ejecutable y es ampliamente utilizado tanto para fines legítimos como maliciosos.
Como ejemplo, vamos a crear un simple programa “¡Hola, EsGeeks!” y convertirlo en un archivo ejecutable utilizando PyInstaller:
$ cat hello.py
print('¡Hola, EsGeeks!')
$ pyinstaller --onefile hello.py
hello.py ...
...
$ ./dist/hello
¡Hola, EsGeeks!
$ file dist/hello
dist/hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=24675b0091f38f45c5c1c5484cf24925b439b164, stripped
$ du -sh dist/hello
6.7M dist/hello
Como resultado, tenemos un archivo portable y autosuficiente en formato ELF, que es el equivalente a un archivo EXE en Windows. Ahora vamos a crear y compilar el mismo programa en C para comparar:
$ cat hello.c
#include <stdio.h>
int main() {
printf("¡Hola, EsGeeks!");
}
$ gcc hello.c -o hello
$ ./hello
¡Hola, EsGeeks!
$ file hello
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f431152876212e6bffc721a316882ea6301e4bea, for GNU/Linux 3.2.0, not stripped
$ du -sh hello
20K hello
Observa la diferencia de tamaño de los archivos ejecutables resultantes: ¡6.7 MB (Python) y 20 KB (C)! Este es uno de los principales inconvenientes mencionados anteriormente en relación con el tamaño de los archivos y el uso de la memoria. Un ejecutable compilado a partir de código Python es mucho más grande ya que un intérprete (como un archivo de objetos compartidos en Linux) debe estar presente dentro del ejecutable para ejecutarse con éxito.
Py2exe
Py2exe es otro método popular para convertir el código en un ejecutable independiente en formato EXE. Al igual que con PyInstaller, un intérprete viene con el código para crear un ejecutable portable. Aunque es probable que py2exe deje de usarse eventualmente ya que no soporta versiones posteriores a Python 3.4, ya que el código de bytes en CPython ha cambiado mucho en Python 3.6 y superiores.
Py2exe utiliza el paquete distutils y requiere un pequeño setup.py
para crear el ejecutable. Como en el ejemplo anterior, vamos a crear un simple programa “¡Hola, EsGeeks!” y compilarlo con py2exe:
> type hello.py
print('¡Hola, EsGeeks!')
> type setup.py
import py2exe
from distutils.core import setup
setup(
console=['hello.py'],
options={'py2exe': {'bundle_files': 1, 'compressed': True}},
zipfile=None
)
> python setup.py py2exe
...
> dist\hello.exe
¡Hola, EsGeeks!
El archivo hello.exe creado por py2exe
tiene un tamaño similar al de PyInstaller, con 6,77 MB.
Nuitka
pip install Nuitka
Nuitka es probablemente uno de los métodos más infravalorados y a la vez más avanzados para convertir código Python en un ejecutable. El código Python se traduce primero a código C, y luego se enlaza con la biblioteca libpython para ejecutar el código exactamente igual que en el caso de CPython. Nuitka es capaz de utilizar diferentes compiladores de C, incluyendo gcc, clang, MinGW64, Visual Studio 2019+ y clang-cl para convertir el código de Python a C.
Una vez más, creamos un sencillo programa “¡Hola, EsGeeks!” y lo compilamos con Nuitka:
$ cat hello.py
print('Hola, EsGeeks!')
$ nuitka3 hello.py
...
$ ./hello.bin
¡Hola, EsGeeks!
$ file hello.bin
hello.bin: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=117f1574ac88fbc25813789b0ce084de5ddd6efc, for GNU/Linux 3.2.0, stripped
$ du -sh hello.bin
536K hello.bin
Esta vez hemos conseguido crear un binario portable con un tamaño de 536 KB, que es mucho más pequeño que usando PyInstaller y py2exe. Para entender cómo hemos conseguido unos resultados tan impresionantes, veamos el contenido de la carpeta en la que se ha realizado la compilación:
$ cloc hello.build/
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 12 2313 741 7979
C/C++ Header 3 1 70 7
-------------------------------------------------------------------------------
SUM: 15 2317 811 8061
-------------------------------------------------------------------------------
Nuitka produjo más de 8.000 líneas de código C a partir de nuestro programa Python de 1 línea. La forma en que Nuitka funciona es que realmente traduce los módulos de Python en código C y luego utiliza libpython y archivos C estáticos propios para ejecutar de la misma manera que lo hace CPython.
El resultado parece muy decente y parece muy probable que Nuitka como “compilador de Python” siga evolucionando. Por ejemplo, puede haber características útiles adicionales, por ejemplo, para proteger contra la ingeniería inversa. Ya existen varias utilidades que pueden analizar fácilmente los archivos binarios compilados con PyInstaller y py2exe para recuperar el código fuente de Python. Sin embargo, si el ejecutable se crea con Nuitka y el código se convierte de Python a C, la tarea del ingeniería inversa se vuelve mucho más difícil.
Otras Utilidades
Una gran ayuda para el malware escrito en Python es el vasto ecosistema de paquetes y repositorios de código abierto. Prácticamente cualquier tarea que quieras implementar es probable que ya esté resuelta de una forma u otra con Python. En consecuencia, los autores de malware pueden encontrar funciones sencillas en la web, y probablemente no sea necesario escribir desde cero una funcionalidad más compleja.
Veamos tres ejemplos de herramientas sencillas, pero potentes:
- Ofuscación del código
- Cómo hacer capturas de pantalla
- Realización de peticiones web
Ejemplo 1: Ofuscación
Los autores de malware que utilizan Python tienen muchas librerías que pueden utilizar para ofuscar su código Python para dificultar la lectura del código, como por ejemplo: pyminifier y pyarmor.
https://github.com/liftoff/pyminifier
https://github.com/dashingsoft/pyarmor
pip install pyarmor
Aquí hay un pequeño ejemplo de cómo pyarmor puede ofuscar el código Python:
$ cat hello.py
print('¡Hola, EsGeeks!')
$ pyarmor obfuscate hello.py
...
$ cat dist/hello.py
from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52\x4d\x4f\x52\x00\x00\x03\x09\x00\x61\x0d\x0d\x0a\x06\x2b\xa0\x01\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\xd6\x00\x00\x00\x00\x00\x00\x18\x19\xe7\x6b\xd7\xc8\x04\x90\x30\xbc\x6c\x6c\x88\x68\xcc\x46\x94\x00\x00\x00\x00\x00\x00\x00\x00\x7c\xf8\xb8\x1d\x41\x1f\x59\x2e\xd6\x7c\x42\xb3\x11\xaf\x45\x81\x89\x5b\x19\xac\xd5\x00\x62\xdb\x00\xeb\xa8\x57\x46\xa8\x00\x58\x32\xa7\x4f\x52\xeb\xa5\xc9\xac\x43\x3f\x8d\xe7\x63\x2e\xe4\xe7\x4b\x79\x69\xe6\x4a\x44\x9c\xb1\xb1\xca\x3d\x19\x5b\x74\xb6\x18\xad\x29\x76\xce\x0f\x0d\x66\x28\x11\x1f\x05\x5c\x9d\x63\x7c\x47\x91\x58\xe2\xc1\x3b\x94\x87\x73\x8a\xec\xdc\x53\x7f\x11\xe8\x6c\x39\x5b\x10\x5e\x15\xd8\x28\x42\xd2\xc9\x04\x05\x20\xf8\x13\xc0\x7b\x9d\xf2\x5d\x41\x4a\x27\xca\xc5\xb6\xea\x53\xc8\xe4\xcb\x93\xe2\x18\x1b\x1c\x24\x0b\x09\x54\xf9\x2c\x87\xf1\x72\xe2\x11\xad\x5c\xb3\xc5\xed\x5e\xda\xab\x49\xb7\xcb\xbe\xa6\x87\x87\xbf\xf6\x19\xf8\x10\xbc\xda\x59\x9c\x50\x7e\x74\x1f\x77\x85\x08\x70\xbe\xe7\xb2\xfc\xb8\xdf\x8b\xd9\x6f\x15\x80\x45\xae\x06\x82\xf1\x3b\x33\x1b\xaf\x90\x26\x1a\xd9\x6b\x5f\xe5\xe3\xf6\xe0\xfb\x2c\xd1\x48\xb8\x5f\xac\x16\xcc', 2)
$ python3 dist/hello.py
¡Hola, EsGeeks!
Ejemplo 2: Hacer capturas de pantalla
Los programas de malware diseñados para robar información suelen tener una función para realizar capturas de pantalla de los escritorios de los usuarios. Esta funcionalidad es fácil de implementar con Python, ya que hay varias bibliotecas disponibles, como pyscreenshot y python-mss.
https://github.com/ponty/pyscreenshot
https://github.com/BoboTiG/python-mss
Un ejemplo de creación de una captura de pantalla utilizando la biblioteca python-mss:
from mss import mss
with mss() as sct:
sct.shot()
Ejemplo 3: Realizar solicitudes web
El malware suele utilizar las solicitudes web para realizar diversas tareas en un sistema comprometido, como la gestión, la obtención de una dirección IP externa, la descarga de nuevas piezas de payload, etc. Con Python, la ejecución de consultas web es fácil y puede implementarse basándose en bibliotecas estándar o de código abierto como requests y httpx.
https://github.com/psf/requests
https://github.com/encode/httpx
Por ejemplo, la dirección IP externa de un sistema comprometido puede obtenerse fácilmente utilizando la biblioteca requests
:
import requests
external_ip = requests.get('http://whatismyip.akamai.com/').text
Las Ventajas de eval()
En general, la función incorporada eval()
se considera muy ambigua y plantea graves riesgos de seguridad cuando se utiliza en el código. Por otro lado, esta función es muy útil a la hora de escribir malware.
La función eval()
es muy potente y puede utilizarse para ejecutar cadenas de código Python desde el propio programa Python. Esta única función se ve a menudo como una capacidad avanzada en el malware compilado. Es la capacidad de ejecutar scripts de alto nivel o “plugins” sobre la marcha cuando se utiliza correctamente. Esto es similar a cuando el malware C incluye un motor de scripting Lua para dar al malware la capacidad de ejecutar scripts Lua. Esto se ha visto en malware de alto perfil como Flame.
Imagina un grupo de hackers interactuando remotamente con un malware escrito en Python. Si el grupo se encontrara de repente en una situación inesperada en la que tuviera que reaccionar rápidamente, la capacidad de ejecutar directamente el código en el sistema de destino podría ser muy útil. Además, el malware escrito en Python puede haber sido colocado con una funcionalidad muy limitada, y se añaden nuevas características según sea necesario con el fin de permanecer invisible el mayor tiempo posible.
Malware Python en el Mundo Real
Muy bien, echemos un vistazo a algunas muestras de malware Python del mundo real.
SeaDuke
SeaDuke es probablemente el malware más conocido escrito en Python. En 2015-2016, el Comité Nacional Demócrata (DNC) se vio comprometido por dos grupos que muchos analistas atribuyeron a APT 28 y 29.
El equipo de Unin 42 de Palo Alto ha realizado un impresionante análisis de SeaDuke. El código fuente descompilado de este malware también está disponible. Además, F-Secure ha publicado un excelente documento en el que se habla de SeaDuke y otros programas maliciosos relacionados.
SeaDuke es un troyano escrito en Python que fue convertido en un ejecutable de Windows usando PyInstaller y procesado por el empaquetador UPX. El código fuente fue ofuscado para dificultar el análisis. El malware tiene una gran cantidad de capacidades, incluyendo varios métodos para permanecer discretamente y a largo plazo en Windows, lanzamiento multiplataforma, y ejecutar consultas web para recuperar comandos y control (C2).
PWOBot
PWOBot también es un malware conocido que, al igual que SeaDuke, fue compilado utilizando PyInstaller. La principal actividad de PWOBot se produjo entre 2013 y 2015 y afectó a varias organizaciones europeas, principalmente en Polonia.
El malware era muy completo e incluía la capacidad de registrar las pulsaciones de las teclas, establecer la persistencia en Windows, descargar y ejecutar archivos, ejecutar código Python, crear peticiones web y minar criptomonedas. Nuevamente, la Unidad 42 de Palo Alto realizó un excelente análisis de PWOBot.
PyLocky
PyLocky es un ransomware basado en Python, compilado con PyInstaller en un ejecutable independiente de Windows. Se dirigía a varios países, como Estados Unidos, Francia, Italia y Corea. Incluía capacidades anti-sandbox, comando y control, y encriptaba los archivos utilizando el cifrado 3DES (Triple DES).
Los expertos de Trend Micro realizaron un buen análisis de PyLocky, y los analistas de Talos Intelligence consiguieron crear un descifrador de archivos (PyLocky Decryptor) para recuperar la información cifrada en los sistemas de las víctimas.
PoetRAT
PoetRAT es un troyano que se dirigió al gobierno y al sector energético de Azerbaiyán a principios de 2020. El troyano estaba incrustado en los sistemas y robaba información relevante para los sistemas ICS/SCADA que controlan las turbinas eólicas.
El malware se lanzó utilizando documentos maliciosos de Microsoft Word. La RAT presentaba muchas capacidades para robar información, incluyendo la extracción de archivos a través de FTP, la toma de imágenes con cámaras web, la carga de herramientas adicionales, el registro de teclas, la enumeración de navegadores y el robo de credenciales. Talos Intelligence informó sobre este actor de la amenaza y elaboró un fantástico informe sobre el actor desconocido que utilizó este malware.
A continuación se muestra el script utilizado para capturar las imágenes de la webcam:
Malware de Código abierto
Además de los programas maliciosos encontrados en la naturaleza, hay algunos troyanos de código abierto, como pupy y Stitch. Estos programas maliciosos demuestran lo sofisticadas y ricas que pueden ser las aplicaciones de este tipo. La RAT pupy es multiplataforma, cuenta con una pauta de ejecución en toda la memoria, deja una huella muy baja, puede combinar varios métodos de cifrado C2, migrar a los procesos mediante inyección reflexiva y puede cargar código Python remoto desde la memoria.
https://github.com/n1nj4sec/pupy
https://github.com/nathanlopez/Stitch
Utilidades de Análisis de Malware
Hay muchas utilidades de análisis de malware escritas en Python, incluso en forma compilada. Describiremos brevemente algunas de las herramientas disponibles.
uncompyle6
pip install uncompyle6
El sucesor de decompyle, uncompyle y uncompyle2 es la utilidad uncompyle6, que es un descompilador multiplataforma que puede utilizarse para convertir el código de bytes en código fuente de Python.
Consideremos un simple script “¡Hola, EsGeeks!” y ejecutémoslo como un archivo pyc
(que contiene código de bytes), mostrado a continuación. El código fuente puede reconstruirse utilizando uncompyle
.
python -m compileall ./
$ xxd hello.pyc
00000000: 03f3 0d0a 90d8 cc60 6300 0000 0000 0000 .......`c.......
00000010: 0001 0000 0040 0000 0073 0900 0000 6400 .....@...s....d.
00000020: 0047 4864 0100 5328 0200 0000 730e 0000 .GHd..S(....s...
00000030: 0048 6f6c 612c 2045 7347 6565 6b73 214e .Hola, EsGeeks!N
00000040: 2800 0000 0028 0000 0000 2800 0000 0028 (....(....(....(
00000050: 0000 0000 7308 0000 0068 656c 6c6f 2e70 ....s....hello.p
00000060: 7974 0800 0000 3c6d 6f64 756c 653e 0100 yt....<module>..
00000070: 0000 7400 0000 00
$ uncompyle6 hello.pyc | grep -v '#'
print('¡Hola, EsGeeks!')
pyinstxtractor.py (PyInstaller Extractor)
PyInstaller Extractor puede extraer datos de Python de los ejecutables compilados con PyInstaller.
> python pyinstxtractor.py hello.exe
...
El resultado serán archivos pyc que pueden ser descompilados y el código fuente puede ser restaurado usando uncompyle6.
python-exe-unpacker
El script pythonexeunpack.py
puede utilizarse para desempaquetar y descompilar los ejecutables compilados con py2exe.
> python python_exe_unpack.py -i hello.exe
...
Detección de Archivos Compilados
En tiempo de compilación, PyInstaller y py2exe añaden cadenas únicas al ejecutable, lo que hace que la detección mediante reglas YARA sea mucho más fácil.
PyInstaller escribe la cadena “pyi-windows-manifest-filename
” cerca del final del ejecutable, puedes identificarlo con un editor hexadecimal (HxD).
En el caso de py2exe, podemos identificar el ejecutable con la palabra mágica “PYTHONSCRIPT
“, utilizando strings64. Ciertamente, podemos probar “pyi-windows-manifest-filename” y “PYTHONSCRIPT” para confirmar el convertidor de scripts en uso.
strings64.exe ../source/dist/hello.exe
strings64.exe C:\Ruta\del\Script\dist\hello.exe | findstr PYTHONSCRIPT
Aquí hay una regla YARA para detectar los ejecutables compilados de PyInstaller (Fuente):
import "pe"
rule PE_File_pyinstaller
{
meta:
author = "Didier Stevens (https://DidierStevens.com)"
description = "Detect PE file produced by pyinstaller"
strings:
$a = "pyi-windows-manifest-filename"
condition:
pe.number_of_resources > 0 and $a
}
Aquí hay una segunda regla de YARA para detectar los ejecutables compilados de py2exe (Fuente):
https://github.com/NVISOsecurity/YARA/blob/master/py2exe.yara
import "pe"
rule py2exe
{
meta:
author = "Didier Stevens (https://www.nviso.be)"
description = "Detect PE file produced by py2exe"
condition:
for any i in (0 .. pe.number_of_resources - 1):
(pe.resources[i].type_string == "P\x00Y\x00T\x00H\x00O\x00N\x00S\x00C\x00R\x00I\x00P\x00T\x00")
}
Conclusión
Aquí termina la historia del malware escrito en Python. Es fascinante ver cómo se mantienen las tendencias a medida que los ordenadores se vuelven más potentes y fáciles de usar. Como profesionales de la seguridad, debemos vigilar de cerca el malware escrito en Python, o los problemas pueden surgir cuando menos lo esperas.