Analyzing a (new?) macOS DNS Hijacker: OSX/MaMi

2018 is barely two weeks old, and already it looks like we’ve got new piece of macOS malware! Hooray ūüôā

Want to play along? I’ve shared both the malware’s binary executable (‘MaMi‘), which can be downloaded¬†here(password:¬†infect3d).

Please don’t infect yourself!

Background
Earlier today (01/11), someone on MalwareBytes’ forum¬†created a post¬†titled “DNS Hijacked”:


So far nobody has answered or offered to help ‘MikeOfMaine’ ūüôĀ And, as far as I’m aware there haven’t been any recent macOS malware that hijacks DNS settings – so I was intrigued! So without further adieu, let’s dive in to analyzing (what I’m calling)¬†OSX/MaMi!

Analysis
Though currently I am unaware of the malware’s infection vector, it is hosted on various sites such as http://regardens.info:

curl -L http://regardens.info/ > MaMi
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   178    0   178    0     0    381      0 --:--:-- --:--:-- --:--:--   381
100  552k  100  552k    0     0   314k      0  0:00:01  0:00:01 --:--:--  581k

MacBookPro:Downloads patrickw$ file MaMi
MaMi: Mach-O 64-bit executable x86_64


As shown by¬†WhatsYourSign, nothing too special about the file; it’s an unsigned Mach-O 64-bit executable:


As is often the case with new malware, it’s¬†currently marked as ‘clean’¬†by all 59 engines on VirusTotal (this will hopefully change shortly as AV products start adding detections):


And speaking of ‘new’ if we load the malware’s binary in a disassembler, we find an app version of¬†1.1.0, which (due to such low version number), may seem to indicate the malware likely hasn’t been around for too long.

000000010003818f  db  "AppVersion: %@\nAppBuild: %@", 0 
00000001000381ab  db  "1.1.0", 0                        
00000001000381b1  db  "0", 0                            

Before we dig further into the disassembly, let’s dump the Objective-C class names and methods, as often this can allow us to quickly gain insight the malware’s (likely) capabilities or at least guide our analysis.

I use J. Levin’s invaluable¬†jtool¬†utility to dump such info:

$ ./jtool -d objc -v MaMi

@interface AppDelegate 
...
/* 2 - 0x100001e0b */ - setupCert;
...
/* 7 - 0x1000027bc */ - setupDNS;
...
/* 9 - 0x100002a97 */ - takeScreenshotAt:;
...
/* 22 - 0x1000049d8 */ - mouseClick:;
...
/* 24 - 0x100004ac5 */ - runAppleScript:;


@interface SBMaMiSettings :
...
/* 2 - 0x10000518b */ - initMaMiSettings;
...
/* 9 - 0x100005385 */ - programArguments;
...
/* 11 - 0x1000053a7 */ - runAtLoad;
...
/* 25 - 0x10000548f */ - launchOnlyOnce;


@interface SBNetwork :
...
/* 0 - 0x10000d2e5 */ + downloadFile:atPath:;
/* 1 - 0x10000d4a8 */ + sendAsyncRequestWithUrls:andMethod:andBody:;


@interface SBFileSystem : ?
/* 0 - 0x10002407e */ + writeString:toPath:;
...
/* 8 - 0x1000247fb */ + runCmd:andPipeToCmd:withParams:andParams2:; 
/* 9 - 0x100024b07 */ + runCmd:withParams:;
/* 10 - 0x100024b23 */ + runCmd:withParams:andUser:;


@interface SBCryptoSystem : ?
/* 0 - 0x100026731 */ + isAdmin;  // Protocol 129824ad7
/* 1 - 0x100026745 */ + elevatePrivilegesWithParams:;  // Protocol 1298247ca
/* 2 - 0x1000267aa */ + relaunchWithPrivilegesAndParams:;

Some very interesting methods! Of course we’ll continue our analysis to confirm, but seems this malware is indeed a ‘dns hijacker’ (method:¬†setupDNS), with a host of other abilities such as:

    • taking screenshots

 

    • generating simulated mouse events

 

    • perhaps persists as a launch item (programArguments,¬†runAtLoad)

 

    • downloading & uploading files

 

    • executing commands

 

  • …and more!

Jumping back to the disassemlby, within the application’s main entrypoint (-[AppDelegate applicationDidFinishLaunching:]), we see a massive encrypted string that is passes to a¬†setDefaultConfiguration:¬†method:

 [SBConfigManager setDefaultConfiguration:@"uZmgulcipekSbayTO9ByamTUu_zVtsflazc2Nsuqgq0dXko
 OzKMJMNTULoLpd-QV9qQy6VRluzRXqWOGscgheRvikLkPRzs1pJbey2QdaUSXUZCX-UNERrosul22NsW2vYpS7HQO4
 VG5l8qic3rSH_fAhxsBXpEe557eHIr245LUYcEIpemnvSPTZ_lNp2XwyOJjzcJWirKbKwtc3Q61pDwTzKvE0..."];                            

Applying some classified decryption methods I learned as an intern working in NSA’s Cryptanalysis and Exploitation Services (CES) group – it was trivial to decrypt this configuration data. I’m totally kidding – not about the internship – but about how to decrypt. Just step over that method in a debugger (lldb) and the data is sitting decrypted in memory:

# lldb MaMi
(lldb) target create "MaMi"
Current executable set to 'MaMi' (x86_64).

...

(lldb) po $rax
{
    defaults =     {
        affiliate = "";
        build = 0;
        "compilation_id" = 0;
        "confirmation_end_time" = 0;
        "confirmation_start_time" = 0;
        "download_complete_time" = 0;
        "download_location" = "";
        "download_retry_count" = 0;
        "download_start_time" = 0;
        "download_url" = "";
        "exception_id" = 0;
        "execute_location" = "";
        "execution_end_time" = 0;
        "execution_start_time" = 0;
        "exit_code" = 0;
        "external_id" = 0;
        "file_crc" = 0;
        "hardware_id" = 0;
        "hosts_active" = "";
        "installer_id" = 0;
        "is_admin" = false;
        "old_secondary_dns" = "";
        "os_build" = 0;
        "os_id" = 0;
        "product_id" = 0;
        "product_name" = "";
        "publisher_id" = 0;
        "register_date" = 0;
        "register_dsrc" = 0;
        "report_id" = 0;
        "run_args" = "";
        "screen_x" = 0;
        "screen_y" = 0;
        "secondary_dns" = "";
        "service_pack" = 0;
        "session_id" = 0;
        status = 0;
        "step_id" = 0;
        tag = "";
        tracker = "";
        "user_time" = 0;
        "validate_end_time" = 0;
        "validate_start_time" = 0;
        version = 0;
    };
    dnsChanger =     {
        affiliate = "";
        "blacklist_dns" =         (
        );
        encrypt = true;
        "external_id" = 0;
        "product_name" = dnsChanger;
        "publisher_id" = 0;
        raw = true;
        reports =         {
            "dnsChanger_activity" =             {
                async = false;
                body = "r={dnsChanger->reports->dnsChanger_activity->template}&rc={dnsChanger}";
                "connection_timeout" = 5;
                domains =                 (
                    "honouncil.info",
                    "gorensin.info"
                );
                "http_headers" =                 (
                                        {
                        name = "Content-Type";
                        value = "application/x-www-form-urlencoded";
                    },
                                        {
                        name = "User-Agent";
                        value = "";
                    }
                );
                "query_string" = "r={dnsChanger->reports->dnsChanger_activity->template}&rc={dnsChanger}";
                "request_method" = 1;
                "request_timeout" = 5;
                "retry_count" = 2;
                "send_port" = 80;
                "send_protocol" = http;
                template =                 {
                    affiliate = "%affiliate%";
                    build = "%build%";
                    "compilation_id" = "%compilation_id%";
                    dns =                     {
                        "hosts_active" = "%hosts_active%";
                        "hosts_config" = "[templates->secondary_dns]";
                    };
                    encrypt = true;
                    "exception_id" = "%exception_id%";
                    expand = true;
                    "external_id" = "%external_id%";
                    "hardware_id" = "%hardware_id%";
                    "is_admin" = "%is_admin%";
                    "old_dns" =                     {
                        "hosts_active" = "%hosts_active%";
                        "hosts_config" = "[templates->old_secondary_dns]";
                    };
                    "os_build" = "%os_build%";
                    "os_id" = "%os_id%";
                    "product_name" = "%product_name%";
                    "publisher_id" = "%publisher_id%";
                    "register_date" = "%register_date%";
                    "register_dsrc" = "%register_dsrc%";
                    "report_id" = "%report_id%";
                    "report_name" = "dnsChanger_activity";
                    "report_type" = 8;
                    "screen_x" = "%screen_x%";
                    "screen_y" = "%screen_y%";
                    "service_pack" = "%service_pack%";
                    "session_id" = "%session_id%";
                    status = "%status%";
                    tag = "%tag%";
                    tracker = "%tracker%";
                    "user_time" = "%user_time%";
                    version = "%version%";
                };
                "url_path" = "";
            };
            "time_report" =             {
                async = false;
                body = "r={dnsChanger->reports->time_report->template}&rc={dnsChanger}";
                "connection_timeout" = 5;
                domains =                 (
                    "squartera.info"
                );
                "http_headers" =                 (
                                        {
                        name = "Content-Type";
                        value = "application/x-www-form-urlencoded";
                    },
                                        {
                        name = "User-Agent";
                        value = "";
                    }
                );
                "query_string" = "";
                "request_method" = 2;
                "request_timeout" = 5;
                "retry_count" = 2;
                "send_port" = 80;
                "send_protocol" = http;
                template =                 {
                    affiliate = "%affiliate%";
                    build = "%build%";
                    "compilation_id" = "%compilation_id%";
                    dns =                     {
                        "hosts_active" = "%hosts_active%";
                        "hosts_config" = "[templates->secondary_dns]";
                    };
                    encrypt = true;
                    "exception_id" = "%exception_id%";
                    expand = true;
                    "external_id" = "%external_id%";
                    "hardware_id" = "%hardware_id%";
                    "is_admin" = "%is_admin%";
                    "os_build" = "%os_build%";
                    "os_id" = "%os_id%";
                    "product_name" = "%product_name%";
                    "publisher_id" = "%publisher_id%";
                    "report_id" = "%report_id%";
                    "report_name" = "time_request";
                    "screen_x" = "%screen_x%";
                    "screen_y" = "%screen_y%";
                    "service_pack" = "%service_pack%";
                    "session_id" = "%session_id%";
                    status = "%status%";
                    tag = "%tag%";
                    tracker = "%tracker%";
                    "user_time" = "%user_time%";
                    "verification_id" = "%verification_id%";
                    version = "%version%";
                };
                "url_path" = "";
            };
        };
        
        "setup_dns" =         (
            "82.163.143.135",
            "82.163.142.137"
        );
    	
        "shared_storage" = "/Users/%USER_NAME%/Library/Application Support";
        "storage_timeout" = 120;
        tag = "";
        "timeout_dns" =         {
            "high_timeout" = 1;
            "low_timeout" = "0.3";
            "medium_timeout" = "0.5";
        };
        tracker = "";
    };
    "installer_id" = 1359747970602718687;
    "report_templates" =     {
        "report_config" =         {
            async = false;
            body = "";
            "connection_timeout" = 5;
            domains =             (
                "domain1.com",
                "domain2.com"
            );
            "http_headers" =             (
                                {
                    name = "Content-Type";
                    value = "application/x-www-form-urlencoded";
                },
                                {
                    name = "User-Agent";
                    value = "";
                }
            );
            "query_string" = "";
            "request_method" = 2;
            "request_timeout" = 5;
            "retry_count" = 2;
            "send_port" = 80;
            "send_protocol" = http;
        };
        "report_config2" =         {
            async = true;
            body = "";
            "connection_timeout" = 5;
            domains =             (
                "domain1.com",
                "domain2.com"
            );
            "http_headers" =             (
                                {
                    name = "Content-Type";
                    value = "application/x-www-form-urlencoded";
                },
                                {
                    name = "User-Agent";
                    value = "";
                }
            );
            "query_string" = "";
            "request_method" = 2;
            "request_timeout" = 5;
            "retry_count" = 2;
            "send_port" = 80;
            "send_protocol" = http;
            "url_path" = "";
        };
        "report_template1" =         {
            affiliate = "%affiliate%";
            build = "%build%";
            "compilation_id" = "%compilation_id%";
            dns =             {
                "hosts_active" = "%hosts_active%";
                "hosts_config" = "[templates->secondary_dns]";
            };
            "exception_id" = "%exception_id%";
            "external_id" = "%external_id%";
            "hardware_id" = "%hardware_id%";
            "is_admin" = "%is_admin%";
            "os_build" = "%os_build%";
            "os_id" = "%os_id%";
            "product_name" = "%product_name%";
            "publisher_id" = "%publisher_id%";
            "report_id" = "%report_id%";
            "screen_x" = "%screen_x%";
            "screen_y" = "%screen_y%";
            "service_pack" = "%service_pack%";
            "session_id" = "%session_id%";
            status = "%status%";
            tag = "%tag%";
            tracker = "%tracker%";
            "user_time" = "%user_time%";
            version = "%version%";
        };
        "report_template2" =         {
            affiliate = "%affiliate%";
            build = "%build%";
            "compilation_id" = "%compilation_id%";
            dns =             {
                "hosts_active" = "%hosts_active%";
                "hosts_config" = "[templates->secondary_dns]";
            };
            "exception_id" = "%exception_id%";
            "external_id" = "%external_id%";
            "hardware_id" = "%hardware_id%";
            "is_admin" = "%is_admin%";
            "os_build" = "%os_build%";
            "os_id" = "%os_id%";
            "product_name" = "%product_name%";
            "publisher_id" = "%publisher_id%";
            "register_date" = "%register_date%";
            "register_dsrc" = "%register_dsrc%";
            "report_id" = "%report_id%";
            "screen_x" = "%screen_x%";
            "screen_y" = "%screen_y%";
            "service_pack" = "%service_pack%";
            "session_id" = "%session_id%";
            status = "%status%";
            tag = "%tag%";
            tracker = "%tracker%";
            "user_time" = "%user_time%";
            version = "%version%";
        };
    };
    templates =     {
        "old_secondary_dns" =         {
            "fill_template" = "%old_secondary_dns%";
            "fill_type" = string;
        };
        "secondary_dns" =         {
            "fill_template" = "%secondary_dns%";
            "fill_type" = string;
        };
    };
    version = 1;
}

Ok, that’s a lot of configuration data! The most interesting part is probably the¬†‘setup_dns’¬†array:

 "setup_dns" = (
 "82.163.143.135",
 "82.163.142.137"
 );

…we’ll see those DNS addresses used shortly!

In lldb we can set a breakpoints on methods of interest such as setupCert and setupDNS methods;

# lldb MaMi

(lldb) b -[AppDelegate setupCert]
Breakpoint 1: where = dcdata`-[AppDelegate setupCert], address = 0x0000000100001e0b

(lldb) b -[AppDelegate setupDNS]
Breakpoint 2: where = dcdata`-[AppDelegate setupDNS], address = 0x00000001000027bc

Once these breakpoints are hit, we can step thru the each instruction, or as I had fired up ProcInfo, the open-source process monitor I recently wrote (on github:¬†ProcInfo) just let the malware run to see what it does. I’m voting for the latter as it’s almost midnight.

# ./procInfo 
starting process monitor
process monitor enabled...

pid: 1294
path: /usr/bin/security
args: (
    "/usr/bin/security",
    "add-trusted-cert",
    "-d",
    "-r",
    trustRoot,
    "-k",
    "/Library/Keychains/System.keychain",
    "/Users/user/Desktop/dcdata.bin"
)

First we see the malware invoking the¬†security¬†tool to install a new certificate (dcdata.bin) it’s downloaded from the internet. Let’s take a peak at this cert:

$ openssl x509 -inform der -in dcdata.bin -out dcdata.pem
$ openssl x509 -in dcdata.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            b6:e1:ab:f3:8b:9a:b4:1a
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=IL, ST=Gush Dan, L=Hertzilia, O=GreenTeam Internet, Ltd., OU=Web, CN=cloudguard.me
        Validity
            Not Before: Jul 23 17:25:15 2014 GMT
            Not After : Jul 15 17:25:15 2044 GMT
        Subject: C=IL, ST=Gush Dan, L=Hertzilia, O=GreenTeam Internet, Ltd., OU=Web, CN=cloudguard.me
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):

We can also view the (now installed) certificate via the ‘Keychain Access’ app. It’s in the System keychain as a root certificate authority….MitM anybody?!


Back to process monitoring:

# ./procInfo 

process start:
pid: 1177
path: /bin/cp
args: (
    "/bin/cp",
    "/Library/Preferences/SystemConfiguration/preferences.plist",
    "/Library/Preferences/SystemConfiguration/preferences.plist.old"
)

Interesting! It’s mucking with the¬†SystemConfiguration/preferences.plist¬†file. What’s in there? If you guessed DNS settings – you’re right!

And remember the two DNS addresses from the decrypted config data?¬†82.163.143.135¬†and¬†82.163.142.137, they’ve been added to the plist file:

$ grep -B 4 -A 2 82. /Library/Preferences/SystemConfiguration/preferences.plist
DNS

	ServerAddresses
	
		82.163.143.135
		82.163.142.137
	

If you’re more inclined to use the UI, you can see these changes via the System Preference app (Network pane):


So, the DNS settings on the infected host have been hijacked as well.

What about the other interesting methods? (e.g.¬†takeScreenshotAt,¬†mouseClick,¬†runAppleScript). We in my brief reversing/analysis/debugging session I didn’t see them being executed. Moreover, though the malware has an embedded launch item plist it didn’t attempt to persist (though as it’s altered system settings, it really doesn’t need to hang around – in fact it does self-delete). When I coerced the malware to execute the method that modifed the launch item plist,¬†initMaMiSettings, the value it configured in the¬†ProgramArguments¬†key – which tells the OS what to persistently execute – was simply:¬†ls -la && sleep 28 && ls:

# lldb MaMi
(lldb) po $rax
{
    AbandonProcessGroup = "<key>AbandonProcessGroup</key><true/>";
    FooterStage = "</dict></plist>";
    HeaderStage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-
    //Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
    <plist version=\"1.0\"><dict>";
    KeepAlive = "<key>KeepAlive</key><true/>";
    LabelStage = "<key>Label</key><string>%Label%</string>";
    ProgramArguments = "<key>ProgramArguments</key><array><string>/bin/sh</string>
    <string>-c</string><string>%ProgramArguments%</string></array>";
    RunAtLoad = "<key>RunAtLoad</key><true/>";
    ...
}

(lldb) po %$rsi
ls -la && sleep 28 && ls

Perhaps in order for the methods to be executed or for the malware to be persisted, requires some attack-supplied input, or other preconditions that just weren’t met in my VM. I’ll keep digging!

Conclusions
Ok, that’s a wrap. OSX/MaMi isn’t particular advanced – but does alter infected systems in rather nasty and persistent ways. By installing a new root certifcate and hijacking the DNS servers, the attackers can perform a variety of nefarious actions such as man-in-the-middle’ing traffic (perhaps to steal credentials, or inject ads).

Source:https://objective-see.com/blog/blog_0x26.html