CVE-2020–28413 / Blind SQL Injection en Mantis Bug Tracker 2.24.3 API SOAP.

EthicalHCOP
9 min readDec 30, 2020

--

Hola a tod@s.

En esta oportunidad veremos una vulnerabilidad de Blind SQL Injection en la API SOAP de la aplicación Mantis BugTracker en su versión 2.24.3. Antes de empezar, es valido aclarar que la información que será entregada a continuación, es entregada con fines EDUCATIVOS e INVESTIGATIVOS y que no me hago responsable del mal uso de la información por parte de los lectores, una vez aclarado esto continuamos. Dicha vulnerabilidad se presentaba en la funcionalidad mc_project_get_users, y su detección es tan solo modificando y enviando el parámetro “access” sin ningún valor y cambiando el tipo de valor a String. Hay que tener en cuenta que para explotar dicha vulnerabilidad es necesario tener un usuario dentro del sistema; para efectos de prueba usaremos un usuario de rol informador.

Envío de parámetro “access” vacío y posterior respuesta con el error SQL.

Se puede evidenciar que al realizar el envió del parámetro “access” sin ningún valor, el sistema esta retornando un mensaje de error SQL en donde nos dice que hay un error en la sintaxis y nos devuelve el comando SQL entero.

Teniendo en cuenta esto, podemos intentar inyectar alguna condición en el query SQL para que nos retorne un resultado valido en el sistema, así que inyectaremos un valor y seguido la condicional “or 1=1” para ver como se comporta.

Envío de parámetro “access” con el valor “0 or 1=1” y posterior respuesta con la condición ejecutada.

Podemos ver claramente que al ingresar una condición en el query SQL, es posible retornar todos los usuarios. Ahora vamos a revisar que es lo que esta pasando en el código fuente de la aplicación.

Porción del código fuente de la funcionalidad mc_project_get_users.

Podemos evidenciar que la funcionalidad esta recibiendo los parámetros de autenticación y esta haciendo una validación si el usuario logueado en realidad puede realizar dicha función. En la linea 1301 vemos que la aplicación esta haciendo uso de los parámetros “p_project_id” y “p_access” enviándolos a otra funcionalidad hasta el momento desconocida.

Funcionalidad project_get_all_user_rows en donde llegan los parametros “project” y “access”.

Analizando la funcionalidad “project_get_all_user_rows la cual recibe los parámetros de “project” y “access” enviados por la funcionalidad anterior, vemos que entre las primeras líneas el sistema reasigna el valor de la variable “access” a otra variable.

Porción de código en donde ocurre la inyección.

Líneas mas abajo podremos ver que hay una condicional en donde se pregunta si el valor de la variable “t_global_access_level” es un array, y se ejecutan una serie de condiciones anidadas guardando en una variable llamada “t_global_access_clause” parte del query SQL. De no ser así, se ejecuta una sola acción que es guardar en dicha variable la concatenación del string “ >=” y del valor de la variable “t_global_access_level”. Finalmente se ingresa la variable “t_global_access_clause” en el query SQL y luego se ejecuta.

Debug en la variable t_query antes de ser ejecutada en base de datos.

Si realizamos un pequeño debug en el código y consultamos como se esta formando el query SQL antes de ser ejecutado, veremos lo siguiente.

Retorno del query SQL en el debug.

Podemos observar como se esta formando la sentencia SQL al realizar una impresión de la variable “t_query”. Como vimos anteriormente, al no ser un array el valor enviado en la variable “access”, este esta simplemente agregando el string “>=” y luego el valor tal cual es enviado por el usuario.

Debug en la variable t_result luego de ser ejecutado el query SQL.

Al mover el print luego de la ejecución del query, podremos leer el valor de la variable “t_result” la cual contiene el valor de la ejecución de la sentencia SQL.

Debug en la variable t_query vista desde SOAP UI.

Al realizar la impresión del valor de esta variable podremos ver la sentencia final que fue ejecutada en la base de datos, en la parte inferior del query veremos como fue inyectada la condicional enviada por el usuario. Luego de esta consulta, el sistema esta capturando solamente el ID del usuario retornado y realiza otra consulta interna a la base de datos con este valor para así retornar los datos finales.

Diagrama de la petición.

Si lo vemos de una manera grafica, este seria el diagrama en donde la primer linea es la petición realizada por el usuario enviando los datos de acceso y de consulta, en esa misma linea, la aplicación realiza la validación del login y de ser correcta envía los parámetros a la siguiente función. En la segunda linea vemos que la base de datos retorna el “id”, “username”, “realname ”y “nivel de acceso” del usuario. Sin embargo, la aplicación realiza otra nueva petición con el id del usuario recién retornado. Finalmente, la base de datos retorna el “id”, “username”, “realname ”y el “email ”del usuario consultado y la aplicación responde con el formato XML correspondiente.

Vemos que al enviar un union select en el query, este se nos refleja en la respuesta SQL. Es importante mencionar que si se envía un string en lugar de algún valor numérico, el valor retornado siempre será 0. También es importante saber que el primer valor del union select , debe de ser un numero que no choque con el id de un usuario real en la base de datos, es decir, si el primer valor del union select es 1, este se referencia con el id del usuario 1 por lo que retornara un valor verdadero. Para efectos de muestra, se coloco el numero 900 como primer valor del union select ya que no se tiene tal cantidad de usuarios, pero en caso de tenerlos es mejor colocar un numero mas grande. Esto también podría servir para enumerar los ID de los usuarios del sistema .

Query para extraer cantidad de usuarios de la base de datos.

Subiendo un poco el nivel al query, utilizaremos dicha vulnerabilidad para conocer la cantidad de usuarios en la aplicación. Para esto concatenamos un string con el count de los users de dicha tabla y lo posicionamos como el primer valor del union select. Es importante tener en cuenta que el string a concatenar debe de ser el caracter “-” ya que si se concatena con una letra o con otro símbolo no suele reconocerlo como tal y no se ejecuta la inyección de manera correcta.

Ahora, subiendo aun mas el nivel del query, vamos a intentar adivinar cada uno de los caracteres de los hashes de las contraseñas de los usuarios. Para esto realizamos el siguiente query SQL:

0 union all select (select if(substring((select binary(password) from mantis_user_table where id = A),B,1)=’C’,’0',’90000')), 2,3,4 order by id desc

Analizando dicho query vemos lo siguiente:

  • select binary(password) from mantis_user_table where id = A: Esta sentencia nos devuelve el valor del hash de la contraseña de un usuario A.
  • substring(query),B,1: En esta sentencia encontramos que se esta realizando un substring, es decir, capturar X cantidad de caracteres de una salida. En esta ocasión se esta capturando solamente el carácter numero B del query anterior en donde se extrae el hash de la contraseña de un usuario A.
  • select if(query)=’C’,’0',’90000'): Por ultimo, encontramos esta función la cual nos compara un valor con otro y según ello da una respuesta. Esta respuesta es la que se coloca en la primer columna del union select, por lo que debe de ser un valor numérico y lo suficientemente alta o baja como para que no se cruce con el ID de un usuario como lo hablamos anteriormente. Así que esta función compara el caracter devuelto por la función Substring y lo compara con un valor que nosotros deseemos, el resultado de esta operación retornara un 0 si es verdadero o un 90000 si es falso.

En resumen, los parámetros A, B y C significan lo siguiente:
A = ID del usuario a quien extraer el hash de la password.
B = Posición del carácter que se quiere comparar.
C = Carácter a comparar

Ahora veamos un ejemplo practico.

Consulta a la base de datos en donde se reflejan los id, usuarios y hashes.

Si vemos la base de datos, el usuario administrador identificado con el ID 1 tiene como inicio del hash de la contraseña los caracteres “e64b”. Así que vamos a realizar unas pruebas con dichos caracteres, para esto vamos a configurar los valores en el script anterior de la siguiente manera A=1, B=1, C=e, quedando de la siguiente manera.

Ejecución de la comparación del primer carecter del hash de la contraseña del administrador.

Notemos que al realizar la petición de dicha consulta, se nos retorna un valor “0” en la respuesta xml ya que el primer caracter del hash es “e” y la esta comparando con el caracter “e”. Por tanto la condición es verdadera.

Ejecución de la comparación del segundo carecter del hash de la contraseña del administrador.

Si cambiamos la posición del caracter del hash que deseamos comparar (ahora es el valor “6”) pero no cambiamos el caracter con el que queremos contrastar (de momento sigue siendo “e”). Observamos que en la respuesta se nos entrego un “90000 ”indicando que dicha condición es falsa.

Ejecución de la comparación del segundo carecter del hash de la contraseña del administrador.

Pero si cambiamos el valor “C” (valor contrastado) al caracter que realmente es comparable proveniente del hash, el sistema nos retorna el valor “0” , indicando que la condición es verdadera.

Ejemplo de script para extraer todos los caracteres de los hash de las contraseñas de los usuarios.

Es así entonces, como con un poco de creatividad se puede realizar un script el cual recorra cada uno de los caracteres del hash y extraiga cada uno de ellos.

De igual manera el exploit fue publicado den exploitDB bajo el siguiente ID:

https://www.exploit-db.com/exploits/49340

Sin embargo, no es necesario irnos tan lejos con script desarrollados por el propio atacante. Ya que mediante herramientas automatizadas como SQLMap es posible aprovechar dicha vulnerabilidad y navegar por la data de la base de datos de manera mas libre.

Para efectos de esta prueba, solamente se extrajeron los nombres de las bases de datos, pero ya es de tu preferencia que tan lejos quieras llegar.

Cabe resaltar nuevamente que este post es realizado con fines EDUCATIVOS e INVESTIGATIVOS y que no me hago responsable del mal uso de dicha información por parte de los lectores.

El respectivo reporte realizado al equipo de Mantis BT esta en el siguiente link: https://www.mantisbt.org/bugs/view.php?id=27495

Resumen:
Producto: MantisBT
Proveedor: MantisBT Team
Versión vulnerable : 2.24.3
Tipo de vulnerabilidad: Blind SQL injection
CWE: CWE-89 / CWE-943
CVE: CVE-2020–28413
OWASP TOP 10: A1 web top 10 / Api8 api top 10
Mantis_ID: 0027495
Nivel de riesgo: Medio
CVSS 3.1: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N

La versión 2.24.4 contiene la solución a dicha vulnerabilidad.
https://www.mantisbt.org/bugs/changelog_page.php?version_id=358

Aquí dejo también los links pertenecientes a CVE MITRE y NIST:
https://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-28413
https://nvd.nist.gov/vuln/detail/CVE-2020-28413

Recuerda que cualquier duda ante el tema o alguna observación, son bienvenidas en la caja de comentarios o en mis redes sociales.

Hasta la próxima.

--

--