Flare-On 7 2020 Challenge #4: report

When I tried to open the report.xls file I received an error:

After pressing the “OK” button we received the Visual Basic code:

Private Declare Function InternetGetConnectedState Lib "wininet.dll" _
(ByRef dwflags As Long, ByVal dwReserved As Long) As Long

We also have something that looks like a key and encrypted message:

Double click on of them shows there function name:

I extracted them to two files in GitHub because of their size:

  1. F.L
  2. F.T

Analyzing the Visual Basic Code

It’s time to understand what the code is doing. The main function is folderol:

It starts by splitting F.L to the array called onzo. Later there are number of calls to rigmarole function with onzo[<nub>] while <num> is being changed. It’s a decoded function, so I converted the code (the interesting parts) to Python code to be able to print some of its variables and understand them. For example, I used it to print all the decoded variables of onzo:

onzo[0]: AppData
onzo[1]: \Microsoft\stomp.mp3
onzo[2]: play
onzo[3]: FLARE-ON
onzo[4]: Sorry, this machine is not supported.
onzo[5]: FLARE-ON
onzo[6]: Error
onzo[7]: winmgmts:\\.\root\CIMV2
onzo[8]: SELECT Name FROM Win32_Process
onzo[9]: vbox
onzo[10]: WScript.Network
onzo[11]: \Microsoft\v.png

Use them as a menu and the code is much more clear. The code is using WMI service to search for vmware process (“vmw” and “vmt”):

Then it writes something to file named “stomp.mp3” (onzo(1)):

Notice that it uses the encrypted text of F.T.Text with the function canoodle which decrypts it.

This is our converted code to Python and it wrote the mp3 file:

with open(r'C:\tmp\flare2020\4\hex_T.txt', 'r') as f:
lines_t = f.read()

with open(r'C:\tmp\flare2020\4\hex_L.txt', 'r') as f:
lines_l = f.read()


def mid(s, offset, amount):
return s[offset:offset+amount]

def rigmarole(es):
furphy = ''

for i in range(0, len(es), 4):
c = int(mid(es, i, 2), 16)
s = int(mid(es, i+2, 2), 16)
cc = c - s
furphy = furphy + chr(cc)

return furphy


def canoodle(panjandrum, ardylo, s, bibble):
kerfuffle = [None]*s
quean = 0
for cattywampus in range(0, len(panjandrum), 4):
a = int(mid(panjandrum, cattywampus + ardylo, 2), 16)
b = bibble[quean % (len(bibble))]

result = a ^ b
kerfuffle[quean] = result
quean += 1
if quean == len(kerfuffle):
break

return
kerfuffle

onzo = lines_l.split(".")
fudgel = rigmarole(onzo[7])


xertz = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]
mp3 = canoodle(lines_t, 0, 168667, xertz)

with open(r'C:\tmp\flare2020\4\stomp2.mp3', 'wb') as f:
f.write(bytes(mp3))

MP3 file

When I played the MP3 file, it played some music, like mini march but that it. I thought maybe this is steganography but look the hint I saw when I played it:

It says something about P. Code.

P-Code

According to Wikipedia, P-Code is:

P-Code is a name for several of Microsoft’s proprietary intermediate languages.

So, I guess we need to see the P-Code of the visual basic code, maybe there is something hiding there.

I found a cool tool that decompile the code:

After installing and running the tool on the report.xls file, it decompiled it and you can view all the decompiled file here.

Finding hiding commands

I followed the code to see if there is something different from the visual basic code we saw in the beginning and I found new commands.

At line #55 I noticed to a new variable called firkin and how it assigns it the name “FLARE-ON”:

Line #55:
Ld firkin
LitDI2 0x0003
ArgsLd onzo 0x0001
ArgsLd rigmarole 0x0001
Ne
IfBlock

At line #60 it calculates it length:

Line #60:
Ld firkin
FnLen
St n

At lines #61-#63, there is a loop that reverse the string “FLARE-ON” to “NO-ERALF”:

Line #61:
StartForVariable
Ld i
EndForVariable
LitDI2 0x0001
Ld n
For
Line #62:
Ld firkin
Ld i
LitDI2 0x0001
ArgsLd Mid$ 0x0003
ArgsLd Asc 0x0001
Ld n
Ld i
Sub
ArgsSt buff 0x0001
Line #63:
StartForVariable
Next

The last piece is at line #65 when there is a call to canoodle but with different arguments we saw in the beginning:

Line #65:
Ld F
MemLd T
MemLd Text
LitDI2 0x0002
LitDI4 0x5C21 0x0004
Ld buff
ArgsLd canoodle 0x0004
St wabbit

We re-wrote the Python script with these new details:

with open(r'C:\tmp\flare2020\4\hex_T.txt', 'r') as f:
lines_t = f.read()

with open(r'C:\tmp\flare2020\4\hex_L.txt', 'r') as f:
lines_l = f.read()


def mid(s, offset, amount):
return s[offset:offset+amount]

def rigmarole(es):
furphy = ''

for i in range(0, len(es), 4):
c = int(mid(es, i, 2), 16)
s = int(mid(es, i+2, 2), 16)
cc = c - s
furphy = furphy + chr(cc)

return furphy


def canoodle(panjandrum, ardylo, s, bibble):
kerfuffle = [None]*s
quean = 0
for cattywampus in range(0, len(panjandrum), 4):
a = int(mid(panjandrum, cattywampus + ardylo, 2), 16)
b = bibble[quean % (len(bibble))]

result = a ^ b
kerfuffle[quean] = result
quean += 1
if quean == len(kerfuffle):
break

return
kerfuffle


# ASC function returns the ASCII value of a character or the first character in a string
buff = [None]*8
for i in range(0,8):
buff[7-i] = ord(mid('FLARE-ON',i,1))


png = canoodle(lines_t, 2, 0x45C21, buff)
with open(r'C:\tmp\flare2020\4\flag.png', 'wb') as f:
f.write(bytes(png))

The result is a PNG that contains the flag:

flag: thi5_cou1d_h4v3_b33n_b4d@flare-on.com

Security researcher interested in reversing, solving CTFs, malware analysis, penetration testing and DevOps security (docker and Kubernetes)