PHP: ¿Estoy libre de la inyección SQL? [duplicar]

3

Permítanme comenzar con esto diciendo que este es mi primer intento de entender el saneamiento de entrada, y en este momento todavía soy un novato en PHP. He creado un registro / inicio de sesión rápido en el sitio web para probar algunas cosas, y estoy teniendo problemas para romperlo. Estoy tratando de explotar mi propio sitio web para poder averiguar (solo, con suerte) cómo solucionarlo.

He investigado un poco, pero no importa cuántas cosas diferentes intente, simplemente no puedo afectar mi sitio. Lo que estoy tratando de hacer es hacer cualquier cosa, desde colocar tablas hasta obtener información del usuario, cualquier cosa que pueda representar un riesgo. A continuación se muestra mi código simplificado. Estoy bastante seguro de que hay espacio para la inyección, pero honestamente no puedo encontrarlo. Además, no prestes atención a mi uso de md5. ¡Sé que hay mejores cosas por ahí!

Suponga que $user y $password provienen de un formulario sin nada hecho.

$password = md5($password);

$query = mysqli_query($con,"SELECT * FROM users WHERE name='$user'");
$numrows = mysqli_num_rows($query);

if ($numrows == 1){

    $row = mysqli_fetch_assoc($query);
    $dbpass = $row['password'];
    $dbuser = $row['name'];

    if ($password == $dbpass){  
        $_SESSION['name'] = $dbuser;
    }
}

Lo que estoy buscando es cualquier vulnerabilidad que pueda usar para aprender. Si tiene sugerencias sobre cómo protegerlo, eso también se agradecería.

Saludos.

    
pregunta 07.04.2015 - 21:41
fuente

2 respuestas

4

¿Has intentado incluir citas para romper esto? Por lo que veo aquí, en realidad debería causar un error horrible si se pasa una cita. A veces hay que juguetear un poco antes de que ocurra el bloqueo.

$query = mysqli_query($con,"SELECT * FROM users WHERE name='$user'");

Aquí está mi intento de romper esto:

'OR 1=1--

Parece que no ha utilizado consultas parametrizadas, por lo tanto, deja su código abierto a inyección. Tenga en cuenta que lo anterior es solo una prueba de concepto: con su código actual no le permitirá hacer mucho lo que puede ver.

Su código valida los resultados de la consulta después que se ejecuta, lo cual es muy peligroso. Usted está abierto a los ataques de tiempo de SQL por lo menos.

Las instrucciones para implementar consultas parametrizadas se encuentran a continuación: enlace enlace

Además:

1) Como principiante, está mucho mejor usando un ORM prefabricado.

2) La limpieza de entrada es clave. Siempre haga esto antes de que los datos entren en una consulta.

3) Si está haciendo esto para aprender seguridad, intente un juego de guerra prefabricado de una clase similar: puede proporcionar una mejor guía para aprender a explotar el error.

Descargo de responsabilidad: No conozco demasiado bien esta API de PHP en particular, por lo que si está realizando consultas parametrizadas de forma predeterminada, no podría decirlo o no lo sabía.

    
respondido por el baordog 07.04.2015 - 22:09
fuente
3

Sí, está completamente abierto a la inyección de SQL, por ejemplo, a través de validUsername' injectionPayload %23 , lo que le daría esta consulta (el # (codificado como %23 cortes del ' restante):

SELECT * FROM users WHERE name='validUsername' injectionPayload #'

En Outfile

dependiendo de los derechos del usuario, puede escribir contenido en un archivo con esta carga útil:

-1' UNION ALL SELECT 'injected string' INTO OUTFILE '/path/to/file.php' #

Inyección ciega (con credenciales de usuario válidas)

Con su código actual, es difícil obtener datos directamente de la base de datos. Solo utiliza la contraseña y el nombre, y ambos deben ser correctos, ya que de lo contrario su código no funcionará.

Pero este es un script de inicio de sesión, por lo que creará un resultado diferente para las credenciales de usuario válidas y para las no válidas, ¿correcto?

Como ese es el caso, se puede usar la inyección de SQL ciego estándar (si tiene credenciales de usuario válidas). Las cargas útiles se verían así:

// mysql 4 or 5?
validUsername' AND substring(version(), 1, 1)='5
validUsername' AND substring(version(), 1, 1)='4

// is first char of database hash smaller than ascii 100?
validUsername' AND ascii(substring((SELECT password FROM mysql.user LIMIT 0,1),1,1))<100 #
[...]

Y así sucesivamente. Lo que sucede es que combina la consulta con una pregunta booleana. Por ejemplo, does the database version start with a 5? . Si la respuesta es true , entonces todo funciona normalmente y usted está conectado. Si la respuesta es false , no iniciará sesión, ya que la consulta no devolverá los datos y, por lo tanto, $numrows == 1 es falso.

Crea muchas solicitudes (especialmente para un script de inicio de sesión), pero es posible extraer datos.

Anulación de inicio de sesión

También debería ser posible omitir la página de inicio de sesión (inicie sesión como cualquier usuario si conoce el nombre de usuario pero no la contraseña):

-1' UNION ALL SELECT name, md5('password') FROM users WHERE name='attackedUser' #

No probé esto, pero qué debería suceder: el -1 elimina los resultados legítimos de su consulta, el union agrega los resultados de la consulta inyectada, y la consulta inyectada obtiene el nombre de un usuario atacado y establece la contraseña en password para esta solicitud (y usted envía password como el valor POST para la contraseña).

Inyección ciega (sin credenciales de usuario válidas)

Como hemos visto en la sección anterior, realmente podemos establecer la contraseña, por lo que no necesitamos credenciales válidas para una inyección ciega. Puedes combinar el primer y el segundo atacante descritos, o puedo imaginar algo como esto:

-1' UNION ALL SELECT 1,md5(user) FROM mysql.user LIMIT 0,1 #

Esto es solo una cadena simplificada para mostrar la idea: nuevamente, elimina los datos válidos con -1 , agrega su consulta y selecciona como la contraseña los datos que desea extraer (md5 con hash, como el que se proporciona) la contraseña será hash). Luego, como la contraseña que envía a través de POST, envía el valor que adivina (por ejemplo, root ). Esos dos valores serán comparados a través de $password == $dbpass . Si ha iniciado sesión, su conjetura es correcta, si no, entonces es incorrecto. Para adivinar los hashes de contraseña, puede utilizar el enfoque de subcadena desde arriba.

Conclusión

Incluso si piensa que la inyección de SQL que tiene no divulga ninguna información, debe usar declaraciones preparadas, ya que podría haber alguna forma de divulgar la información. E incluso si no existe en este momento, su código podría cambiar en el futuro.

    
respondido por el tim 07.04.2015 - 22:18
fuente

Lea otras preguntas en las etiquetas