Joven hacker sonriendo

Prevenir SQLi Mediante Sentencias Parametrizadas

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la programación segura en COBOL al evitar que un intruso pueda realizar ataques de inyección sql para acceder a información relevante almacenada en la base de datos.

Necesidad

Prevenir SQL Injection a través del uso de sentencias parametrizadas y sentencias estáticas en COBOL.

Contexto

A continuación se describe las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación en COBOL.

  2. La aplicación hace uso de SQL en combinación con datos que pueden ser establecidos por el usuario.

  3. Debe validarse la entrada de información antes de ser usada[1].

Solución

  1. El uso de sentencias dinámicas en las aplicaciones pueden generar problemas de seguridad, y más cuando el usuario es quien ha establecido los valores que se usan en la sentencia. Se creará una aplicación en COBOL vulnerable a inyección de SQL haciendo uso de construcciones dinámicas.

  2. La siguiente tabla representa los datos almacenados en la tabla SQLTEST:

    Tabla 1. Datos de la tabla SQLTEST.
    USUARIO CONTRASEÑA

    admin

    123456Abcd$

    test

    test

    FLUIDsignal

    FLUIDsignalgroup2012

  3. Se da inicio al programa mediante la división IDENTIFICATION DIVISION[2]:

    cobolsql.cob
    1
    2
    3
    4
    5
           IDENTIFICATION DIVISION.
          ******************
          * Identification *
          ******************
           PROGRAM-ID. COBOLSQL.
    
  4. La división DATA DIVISION[3] contiene las variables a usar:

    • W01-USERNAME: es el nombre de usuario recibido desde la entrada estándar.

    • W02-PASSWORD: es la contraseña del usuario obtenida a partir de la sentencia dinámica.

    • W03-SQLCMD: contiene la sentencia dinámica.

    1
    2
    3
    4
    5
    6
    7
    8
    9
          ********
          * Data *
          ********
           DATA DIVISION.
    
           WORKING-STORAGE SECTION.
           01 W01-USERNAME PIC X(64) VALUE "".
           01 W02-PASSWORD PIC X(64) VALUE "".
           01 W03-SQLCMD   PIC X(128) VALUE "".
    
  5. Se copia la estructura de datos SQLCA, la cual contiene las variables necesarias para el manejo de errores en las sentencias SQL:

    1
           COPY SQLCA OF QSYSINC-QCBLLESRC.
    
  6. Dentro de la división PROCEDURE DIVISION[4] se obtiene el nombre del usuario desde la entrada estándar y se lo almacena en la variable W01-USERNAME:

    1
    2
    3
    4
    5
    6
    7
          ********
          * Main *
          ********
           PROCEDURE DIVISION.
           MAIN.
               DISPLAY "Username: ".
               ACCEPT W01-USERNAME.
    
  7. El propósito de la sentencia dinámica es obtener la contraseña para el usuario ingresado:

    1
    SELECT contrasenia FROM SQLTEST WHERE usuario = "user_input"
    
  8. Concatenamos parte de la sentencia con el nombre del usuario recibido de la entrada estándar, en este punto es donde se activa la vulnerabilidad al no manejar un filtro adecuado:

    1
    2
    3
    4
    5
           STRING "SELECT contrasenia" SPACE
                  "FROM SQLTEST" SPACE
                  "WHERE usuario = """ W01-USERNAME """"
                  DELIMITED BY SIZE
                  INTO W03-SQLCMD.
    
  9. Declaramos la sentencia STMT:

    1
    2
    3
           EXEC SQL
               DECLARE STMT STATEMENT
           END-EXEC
    
  10. Se prepara Q1 en base a la sentencia dinámica:

    1
    2
    3
           EXEC SQL
               PREPARE STMT FROM :W03-SQLCMD
           END-EXEC
    
  11. El cursor sirve para recorrer todos los registros obtenidos a partir de la sentencia:

    1
    2
    3
           EXEC SQL
               DECLARE C1 CURSOR FOR STMT
           END-EXEC.
    
  12. Se abre el cursor:

    1
    2
    3
           EXEC SQL
               OPEN C1
           END-EXEC.
    
  13. Ejecutamos la sentencia y enviamos el resultado a la variable W02-PASSWORD:

    1
    2
    3
           EXEC SQL
               FETCH C1 INTO :W02-PASSWORD
           END-EXEC.
    
  14. Realizamos el mismo procedimiento hasta que no se encuentre más registros:

    1
    2
    3
    4
    5
    6
    7
           PERFORM UNTIL SQLCODE NOT = 0
               DISPLAY "Resultado: " W02-PASSWORD
    
               EXEC SQL
                   FETCH C1 INTO :W02-PASSWORD
               END-EXEC
           END-PERFORM.
    
  15. Cerramor el cursor y terminamos el programa:

    1
    2
    3
    4
    5
           EXEC SQL
               CLOSE C1
           END-EXEC.
    
           STOP RUN.
    
  16. Ingresamos un usuario diseñado especialmente para inyectar código SQL:

    1
    " OR 1=1-- -
    
  17. Esto permite romper la sentencia, quedando como:

    1
    SELECT contrasenia FROM SQLTEST WHERE usuario = "" OR 1=1-- -"
    
  18. El resultado es el listado de las contraseñas para todos los usuarios:

    1
    2
    3
    4
    5
    Resultado: 123456Abcd$
    
    Resultado: test
    
    Resultado: FLUIDsignalgroup2012
    
  19. Esta vulnerabilidad puede ser corregida haciendo uso de sentencias estáticas o a través del uso de la palabra clave USING para parametrizar los valores.

Sentencias parametrizadas

En el anterior código se deben corregir dos bloques:

  1. El primero es donde inicializamos la sentencia dinámica[5], ahora parametrizamos el valor dinámico haciendo uso del carácter "?". Esto le permite internamente al motor de la base de datos, buscar el valor ingresado por el usuario sin romper la sentencia como se vio anteriormente:

    1
    2
    3
    4
    5
           STRING "SELECT contrasenia" SPACE
                  "FROM SQLTEST" SPACE
                  "WHERE usuario = ?"
                  DELIMITED BY SIZE
                  INTO W03-SQLCMD.
    
  2. Para sentencias SELECT, hacemos uso de la palabra clave USING en el momento de abrir el cursor:

    1
    2
    3
           EXEC SQL
               OPEN C1 USING :W01-USERNAME
           END-EXEC.
    
  3. Al ingresar de nuevo la inyección, el programa procesa correctamente la cadena y retorna una lista nula de contraseñas.

Sentencias estáticas

  1. El uso de sentencias estáticas es una forma segura de combinar sentencias con la entrada del usuario. En este caso no es necesario declarar y preparar sentencias, basta con construir la petición en el mismo código de COBOL al momento de declarar el cursor:

    1
    2
    3
    4
    5
    6
           EXEC SQL
               DECLARE C1 CURSOR FOR
               SELECT contrasenia
               FROM SQLTEST
               WHERE usuario = :W01-USERNAME
           END-EXEC.
    

Descargas

Puedes descargar el código fuente pulsando en los siguientes enlaces:

  1. cobolsql.cob contiene la implementación del código vulnerable.

  2. preparedstatement.cob contiene la implementación del código con la vulnerabilidad corregida mediante sentencias parametrizadas.

  3. staticstatement.cob contiene la implementación del código con la vulnerabilidad corregida mediante sentencias estáticas.




Haz un comentario

Estado de los servicios - Términos de Uso