Escrito por Cole Herzog
Las ofuscaciones y tú
Application security Es un conocido conocido ahora. Todos saben que lo necesitan y los atacantes saben cómo burlar las protecciones básicas; enviar una aplicación desprotegida simplemente no es una opción. Todas las protecciones sólidas tienen dos partes: prevención de análisis estático y detección de ataques dinámicos. El análisis estático es una herramienta poderosa que brinda a los atacantes una visión única de los detalles de implementación específicos de la aplicación. Expone las partes más vulnerables de una aplicación, lo que ayuda a acelerar un ataque o robar IP empresarial valiosa. El código fuente, así como los datos, ofuscación es una parte importante de cualquier protección de análisis estático. A continuación se muestran algunas formas en que se pueden transformar el código y los datos para frenar a los actores maliciosos. Cuanto más tienen que trabajar, más safer nuestro mundo puede ser.
Privatiza tu protección
Si bien las aplicaciones no deben enviarse sin algún tipo de ofuscación, no todas las ofuscaciones son iguales. Dos componentes cruciales para cualquier ofuscación fuerte son la propiedad intelectual privada y la aleatorización. Los ofuscadores de código abierto disponibles actualmente pueden ser tan útiles para los atacantes como para los desarrolladores de aplicaciones. El nivel de protección que ofrecen esos ofuscadores de código abierto no es lo suficientemente potente como para proteger de manera confiable las aplicaciones de calidad empresarial. Las ofuscaciones impactantes deben transformar el código fuente en algo ilegible para los humanos e irreversible para las herramientas de ataque automatizadas. Las técnicas de ofuscación que están disponibles públicamente funcionan como su propia Piedra Rosetta para derrotarlos. El uso de técnicas de ofuscación personalizadas y que no son de acceso público impide que Timmy Turner revierta la ofuscación de una aplicación utilizando sus hadas de IA generativa. Combine esos algoritmos privados con una aleatoriedad inherente para garantizar que ningún ser humano, o LLM, pueda desenredar el hilo.
PRNG contraataca
Los atacantes pueden obtener bastante información comparando diferentes releases de la misma aplicación. Hacer que las ofuscaciones cambien entre las versiones de la aplicación es una necesidad absoluta. La mayoría de los motores de pseudoaleatorización utilizan una semilla para forzar la previsibilidad manteniendo la aleatoriedad inherente, y las ofuscaciones no son diferentes. Simplemente incrementar o disminuir un valor inicial afectará dramáticamente las ofuscaciones aplicadas a una aplicación de entrada. Esto proporciona reproducibilidad cuando es necesario, pero obliga a los atacantes a empezar de nuevo al revertir una aplicación después de lanzar una nueva versión. released. Es como si el atacante paleara la mitad del camino de entrada, tomara un descanso para tomar un chocolate caliente y regresara a la nieve fresca. Una seguridad fuerte se centra en desperdiciar una ingeniería inversa El tiempo, y un paisaje en constante cambio es una de las mejores formas de perder el tiempo.
Ofuscación Ofuscación Ofuscaciones
Conceptualmente, la ofuscación es lo opuesto a la optimización. Así como los compiladores suelen tener múltiples pases de optimización, nosotros tenemos múltiples pases de ofuscación. Eso significa que aplicamos ofuscación a las ofuscaciones que se acaban de agregar. El resultado es asombrosamente difícil de leer.
Cosas como una simple instrucción "add" o "mov" pueden convertirse rápidamente en cincuenta o cientos de instrucciones. Ofuscar adecuadamente los emparejamientos `adrp add` puede diezmar la capacidad de una herramienta automatizada (como ghidra o IDA) para resolver el flujo de control del programa. Ofuscaciones aparentemente menores pueden hacer que sea casi imposible para un atacante encontrar el inicio, el final o la mitad de esa llamada a la función "acceptUserCredentials". Cientos de ofuscaciones variadas y compuestas dejan a los atacantes potenciales tratando de descubrir cómo el código se ejecuta exitosamente.
Bótelo, gírelo, configúrelo
No todas las llamadas a métodos o funciones son confidenciales en una aplicación, y algunas llamadas a métodos o funciones pueden ser muy sensibles al rendimiento. Si bien aplicar de manera uniforme la ofuscación en toda una aplicación es significativamente mejor que nada, esa estrategia puede tener un impacto considerable en el rendimiento del tiempo de ejecución. Brindar a los ingenieros de seguridad la capacidad de ofuscar en gran medida las llamadas confidenciales, mientras ofuscan ligeramente las llamadas de alto rendimiento, es parte de proporcionar una herramienta de protección de clase mundial. Cualquier protección dinámica (guardias) inyectada en la aplicación requiere tanta ofuscación como lo requiere el código fuente subyacente. Aplicar las mismas técnicas de ofuscación de alta seguridad a esos guardias activos ayuda a mantenerlos protegidos y en funcionamiento y ralentiza los esfuerzos de los atacantes por identificarlos y eliminarlos. La configurabilidad es crucial para cualquier organización centrada en la seguridad, y las herramientas de protección deben cambiar tan rápido como lo haga la dirección de una empresa. Como se mencionó anteriormente, algunas ofuscaciones se construyen de manera diferente. A continuación se presentan algunos conceptos que proporciona cualquier motor de ofuscación serio y cómo puede mantener alejado el tráfico aéreo no aprobado.
Flujo de control calculado
Los atacantes pueden obtener bastante conocimiento de una aplicación con solo observar el flujo de llamadas a funciones en toda la aplicación. Para combatir eso, nuestras ofuscaciones utilizan algo llamado Flujo de control computarizado. Computed Control Flow impide que los descompiladores conecten llamadas a funciones y referencias de etiquetas al punto de entrada principal de las aplicaciones. Es una parte fundamental de cualquier protección de clase mundial. Cuando un descompilador analiza un binario, ver cómo una función llama a otra función suele ser trivial. Tomemos, por ejemplo, instrucciones como: `b.eq 0x10055bc42` y `bl 0x100abcdef`. Un descompilador sabe exactamente qué hay en esas ubicaciones de memoria virtual. En el caso de las dos instrucciones anteriores, se trata de etiquetas dentro de una función o de una llamada a la función misma. Cualquier descompilador que se precie generará una hermosa telaraña de llamadas a funciones entrelazadas, mostrando una ruta clara de ejecución a lo largo de una aplicación. Combine esto con otros trucos básicos, como ver en qué parte del ensamblado se usa un mensaje de "inicio de sesión exitoso", y para un atacante resultará trivial identificar código explotable. El flujo de control computarizado es un conjunto de operaciones matemáticas inyectadas antes de llamadas obvias al flujo de control. Estas operaciones matemáticas impiden que un descompilador conozca la dirección virtual relativa exacta a la que se salta, destruyendo su capacidad de generar un gráfico de flujo de control.
Controlar el aplanamiento del flujo
Computed Control Flow ya es un terror para los descompiladores; Seguro que sería una lástima que lo empeore. Otra estrategia para descarrilar el flujo de control lógico se llama aplanamiento del flujo de control. Tenemos varias formas de CFF, pero una recientemente agregada (llamada The Nexus) da nueva vida a esta técnica de ofuscación crítica. Todos hemos escrito un juego de ajedrez en `main` antes, pero ¿qué tal escribir una aplicación completa a nivel de producción en una única declaración de cambio autorreferenciada? Nexus hace precisamente eso y al mismo tiempo prioriza el rendimiento en tiempo de ejecución. Existe una gran cantidad de información sobre CFF, su importancia y cómo funciona. Es imposible subestimar lo difícil que esta técnica de ofuscación hace la vida de los atacantes, y ninguna aplicación debería salir del escenario sin ella.
saltar
Ningún juego de cuchillos de acero inoxidable estaría completo sin un par de tijeras de acero inoxidable a juego, y Chopup complementa las ofuscaciones del flujo de control de la misma manera. Los formatos de archivos binarios suelen estar densamente empaquetados para ahorrar espacio en el disco. Eso significa que la mayoría de las llamadas a funciones relacionadas están bastante cerca unas de otras. Como resultado, un binario desensamblado tiende a agrupar funciones relacionadas y casi siempre tiene funciones como un bloque contiguo en el binario. Eso es demasiado fácil de leer y realmente nos pone nerviosos. Chopup cambia eso. No hay ninguna restricción técnica en un binario que requiera que las llamadas a funciones sean contiguas en la memoria, y un buen par de tijeras (con un poco de pegamento flexible) puede dejar un binario funcionando como se espera, pero obliga a los atacantes a saltar a cada rincón del binario en busca de lógica. flujo de control.
mgDaea y Reparación
Si bien ofuscar el flujo de código en sí es una capa crucial de protección, ofuscar los datos subyacentes de la aplicación es igualmente necesario. El daño y la reparación son el mal caótico y el bien legal de las protecciones de análisis estático y casi siempre van de la mano. Dañar un dato, o incluso en algunos casos, un fragmento de código, y luego repararlo justo antes de leerlo o ejecutarlo hace que el análisis estático sea efectivamente imposible. El daño cambia los datos especificados en lo que parece ser memoria basura. Cuando esos mismos datos se exponen a una llamada de reparación, vuelven a su forma original, muchas veces justo antes de usarse. Luego, esos datos pueden dañarse nuevamente después de quedar fuera del alcance y no se usarán hasta la próxima vez que se llame a la función. Esa clave API podría ser “1#&at0d$*nd@@z” en el binario para comenzar, pero tenga la seguridad de que se arreglará antes de usarse.
Cambio de nombre de símbolo
Incluso con la ofuscación del código fuente y la ofuscación de datos, todavía quedan nombres de símbolos del código fuente en un binario compilado. Por ejemplo, un artefacto de símbolo para el nombre de una función como "acceptContinuousPayment" puede proporcionar fácilmente un enlace directo a la implementación de esa función. Una vez encontrado, es trivial modificar el ensamblaje y acceder a funciones premium sin pagar. El cambio completo de nombre de los símbolos oculta los artefactos restantes y no deja nada con lo que puedan trabajar los atacantes. Eliminar cualquier indicio o pista sobre una funcionalidad sensible en un binario protegido puede obligar a los atacantes a abandonar por completo el análisis estático.
El fin
Estos son sólo ejemplos de cómo se ven las prácticas y metodologías de ofuscación sólidas, y no es de ninguna manera una lista exhaustiva. Dejar una aplicación desprotegida en la naturaleza brinda a cualquiera una visibilidad completa de la IP única y brinda a cualquier atacante una ventana clara a la lógica empresarial. Con el meteórico ascenso de la IA generativa y los copilotos de código fuente hacia la corriente principal, se ha vuelto infinitamente más fácil para las personas escribir código y para algunas personas revertir el código. Un compromiso de toda la organización para implementar soluciones de seguridad sólidas es el único camino viable a seguir en un entorno tan acelerado. No dejes que el arduo trabajo de tu desarrollador se vaya de casa sin él.
Lea la opinión de IDC sobre la importancia de ofuscación y anti-manipulación como parte de tu DevSecOps práctica.
¿Estás listo para escalar tu empresa?
Explorar
¿Qué hay de nuevo en el mundo de Digital.ai
Resumen del incidente de CrowdStrike y prevención con Digital.ai Soluciones
El 19 de julio de 2024, una actualización de configuración de software defectuosa de…
Guía: Cómo ofuscar el código
Aprenda a ofuscar código de forma eficaz en esta guía completa. Descubra la importancia de la ofuscación de código y explore diferentes tipos y técnicas.
Cómo ofuscar el código C#
Descubra los motivos detrás de la ofuscación del código C# y explore casos de uso comunes. Proteja su propiedad intelectual y evite la ingeniería inversa.