Language Matters (Palo Alto challenge)

After opening the .doc file, we get a warning stating that it contains macros (which are disabled by default for security purposes). We can extract the macros and look at their source to see if there’s anything interesting – in LibreOffice, this is under “Tools – Macros – Edit Macros”. We find a module NewMacros containing several functions, the most interesting being a():

Sub a()
    Dim s As String
    x2 = Application.LanguageSettings.LanguageID(msoLanguageIDUI)
    Set fso = CreateObject("Scripting.FileSystemObject")
    s = ActiveDocument.Content.Text
    x = "-----BEGIN CERTIFICATE-----"
    y = "-----END CERTIFICATE-----"
    Z = InStr(s, x)
    If Z > 0 Then
        s = Mid(s, Z + Len(x))
        s = Mid(s, 1, InStr(s, y) - 1)
        s = Replace(s, "-", vbNullString)
        s = Replace(s, vbCr, vbNullString)
        s = Replace(s, vbLf, vbNullString)
        
        b1 = Base64Decode(s)
        b2 = Blah(b1, x2)
        
        If InStr(1, b2, "Key:") = 1 Then
            MsgBox b2
        End If
    End If
End Sub

We see that this initialises b1 with a base64-decoded string from the document text, which is extracted from the part between “BEGIN CERTIFICATE” and “END CERTIFICATE”. A second variable x2 is set to the value of the current language ID (a 4-digit integer). The script then calls Blah(b1, x2), checks if the output starts with Key: and prints it if it does.

The Blah function is relatively simple:

Function Blah(ByVal sData As String, ByVal sKey As String) As String
    Dim l As Long, i As Long, byIn() As Byte, byOut() As Byte, byKey() As Byte
    byIn = sData
    byOut = sData
    byKey = sKey
    l = LBound(byKey)
    For i = LBound(byIn) To UBound(byIn) - 1 Step 2
        byOut(i) = (byIn(i) Xor byKey(l))
        l = l + 2
        If l > UBound(byKey) Then l = LBound(byKey)
    Next i
    XorC = byOut
End Function

It takes a string and a key, and deciphers the string by xor-ing it with the key bytes (where the key is repeated enough times to “cover” the entire string). Since we know that the key is four characters long, and we know the first four characters of the result, we have enough information to recover the key and decode the data.

First, we get the encrypted data. Running strings challenge.doc -n 10 returns (among other things):

-----BEGIN CERTIFICATE-----
elVNCn5Ya15eb11EbllHb3BCWVFWeHFxdVRbXhARFQ==
-----END CERTIFICATE-----

We can take the first four bytes of the decoded data and xor them with Key: to get the encryption key, which we then use to decode the entire string:

>>> import base64
>>> c = base64.b64decode('elVNCn5Ya15eb11EbllHb3BCWVFWeHFxdVRbXhARFQ==')
>>> key = [a^b for a,b in zip(c, b'Key:')]
>>> out = [chr(a^b) for a,b in zip(c, 20*key)]
>>> ''.join(out)
'Key:Oh_no_it_is_ArmagHEADdon!!!'

The flag is flag{Oh_no_it_is_ArmagHEADdon!!!}.