{"id":161,"date":"2014-03-05T20:07:20","date_gmt":"2014-03-05T20:08:08","guid":{"rendered":"https:\/\/07dd6simonroses_python_cap11.png"},"modified":"2015-07-16T20:53:51","modified_gmt":"2015-07-17T02:53:51","slug":"mitos-en-python-sobre-ofuscacion-y-reversing","status":"publish","type":"post","link":"https:\/\/emanuelpaxtian.com\/blog\/mitos-en-python-sobre-ofuscacion-y-reversing\/","title":{"rendered":"Mitos en Python sobre ofuscaci\u00f3n y reversing"},"content":{"rendered":"<p>Es habitual que se entregue el c\u00f3digo fuente de los programas en Python, aunque en algunos casos se aplican diversas t\u00e9cnicas como ofuscaci\u00f3n y compilaci\u00f3n para proteger el c\u00f3digo de los ojos curiosos. Pero \u00bfrealmente funcionan estas t\u00e9cnicas?<\/p>\n<p>En este art\u00edculo veremos algunas herramientas que supuestamente nos ayudan a proteger nuestro c\u00f3digo y lo f\u00e1cil que es subvertirlas.<\/p>\n<p>A continuaci\u00f3n tenemos dos programas de ejemplo escritos en Python: el primero es una simple funci\u00f3n que nos pide una contrase\u00f1a y nos muestra un mensaje; el segundo ejemplo es igual, aunque en esta ocasi\u00f3n hemos utilizado una clase.<\/p>\n<pre class=\"brush: python\">def main():\r\n\r\n        a = \"toomanysecrets\"\r\n\r\n        res = raw_input(\"Please enter your password: \")\r\n        \r\n        if res == a:\r\n                print \"ACCESS GRANTED\"\r\n        else:\r\n                print \"ACCESS DENIED\"\r\n\r\nif __name__ == \"__main__\":\r\n\tmain()\r\n<\/pre>\n<p>secretapp1.py<\/p>\n<pre class=\"brush: python\">class DoMain:\r\n\r\n        def __init__(self):\r\n                self.a = \"toomanysecrets\"\r\n\r\n        def Ask(self):\r\n                res = raw_input(\"Please enter your password: \")\r\n\r\n                if res == self.a:\r\n                        print \"ACCESS GRANTED\"\r\n                else:\r\n                        print \"ACCESS DENIED\"\r\n\r\nif __name__ == \"__main__\":\r\n\tdm = DoMain()\r\n\tdm.Ask()\r\n<\/pre>\n<p>secretapp2.py<\/p>\n<p>Supongamos que no quiero entregar el c\u00f3digo de estos programas, por lo que tengo varias opciones. Nuestra primera opci\u00f3n ser\u00e1 ofuscar el c\u00f3digo, dificultando su lectura.<\/p>\n<p><strong>Pyobfuscate<\/strong><\/p>\n<p>Este programa nos permite ofuscar nuestro programa de forma totalmente v\u00e1lida para el int\u00e9rprete Python. Veamos un ejemplo con SecretApp1 y SecretApp2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap1.png\" alt=\"simonroses_python_cap1\" width=\"677\" height=\"354\" border=\"0\" \/><br \/>\nFig. 1 \u2013 Secretapp1 ofuscado<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap2.png\" alt=\"simonroses_python_cap2\" width=\"677\" height=\"354\" border=\"0\" \/><br \/>\nFig. 2 \u2013 Secretapp2 ofuscado<\/p>\n<p>A simple vista nuestro c\u00f3digo no tiene sentido, pero si analizamos el resultado veremos c\u00f3mo las cadenas de texto siguen en el c\u00f3digo y podemos reconocer sintaxis de Python. No es demasiado dif\u00edcil reconstruir el c\u00f3digo ofuscado.<\/p>\n<p><strong>Htibctobf<\/strong><\/p>\n<p>Originalmente esta herramienta se escribi\u00f3 para resolver un reto en una competici\u00f3n de hacking en el congreso Hack in the Box.<\/p>\n<p>A diferencia de la anterior herramienta, Htibctobf ofusca el c\u00f3digo Python modificando los AST (<a href=\"http:\/\/docs.python.org\/2\/library\/ast.html\">Abstract Syntax Trees<\/a>). Al ejecutar esta herramienta podemos ver nuestro c\u00f3digo Python ofuscado en la Fig. 3 y Fig. 4.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap3.png\" alt=\"simonroses_python_cap3\" width=\"677\" height=\"342\" border=\"0\" \/><br \/>\nFig. 3 \u2013 Secretapp1 ofuscado<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap4.png\" alt=\"simonroses_python_cap4\" width=\"677\" height=\"342\" border=\"0\" \/><br \/>\nFig. 4 \u2013 Secretapp2 ofuscado<\/p>\n<p>Podemos observar el c\u00f3digo ofuscado, incluso las cadenas de texto, sin embargo a pesar de todo no es demasiado dif\u00edcil reconstruir el c\u00f3digo original.<\/p>\n<p>Sin duda un interesante concepto con amplias posibilidades pero que requiere de mejoras para ser \u00fatil.<\/p>\n<p>En algunos casos quiz\u00e1s baste con ofuscar el c\u00f3digo, pero busquemos otras opciones para proteger nuestro c\u00f3digo de forma m\u00e1s efectiva: tendremos que recurrir a compilar el c\u00f3digo Python creando un ejecutable.<\/p>\n<p><strong>Py2exe<\/strong><\/p>\n<p>Posiblemente una de las opciones m\u00e1s populares para convertir c\u00f3digo Python en ejecutables Windows.\u00a0<a href=\"http:\/\/www.py2exe.org\/\">Py2exe<\/a><\/p>\n<p>En primer lugar tenemos que crear un fichero llamado setup.py que incluya una referencia a nuestro programa que queremos compilar. Ver c\u00f3digo setup.<\/p>\n<pre class=\"brush: python\">from distutils.core import setup\r\nimport py2exe\r\n\r\nsetup(console=['secretapp1.py'])\r\n<\/pre>\n<p>setup.py<\/p>\n<p>Ya estamos listos para compilar nuestro c\u00f3digo Python en un ejecutable Windows, para ello ejecutamos py2exe. Ver Fig. 5.<\/p>\n<p><img decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap5.png\" alt=\"simonroses_python_cap5\" border=\"0\" \/><br \/>\nFig. 5 \u2013 Compilando secretapp1.exe<\/p>\n<p>Una vez finalizado el proceso de compilaci\u00f3n, py2exe nos crear\u00e1 un directorio llamado \u201cdist\u201d que incluye nuestro ejecutable y algunas librer\u00edas necesarias. En la Fig. 6 podemos ver que py2exe finaliza con \u00e9xito y ejecutamos nuestro programa en formato exe.<\/p>\n<p><a href=\"http:\/\/www.simonroses.com\/wp-content\/uploads\/2013\/10\/simonroses_python_cap6.png\"><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap6.png\" alt=\"simonroses_python_cap6\" width=\"677\" height=\"342\" border=\"0\" \/><\/a><br \/>\nFig. 6 \u2013 Compilaci\u00f3n completada<\/p>\n<p>Ahora podr\u00edamos distribuir este binario sin miedo a entregar nuestro c\u00f3digo o quiz\u00e1s no \u00bf?<\/p>\n<p><strong>Py2exe_extract<\/strong><\/p>\n<p>Esta herramienta nos permite extraer los objetos Python dentro de los ejecutables creados con py2exe, b\u00e1sicamente invertimos el proceso.<a href=\"http:\/\/code.google.com\/p\/py2exe-extract\/\">Py2exe_extract<\/a><\/p>\n<p>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\u00f3n secretapp1.exe (secretapp1.pyc)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap7.png\" alt=\"simonroses_python_cap7\" width=\"677\" height=\"342\" border=\"0\" \/><br \/>\nFig. 7 \u2013 Extrayendo el fichero objeto<\/p>\n<p>Ahora tenemos que ver c\u00f3mo podemos obtener el c\u00f3digo de este fichero objeto.<\/p>\n<p><strong>Unwind<\/strong><\/p>\n<p><a href=\"https:\/\/github.com\/evanw\/unwind\">Unwind<\/a>\u00a0es un desensamblador para Python Bytecode que podemos utilizar para analizar ficheros objetos \u201c.pyc\u201d. Para este ejemplo he escrito un simple script en Python, mytest.py, que importa el desensamblador y analiza el fichero pyc. Ver c\u00f3digo a continuaci\u00f3n.<\/p>\n<pre><code>\r\nimport unwind\r\n\r\nprint(unwind.disassemble('secretapp1.pyc'))\r\n<\/code><\/pre>\n<p>mytest.py<\/p>\n<p>Con este script podemos ejecutar el siguiente comando y obtener un desensamblado del fichero objeto. Ver Fig. 8.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap8.png\" alt=\"simonroses_python_cap8\" width=\"669\" height=\"850\" border=\"0\" \/><br \/>\nFig. 8 \u2013 Python Bytecode<\/p>\n<p><strong>Uncompyle2<\/strong><\/p>\n<p>Otra opci\u00f3n es utilizar un decompilador como\u00a0<a href=\"https:\/\/github.com\/wibiti\/uncompyle2\">uncompyle2<\/a>\u00a0para obtener el c\u00f3digo directamente del fichero objeto \u201c.pyc\u201d sin tener que pasar por el desensamblador como vimos anteriormente.<\/p>\n<p>Esta herramienta es potente y f\u00e1cil de utilizar, como se puede observar en la Fig. 9 mediante un simple comando obtenemos el c\u00f3digo fuente de secretapp1.pyc.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"..\/fotos\/simonroses_python_cap9.png\" alt=\"simonroses_python_cap9\" width=\"677\" height=\"342\" border=\"0\" \/><br \/>\nFig. 9 \u2013 C\u00f3digo de secretapp1 obtenido del fichero objeto<\/p>\n<p>Wow, tenemos c\u00f3digo fuente!<\/p>\n<p>A lo largo del art\u00edculo hemos visto algunas t\u00e9cnicas de ofuscaci\u00f3n y compilaci\u00f3n de c\u00f3digo Python para protegerlo, no obstante hemos podido subvertir todo el proceso de protecci\u00f3n f\u00e1cilmente \u00ef\u0081\u0160<\/p>\n<p>La siguiente lista son otros compiladores Python que podemos utilizar para Windows, Linux o MacOS, pero tienen los mismos problemas.<\/p>\n<ul>\n<li><a href=\"http:\/\/cx-freeze.sourceforge.net\/\">cx_Freeze<\/a><\/li>\n<li><a href=\"http:\/\/code.google.com\/p\/shedskin\/\">Shed Skin<\/a><\/li>\n<li><a href=\"http:\/\/svn.pythonmac.org\/py2app\/py2app\/trunk\/doc\/index.html\">py2app<\/a><\/li>\n<\/ul>\n<p>Tambi\u00e9n podr\u00edamos analizar y subvertir el binario utilizando herramientas como\u00a0<a href=\"https:\/\/www.hex-rays.com\/products\/ida\/index.shtml\">IDA PRO<\/a>\u00a0o\u00a0<a href=\"http:\/\/www.immunityinc.com\/products-immdbg.shtml\">Immunity Debugger<\/a>, lo que dejaremos para un futuro post. Otra interesante herramienta que no he comentado es\u00a0<a href=\"https:\/\/github.com\/MyNameIsMeerkat\/pyREtic\">pyREtic<\/a>, un potente framework para reversing de in-memory de Python Bytecode.<\/p>\n<p>Que un atacante consiga el c\u00f3digo Python es cuesti\u00f3n de tiempo, sin embargo para pon\u00e9rselo realmente dif\u00edcil desde un punto de vista defensivo se<br \/>\ntienen que combinar diferentes t\u00e9cnicas.<\/p>\n<p>Fuente\u00a0simonroses<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Es habitual que se entregue el c\u00f3digo fuente de los programas en Python, aunque en algunos casos se aplican diversas t\u00e9cnicas como ofuscaci\u00f3n y compilaci\u00f3n para proteger el c\u00f3digo de&hellip;<\/p>\n","protected":false},"author":1,"featured_media":410,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-161","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/posts\/161","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/comments?post=161"}],"version-history":[{"count":0,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/posts\/161\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/media\/410"}],"wp:attachment":[{"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/media?parent=161"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/categories?post=161"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/emanuelpaxtian.com\/blog\/wp-json\/wp\/v2\/tags?post=161"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}