Por Nicolás Adrién y Gerardo Canedo

Luego de convenir el alcance, comenzamos por el inicio: la fase de reconocimiento.

Esta fase es una de las que más disfrutamos y es una en las cuales invertimos más tiempo. ¿Con qué puertos nos encontraremos? ¿Qué software estará disponible para ser utilizado por un atacante?

Muchas veces encontramos productos de terceros instalados. Algunas veces son proyectos open source ampliamente conocidos, otras son productos de nicho, no tan comunes.

En este caso en particular se trataba de un software de código abierto, disponible en un repositorio de control de versiones. ¿Cómo lo detectamos? Googleando, por supuesto.

Código

Haremos uso de las palabras de Ilia Kolochenko, fundadora y directora ejecutiva de ImmuniWeb, cuando fue consultada sobre una exposición de código fuente y credenciales de Scotiabank en repositorios de GitHub en Setiembre del 2019:

“Los repositorios de código público, varios proyectos de código y de intercambio de datos pueden facilitar enormemente DevSecOps y acelerar el desarrollo de software ágil. Sin embargo, también traen consigo un amplio espectro de riesgos empresariales críticos de fugas de datos involuntarias o descuidadas, exacerbados por desarrolladores de terceros con insuficiente formación en seguridad”.

Como dijo Ilia, tener un código accesible para cualquier usuario puede (no siempre) implicar un riesgo de seguridad importante.

Esto puede ser un problema por varios puntos:

  1. Se incluye código confidencial (por ejemplo, credenciales).
  2. Se puede observar la estructura completa de la aplicación.
  3. Se pueden identificar objetos no pensados para estar accesibles o disponibles públicamente.

Entre otros…

La etapa de reconocimiento continuó descargando la última versión disponible del código fuente. No era la misma versión que la instalada en nuestro objetivo (la del objetivo era mayor), pero con un poco de suerte, el análisis podría terminar también con alguna vulnerabilidad presente en esta versión.

Continuamos generando un ambiente local de laboratorio. Al estar totalmente bajo nuestro control, podemos realizar cualquier tipo de prueba, además de debuggear e instrumentar su ejecución. Esto, a un atacante le permite detectar vectores de vulnerabilidades y generar exploits que con un enfoque de caja negra exclusivamente no es posible.

En este caso en particular, existía un proceso de inicialización de la base de datos. En este proceso (y en la documentación del propio repositorio) se incluía un usuario y password de administración por defecto. También, en el repositorio se incluía una advertencia sobre cambiar esta contraseña. ¿Algo a destacar? Nadie lee los README (salvo los atacantes). Primer vector, y primero con éxito.

Otra cosa que siempre hacemos es revisar el historial de GIT. Para esto existen muchas herramientas que facilitan la tarea (GitKraken, gitg, LazyGit, git-cola, etc).

¿Qué buscamos aquí?

Hay que darse cuenta que estamos teniendo acceso a toda la vida que ha tenido el repositorio desde sus inicios, por lo tanto, se puede encontrar información de utilidad que posteriormente se decidió quitar.

Siguiendo con el caso anterior, alguien del equipo de desarrollo detectó que fueron commiteadas credenciales, y decide realizar otro commit que las elimina. 

Gracias al historial de versiones y sus herramientas de visualización, pudimos  revisar fácilmente los cambios y detectar las credenciales que fueron eliminadas. Por lo cierto, éstas estaban en un comentario del código.

Esto no solo puede pasar con credenciales, sino también se pueden encontrar fragmentos de código con información relevante, endpoints, documentación, etc.

Herramienta GitKraken a modo de ejemplo

Ahora, a leer código de lleno. Otro aspecto a identificar en el análisis de código son los posibles endpoints publicados por la aplicación. Muchas veces existen páginas o servicios pensados para la administración o la resolución de problemas (troubleshooting) en los cuales no se aplican las mismas reglas de control de acceso que en el resto de la aplicación.

Inspeccionando todos los posibles endpoints, detectamos páginas que permitían analizar logs, inspeccionar el file system, entre otras. Accediendo a la aplicación, no había forma de encontrar un camino a dicho panel. Podríamos haber realizado un escaneo de directorios, pero posiblemente no hubiésemos tenido éxito y seguramente el tiempo insumido iba a ser mayor.

Un atacante que accedió al código y encontró dichos objetos, simplemente puede acceder a los mismos en el ambiente de producción y tener el control de cosas no permitidas.

Continuando con el análisis, detectamos que existían componentes con vulnerabilidades conocidas. Conociendo las versiones exactas y teniendo un ambiente de pruebas similar, pudimos personalizar un exploit para obtener una ejecución remota de código (RCE) en el servidor.

Finalmente, realizamos una búsqueda con el fin de detectar inyecciones SQL. En este punto también pudimos enfocar nuestro esfuerzo en aquellos puntos donde se realizaban concatenaciones de texto, y trazar su ejecución a endpoints que un atacante podría utilizar. Para este caso, detectamos una inyección SQL basada en tiempo, con la cual un atacante podría obtener cualquier valor de la base de datos, incluyendo información que le permitiera iniciar sesión y utilizar alguno de los otros vectores ya detectados.

Esta historia es de un caso puntual, y es mucho más común de lo que los desarrolladores o product owners creen.

¿Cuáles son las recomendaciones?

Un atacante se puede hacer del código fuente de una aplicación de distintas formas:

  1. Por un error en la gestión de código (SCM), donde la publicación no fue intencional: Ya no hay marcha atrás. Una vez hecho público, los atacantes pueden haberlo obtenido y distribuirlo por Internet a través de redes P2P, Foros, Deep Web, etc. 
  2. Publicación intencional: Hay que asegurarse que el código publicado no contiene ningún elemento que pueda ser usado con fines maliciosos en ambientes productivos. Es un hecho que alguien lo analizará en búsqueda de eventuales vulnerabilidades.
  3. Obtención de ejecutables: Los atacantes pueden descargar versiones de evaluación, versiones completas desde páginas de soporte del producto, copia de instalaciones ya comprometidas, etc. Obteniendo el binario, es posible utilizar técnicas de ingeniería inversa y obtener el código a analizar. Es importante notar que de tratarse de lenguajes de scripting (PHP, Python, JSPs de Java, etc),  ¡siempre estamos entregando el código fuente!

Como administrador del sitio bajo análisis, le pueden surgir algunas preguntas o preocupaciones:

¿Debo de dejar de utilizar este software? ¿No utilizo nada más open source?

Lo que hemos hecho en estos casos es recomendar no publicar la aplicación a Internet, revisar el hardening de la aplicación con respecto a los manuales, bloquear URLs específicas utilizando un WAF, y darle aviso al proveedor de la solución para implementar las remediaciones de código necesarias. Normalmente hay una relación contractual entre este proveedor de código y la organización, por lo que los caminos del diálogo están abiertos.

Es común que luego de esta comunicación tengamos una reunión entre las tres partes para comunicar los hallazgos que les competen y establecer un plan de acción.

Desde el punto de vista del product owner pueden surgir distintas preguntas, siendo la más comunes:

¿Cómo lo supo? ¿Qué tengo que hacer para solucionar el problema? ¿Y cómo sigo?

La primera pregunta espero haberla respondido en este post. No es magia negra, es una revisión teniendo un aspecto en mente: Seguridad de Aplicaciones.

En cuanto a la segunda pregunta, por cada vulnerabilidad siempre planteamos por lo menos una remediación. La acción a corto plazo es realizar un HotFix y solucionar el problema en la instalación detectada. Si se trata de un producto, puede encontrarse instalado en otros clientes. Existen opciones de divulgación, entre la completa (full disclosure) y la privada. En cada caso, lo analizamos en conjunto con nuestro cliente. 

Finalmente, es importante comunicarle al proveedor de software que su código va a ser analizado, lo quiera o no, por un atacante. Una aplicación segura es aquella que no puede ser hackeada aún teniendo acceso a su código fuente.

La mejor forma de asegurar el nivel de seguridad del producto y la imagen de éste, junto a la del desarrollador, es implementar revisiones de código periódicas como parte de un ciclo de desarrollo seguro. También resulta muy útil, con cada revisión, implementar casos de prueba de los hallazgos detectados, de forma de asegurarnos de forma eficaz y eficiente que lo que una vez detectamos, no vuelva a ocurrir.

¿Querés ampliar la información?

Escribinos a info@genexusconsulting.com, con gusto te responderemos.

 

Toma contacto con alguno de nuestros especialistas

CONTÁCTANOSarrow-right