Esta pagina se ve mejor con JavaScript habilitado

Introducción a las inyecciones SQL

 ·  🎃 kr0m

Una inyección sql consiste en la ejecución de sentencias sql en una base de datos, sentencias que en primera instancia no deberían de ser permitidas por la aplicación, esto es debido a que el programador no tomó las precauciones necesarias filtrando las variables de entrada por parte del usuario. De este modo se pueden conseguir diversidad de resultados, desde averiguar el motor de base de datos utilizado hasta ejecutar comandos en el propio sistema operativo. En este artículo explicaré de forma muy sencilla como explotar una vulnerabilidad web en una aplicación diseñada para tal fin, de este modo veremos con código real el funcinamiento de dicho tipo de ataques.

Nuestra interfaz web servirá como punto de acceso a la base datos de la cia que relaciona los nombre en clave de los agentes con sus nombres reales y sus domicilios por lo tanto una fuga de información de esta base de datos puede ser catastrófica, la web la compondrán varios ficheros, cabecera, pie, funciones y login.

Los ficheros en cuestión son.

Cabecera:

vi /var/www/ciaweb/cabecera.html

<!DOCTYPE html>
<html lang="es_ES">
  <head>
    <title>CIA ultra secret webpage</title>
    <meta charset="utf-8">
    <meta name="description" content="CIA ultra secret webpage">
    <meta name="author" content="Kr0m">
    <meta name="robots" content="all">
    <meta name="cache" content="cache">
    <style>
      body { padding-top: 60px; }
    </style>
    <link href="/bootstrap/css/bootstrap.css" rel="stylesheet">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="/bootstrap/js/bootstrap.min.js"></script>
  </head>
  <body>

Pie:

vi /var/www/ciaweb/pie.html

  </body>
</html>

Funciones:

vi /var/www/ciaweb/dbfunctions.php

<?php
function conecta($host, $user, $pass, $db) {
    $conexion = mysqli_connect($host, $user, $pass, $db) or die(mysqli_error($conexion));
    return $conexion;
}

function consulta($id, $query){
    $resultado = mysqli_query($id, $query) or die(mysqli_error($id));
    return $resultado;
}

Login:

vi /var/www/ciaweb/login.php

<?php
require("/var/www/ciaweb/cabecera.html");
require("/var/www/ciaweb/dbfunctions.php");
if (isset($_POST['username'])) {
    $id_conexion =  conecta('localhost', 'ciadb_user', 'XXXXXXXXXXXXXXXXX', 'ciadb');
    $resultado = consulta($id_conexion, "SELECT * FROM usuarios WHERE nombre='$_POST[username]' && password='$_POST[password]'", $id_conexion);
    $N = mysqli_num_rows($resultado);
        if ($N >0){
                $fila = mysqli_fetch_assoc($resultado);
                $resultado = consulta($id_conexion, "SELECT * FROM noc", $id_conexion);
                ?>      
                <div class="well">
                  <table class="table table-striped table-hover">
                    <thead>
                      <tr>    
                        <th>ID</th>
                        <th>Nombre en clave</th>
                        <th>Nombre real</th>
                        <th>Domicilio</th>
                        <th style="width: 36px;"></th>
                      </tr>   
                   </thead>
                   <tbody>
                <?php   
                while ($fila = mysqli_fetch_assoc($resultado)) {
                ?>      
                        <tr>    
                                <td><?php echo $fila['id']?></td>
                                <td><?php echo $fila['nombre_en_clave']?></td>
                                <td><?php echo $fila['nombre_real']?></td>
                                <td><?php echo $fila['domicilio']?></td>
                    </tr>   
                <?php   
                }       
    } else {
        ?>
        <section>
            <div class="container login">
                <div class="row ">
                    <div class="center span4 well">
                        <legend>Ultra secret Cia Database</legend>
                        <form method="POST" action="/login.php" accept-charset="UTF-8">
                          <input type="text" id="username" class="span4" name="username" placeholder="Nombre de usuario" />
                          <input type="password" id="password" class="span4" name="password" placeholder="Password" />
                          <button type="submit" name="submit" class="btn btn-primary btn-block">Sign in</button>
                        </form>
                    </div>
                </div>
            </div>
            <p class="text-center muted ">Ultra secret Cia Database</p>
        </section>
        <?php
    }
} else {
    ?>
    <section>
        <div class="container login">
            <div class="row ">
                <div class="center span4 well">
                    <legend>Ultra secret Cia Database</legend>
                    <form method="POST" action="/login.php" accept-charset="UTF-8">
                      <input type="text" id="username" class="span4" name="username" placeholder="Nombre de usuario" />
                      <input type="password" id="password" class="span4" name="password" placeholder="Password" />
                      <button type="submit" name="submit" class="btn btn-primary btn-block">Sign in</button>
                    </form>
                </div>
            </div>
        </div>
        <p class="text-center muted ">Ultra secret Cia Database</p>
    </section>
    <?php
}
require("/var/www/ciaweb/pie.html");

NOTA: Para darle atractivo a la interfaz se ha utilizado bootstrap .

Con esto ya tenemos la parte web, ahora creamos la base de datos:

CREATE DATABASE ciadb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE ciadb;
CREATE TABLE noc ( id int(11) NOT NULL, nombre_en_clave varchar(80) NOT NULL, nombre_real varchar(80) NOT NULL, domicilio varchar(80) NOT NULL);
CREATE TABLE usuarios ( id int(11) NOT NULL, nombre varchar(80) NOT NULL, password varchar(80) NOT NULL);

Insertamos usuarios:

INSERT INTO usuarios (id, nombre, password) VALUES ('0','maxsnake','123');
INSERT INTO usuarios (id, nombre, password) VALUES ('1','lolman','321');

INSERT INTO noc (id, nombre_en_clave, nombre_real, domicilio) VALUES ('0', 'maxsnake', 'jhon', 'calle salamandra 123');
INSERT INTO noc (id, nombre_en_clave, nombre_real, domicilio) VALUES ('1', 'lolman', 'logic', 'calle margarita 321');

Asignamos permisos:

USE mysql
GRANT ALL PRIVILEGES ON ciadb.* to ciadb_user@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXXXXX';
FLUSH PRIVILEGES;

Configuramos el vhost tal que así:

<VirtualHost *:80>
        ServerAdmin kr0m@alfaexploit.com
        DocumentRoot /var/www/ciaweb
        ServerName www.ciaweb.com
        SuexecUserGroup ABC DEF
        ErrorLog /var/log/apache2/error_ciaweb.log
        CustomLog /var/log/apache2/access_ciaweb.log combined
        DirectoryIndex login.php
        ScriptAlias /local-bin /var/www/cgi-bin/ABC
        AddHandler application/x-httpd-php5 php
        Action application/x-httpd-php5 /local-bin/php-cgi

        <Directory "/var/www/cgi-bin/ABC">
            Options -Indexes ExecCGI
            Order allow,deny
            Allow from all
        </Directory>

        <Directory "/var/www/ciaweb">
            options -Indexes ExecCGI FollowSymLinks
            AllowOverride All
            order allow,deny
            Allow from all
        </Directory>
</VirtualHost>

Ahora editamos nuestro fichero de hosts para que www.ciaweb.com resuelva a la ip de nuestro servidor:

vi /etc/hosts

A.B.C.D     www.ciaweb.com

Para poder ver con claridad lo que ocurre entre bambalinas habilitamos el log de MySQL:

set global general_log='ON';
set global general_log_file='/var/run/mysqld/mysqld.log';
exit
tail -f /var/run/mysqld/mysqld.log

Accedemos a la web desde  un navegador y debería de aparecer nuestra app:
http://www.ciaweb.com/

Si hacemos login con algún usuario podemos ver los datos de todos los agentes:

Ahora procedemos con la inyección:


Que ha ocurrido, pero como es posible si en el código se comprueba el usuario y password contra la base de datos?

Como podemos ver toda la seguridad de la aplicación radica en:

$resultado = consulta($id_conexion, "SELECT * FROM usuarios WHERE nombre='$_POST[username]' && password='$_POST[password]'", $id_conexion);
    $N = mysqli_num_rows($resultado);
    if ($N >0){

El problema está en que no se parsea de ningún modo la entrada del usuario pudiendo introducir como usuario ’ or ‘1 = 1’; –  lo que está haciendo es hacer que la condición del select sea siempre verdadera para todas las filas mediante el 1 = 1, de este modo el select ofrecerá como resultado todos los registros de la tabla, cumpliendo así el requisito impuesto por código de N>0 , finalmente termina la query sql de forma correcta e indica que lo que hay después del ; son comentarios, de este modo la query ejecutada por la base de datos quedaría así:

SELECT * FROM usuarios WHERE nombre='' or '1 = 1'; -- ' && password=''

En el código se comprobaría que efectivamente la query anterior devuelve resultados dejando a los agentes al descubierto :)

Si te ha gustado el artículo puedes invitarme a un RedBull aquí