Descubrí un formulario web que acepta un correo electrónico y un código de promoción, y si el código es válido, recibirías un regalo en el juego. Desafortunadamente, no tenía códigos de promoción, sin embargo, encontré que este formulario contiene vulnerabilidades de inyección de SQL en ambos campos.
Disclaimer: Después de descubrir la vulnerabilidad inicial, la reporté al propietario del servidor y le pedí permiso para buscar, así que me otorgó el permiso y eliminó la forma vulnerable de acceso público.
He logrado reconstruir la lógica remota (en pseudocódigo):
handle(code, email) {
cursor = 'SELECT type, date FROM codes WHERE code='${code}' LIMIT 1';
if (cursor.rows != 0) {
'INSERT INTO codes
(code, type, email, date)
VALUES ('already_used', 0, '${email}', NOW())';
return 'Error: promo code has already been used';
}
if (cursor.date != NULL) {
'INSERT INTO codes
(code, type, email, date)
VALUES ('invalid', 0, '${email}', NOW())';
return 'Error: invalid promo code';
}
'UPDATE codes SET email='${email}', date=NOW() WHERE code='${code}'';
if (cursor.type == 2) {
send_mail(to=email, "<html>You have used promo ${code}, but the cake is a lie.</html>");
}
return 'HUGE SUCCESS! You will now get a cake.';
}
Y el esquema de la tabla codes
es aparentemente como sigue:
CREATE TABLE codes (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
code CHAR(16) NOT NULL,
type INT NOT NULL,
email VARCHAR(256),
date DATETIME
);
La inyección en el campo code
es ciega, por lo que puedo verificar si SQL expr
es verdadero ("promo ya usado") o falso ("promo inválido"). Estoy usando ' OR (${expr}) #
vector.
He expandido este canal para permitir la transmisión de resultados de cadenas. Primero detecto la longitud del resultado a través de ' OR ((${expr}) REGEXP '^.{N,}') #
cheques, utilizando la búsqueda binaria para encontrar N
apropiado. Luego revelo el resultado char por char usando consultas de rango como ' OR (CONVERT((${expr}) USING BINARY) REGEXP '^id[[.comma.]][f-k]') #
a través de la búsqueda binaria para cada char.
De esta forma he aprendido VERSION()
(MySQL 5.1.73-cll), el esquema exacto de la tabla codes
a través de la introspección information_schema
, nombres de otras bases de datos, etc. No tengo el permiso FILE
, entonces no puedo usar LOAD_FILE()
.
Aprovechando la inyección en el campo email
, he logrado registrar un nuevo código de promoción a través de ', NOW()), ('fake-promo', 2, NULL, NULL) #
vector.
Debido a que no se suprimieron las advertencias, conozco la ruta absoluta al script PHP que maneja estas solicitudes. Desafortunadamente, como carezco de permiso de FILE
, no conozco la forma de obtener su fuente. De las advertencias también puedo decir que el script usa las API mysql_query()
y mysql_num_rows()
obsoletas.
La secuencia de comandos envía correos electrónicos que tienen un conjunto de encabezados X-Source-Args
y X-Source-Dir
(y PHP parece ejecutarse bajo apache2), por lo que puedo concluir que los correos electrónicos se envían a través de la función PHP mail()
.
Entonces, ¿cuáles son mis pasos adicionales para explorar el sistema?
Estoy sugiriendo que mail
podría explotarse con algún tipo de inyección de shell a través del destinatario falsificado email
, sin embargo, no estoy seguro de por dónde empezar.
Además, ¿algún otro vector de ataque tal vez? ¿Es posible hacer UPDATE
, INSERT
o DELETE
en tablas arbitrarias? ¿Rompiendo mi canal de comunicación de un bit? ¿Leyendo la fuente del script PHP?