Estoy estudiando infosec, comenzando a abrirme camino a través de algunos de los juegos de guerra en línea que hay. Recientemente he estado trabajando en Natas por OverTheWire ( enlace ) y hasta ahora ha sido muy divertido. Mi pregunta está relacionada con uno de los desafíos en el juego.
** SPOILERS PARA EL JUEGO SIGUE **
En uno de los niveles, tengo mi primer encuentro con mysql. El código de fondo se ve así (el juego te permite ver el código php para cada función, al menos al principio):
<?
if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas14', '<censored>');
mysql_select_db('natas14', $link);
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
if(mysql_num_rows(mysql_query($query, $link)) > 0) {
echo "Successful login! The password for natas15 is <censored><br>";
} else {
echo "Access denied!<br>";
}
mysql_close($link);
} else {
?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<? } ?>
Comencé a investigar la inyección de SQL, e intenté algunas cosas básicas como foo OR 1=1
como entrada, pero no conseguí nada. Miré el código e intenté insertar citas con mi entrada, lo que me arrojaría errores cuando tuviera una cantidad impar de citas en la entrada, pero no sabía a dónde ir desde allí.
Finalmente, comencé a trabajar en la página de OWASP sobre las pruebas de inyección de SQL, y durante la prueba de sus ejemplos, encontré algo que se decía que debía intentar:
$username = 1' or '1' = '1
$password = 1' or '1' = '1
Eso no funcionó, pero por alguna razón, mi intuición dijo que cambiara el primer '
a "
en la entrada $ username, y el último '
a "
en la entrada $ password. Así se convirtió
$username = 1" or '1' = '1
$password = 1' or '1' = "1'
Y ... funcionó. Satisfecho la condición para este bloque.
if(mysql_num_rows(mysql_query($query, $link)) > 0) {
echo "Successful login! The password for natas15 is <censored><br>";
} else {
echo "Access denied!<br>";
}
y me dio la contraseña para el siguiente nivel.
Mi pregunta es ... ¿POR QUÉ? No sé lo suficiente sobre la inyección de SQL ya que obviamente soy un noob, pero no entiendo lo que está sucediendo exactamente en el servidor de SQL con esa entrada en particular.
Para reiterar, la consulta tiene este aspecto (utilicé la función de depuración en el código anterior para imprimirla):
Executing query: SELECT * from users where username="1" or '1' = '1" and password="1' or '1' = "1"'
¿Cuál es el comportamiento de esta consulta, y cómo lo está evaluando sql para darme una salida VERDADERA en ambas entradas (al menos supongo que eso es lo que está haciendo ...)? Siempre que cambio el "
a '
en cualquier otra configuración dentro de la consulta, falla. Y el hecho de que haya un número impar de "
y '
dentro de la consulta me está volviendo loco.
Cualquier consejo o explicación sería muy apreciado. ¡Siento que realmente no habré pasado este nivel hasta que entienda lo que está sucediendo!