Flare-On 7 2020 Challenge #4: report

Eviatar Gerzi
6 min readOct 29, 2020

--

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
Private Declare PtrSafe Function mciSendString Lib "winmm.dll" Alias _
"mciSendStringA" (ByVal lpstrCommand As String, ByVal _
lpstrReturnString As Any, ByVal uReturnLength As Long, ByVal _
hwndCallback As Long) As Long
Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" _
(ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal lBuffer As Long) As Long
Public Function GetInternetConnectedState() As Boolean
GetInternetConnectedState = InternetGetConnectedState(0&, 0&)
End Function
Function rigmarole(es As String) As String
Dim furphy As String
Dim c As Integer
Dim s As String
Dim cc As Integer
furphy = ""
For i = 1 To Len(es) Step 4
c = CDec("&H" & Mid(es, i, 2))
s = CDec("&H" & Mid(es, i + 2, 2))
cc = c - s
furphy = furphy + Chr(cc)
Next i
rigmarole = furphy
End Function
Function folderol()
Dim wabbit() As Byte
Dim fn As Integer: fn = FreeFile
Dim onzo() As String
Dim mf As String
Dim xertz As Variant

onzo = Split(F.L, ".")

If GetInternetConnectedState = False Then
MsgBox "Cannot establish Internet connection.", vbCritical, "Error"
End
End If
Set fudgel = GetObject(rigmarole(onzo(7)))
Set twattling = fudgel.ExecQuery(rigmarole(onzo(8)), , 48)
For Each p In twattling
Dim pos As Integer
pos = InStr(LCase(p.Name), "vmw") + InStr(LCase(p.Name), "vmt") + InStr(LCase(p.Name), rigmarole(onzo(9)))
If pos > 0 Then
MsgBox rigmarole(onzo(4)), vbCritical, rigmarole(onzo(6))
End
End If
Next

xertz = Array(&H11, &H22, &H33, &H44, &H55, &H66, &H77, &H88, &H99, &HAA, &HBB, &HCC, &HDD, &HEE)
wabbit = canoodle(F.T.Text, 0, 168667, xertz)
mf = Environ(rigmarole(onzo(0))) & rigmarole(onzo(1))
Open mf For Binary Lock Read Write As #fn
Put #fn, , wabbit
Close #fn

mucolerd = mciSendString(rigmarole(onzo(2)) & mf, 0&, 0, 0)
End Function
Function canoodle(panjandrum As String, ardylo As Integer, s As Long, bibble As Variant) As Byte()
Dim quean As Long
Dim cattywampus As Long
Dim kerfuffle() As Byte
ReDim kerfuffle(s)
quean = 0
For cattywampus = 1 To Len(panjandrum) Step 4
kerfuffle(quean) = CByte("&H" & Mid(panjandrum, cattywampus + ardylo, 2)) Xor bibble(quean Mod (UBound(bibble) + 1))
quean = quean + 1
If quean = UBound(kerfuffle) Then
Exit For
End If
Next cattywampus
canoodle = kerfuffle
End Function

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
// firkin = rigmarole(onzo(3)) // 3 - "FLARE-ON"

At line #60 it calculates it length:

Line #60:
Ld firkin
FnLen
St n
// n = Len(firkin)

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
for i = 1 To n
buff(n-i) = Asc(Mid(firkin,i,1))
# Print it the opposite:
"NO-ERALF"

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
canoodle(F.T.Text, 2, 0x45C21, buff)

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

--

--

Eviatar Gerzi

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