Python es un lenguaje de programación fácil y potente que nos permite escribir sofisticados programas, como por ejemplo Dropbox y BitTorrent. Es habitual que se entregue el código fuente de los programas en Python, aunque en algunos casos se aplican diversas técnicas como ofuscación y compilación para proteger el código de los ojos curiosos. Pero ¿realmente funcionan estas técnicas?
En este artículo veremos algunas herramientas que supuestamente nos ayudan a proteger nuestro código y lo fácil que es subvertirlas.
A continuación tenemos dos programas de ejemplo escritos en Python: el primero es una simple función que nos pide una contraseña y nos muestra un mensaje; el segundo ejemplo es igual, aunque en esta ocasión hemos utilizado una clase.
def main():
a = "toomanysecrets"
res = raw_input("Please enter your password: ")
if res == a:
print "ACCESS GRANTED"
else:
print "ACCESS DENIED"
if __name__ == "__main__":
main()
secretapp1.py
class DoMain:
def __init__(self):
self.a = "toomanysecrets"
def Ask(self):
res = raw_input("Please enter your password: ")
if res == self.a:
print "ACCESS GRANTED"
else:
print "ACCESS DENIED"
if __name__ == "__main__":
dm = DoMain()
dm.Ask()
secretapp2.py
Supongamos que no quiero entregar el código de estos programas, por lo que tengo varias opciones. Nuestra primera opción será ofuscar el código, dificultando su lectura.
Pyobfuscate
Este programa nos permite ofuscar nuestro programa de forma totalmente válida para el intérprete Python. Veamos un ejemplo con SecretApp1 y SecretApp2.
A simple vista nuestro código no tiene sentido, pero si analizamos el resultado veremos cómo las cadenas de texto siguen en el código y podemos reconocer sintaxis de Python. No es demasiado difícil reconstruir el código ofuscado.
A pesar de sus limitaciones os invito a visitar la web de esta herramienta para ver sus posibilidades.
Htibctobf
Originalmente esta herramienta se escribió para resolver un reto en una competición de hacking en el congreso Hack in the Box. Os recomiendo leer este estupendo artículo.
A diferencia de la anterior herramienta, Htibctobf ofusca el código Python modificando los AST (Abstract Syntax Trees). Al ejecutar esta herramienta podemos ver nuestro código Python ofuscado en la Fig. 3 y Fig. 4.
Podemos observar el código ofuscado, incluso las cadenas de texto, sin embargo a pesar de todo no es demasiado difícil reconstruir el código original.
Sin duda un interesante concepto con amplias posibilidades pero que requiere de mejoras para ser útil.
En algunos casos quizás baste con ofuscar el código, pero busquemos otras opciones para proteger nuestro código de forma más efectiva: tendremos que recurrir a compilar el código Python creando un ejecutable.
Py2exe
Posiblemente una de las opciones más populares para convertir código Python en ejecutables Windows. Py2exe
En primer lugar tenemos que crear un fichero llamado setup.py que incluya una referencia a nuestro programa que queremos compilar. Ver código setup.
from distutils.core import setup
import py2exe
setup(console=['secretapp1.py'])
setup.py
Ya estamos listos para compilar nuestro código Python en un ejecutable Windows, para ello ejecutamos py2exe. Ver Fig. 5.
Fig. 5 – Compilando secretapp1.exe
Una vez finalizado el proceso de compilación, py2exe nos creará un directorio llamado “dist” que incluye nuestro ejecutable y algunas librerías necesarias. En la Fig. 6 podemos ver que py2exe finaliza con éxito y ejecutamos nuestro programa en formato exe.
Fig. 6 – Compilación completada
Ahora podríamos distribuir este binario sin miedo a entregar nuestro código o quizás no ¿?
Py2exe_extract
Esta herramienta nos permite extraer los objetos Python dentro de los ejecutables creados con py2exe, básicamente invertimos el proceso. Py2exe_extract
En la Fig. 7 podemos ver como utilizamos py2exe_extract para obtener el fichero objeto (el contenido de este fichero es independiente de la plataforma y se conoce como Bytecode) de nuestra aplicación secretapp1.exe (secretapp1.pyc)
Fig. 7 – Extrayendo el fichero objeto
Ahora tenemos que ver cómo podemos obtener el código de este fichero objeto.
Unwind
Unwind es un desensamblador para Python Bytecode que podemos utilizar para analizar ficheros objetos “.pyc”. Para este ejemplo he escrito un simple script en Python, mytest.py, que importa el desensamblador y analiza el fichero pyc. Ver código a continuación.
import unwind
print(unwind.disassemble('secretapp1.pyc'))
mytest.py
Con este script podemos ejecutar el siguiente comando y obtener un desensamblado del fichero objeto. Ver Fig. 8.
Para los amantes del bajo nivel esta será su opción favorita 😉
Uncompyle2
Otra opción es utilizar un decompilador como uncompyle2 para obtener el código directamente del fichero objeto “.pyc” sin tener que pasar por el desensamblador como vimos anteriormente.
Esta herramienta es potente y fácil de utilizar, como se puede observar en la Fig. 9 mediante un simple comando obtenemos el código fuente de secretapp1.pyc.
Fig. 9 – Código de secretapp1 obtenido del fichero objeto
Wow, tenemos código fuente!
A lo largo del artículo hemos visto algunas técnicas de ofuscación y compilación de código Python para protegerlo, no obstante hemos podido subvertir todo el proceso de protección fácilmente
La siguiente lista son otros compiladores Python que podemos utilizar para Windows, Linux o MacOS, pero tienen los mismos problemas.
También podríamos analizar y subvertir el binario utilizando herramientas como IDA PRO o Immunity Debugger, lo que dejaremos para un futuro post. Otra interesante herramienta que no he comentado es pyREtic, un potente framework para reversing de in-memory de Python Bytecode.
Que un atacante consiga el código Python es cuestión de tiempo, sin embargo para ponérselo realmente difícil desde un punto de vista defensivo se
tienen que combinar diferentes técnicas.
¿Proteges tus programas en Python? ¿Qué métodos utilizas?
— Simon Roses Femerling
6 respuestas a AppSec: Mitos en Python sobre ofuscación y reversing