NYST-CTF 2016 Writeup — Part 2

Image for post
Image for post

During the first part of our journey through the New Year Shmoo Ticket CTF challenges, I cracked a hash, brute forced password authentication, pillaged a git repo from a protected directory, extracted some images from old commits, and reverse engineered an executable to get it to run. Now it’s time to see what other sick tricks Rob Fuller (@mubix) had up his sleeves :)

Image for post
Image for post

Challenge #4

I started this challenge by downloading the RAR archive from the URL mentioned in the Challenge #3 message box. Once again, the archive was password protected so I nonchalantly reused our reliable Challenge #1 password “P4sSword!”. To my surprise, it didn’t work. I use 7-Zip to open just about everything. I love it/trust it/depend on it. However, my trust must have been running thin because I quickly went and downloaded WinRAR *just* to make sure ;) Again, no luck!

Image for post
Image for post

So there I was, regretting all praise previously given to Rob for not having to blindly brute force a challenge. Thankfully, I’ve had to do this a time or two and was familiar with using cRARk (a CUDA and OpenCL compatible RAR cracker). Within 10 minutes, I had the required password.def file finished and was attempting~9,000 passwords/second. During this time, it’s a great opportunity to stretch your legs, help friends with other unsolved challenges, catch a nap, or take a shower (especially during all weekend events). Unfortunately, I was alone in my office where most of these options didn’t apply. While walking around the building, I started thinking about the previous NYST-CTF challenges and it made me second guess my brute forcing approach. Was I missing a password from a previous challenge? How about an ignored hint? And I was…

Image for post
Image for post

Staring right at me in the upper-left corner of the message box was the word “Juniper”. I giddily typed it into WinRAR (still mistrusting good ole 7-Zip) and once again, FAIL! So what could it be? Out of desperation, I searched for “juniper password” and a couple entries down was an article from The Register about the hard coded backdoor password “<<< %s(un=’%s’) = %u” found in Juniper’s Netscreen firewalls. And just like that, the archive extracted “dbbackup.txt”. Within the file was the string:

uggcf://ebbz362.pbz/pbagrfg/S5.rap

The “://” was a pretty good indicator that I was looking at an obfuscated URL. The string “uggcf://ebbz362.pbz” also looked a whole lot like “https://room362.com”, so I was pretty sure a simple substitution cipher was used. My favorite resource for solving cipher challenges is Rumkin. Although there many different types of substitution ciphers, CTFs are notorious for using ROT13. Thankfully, Rumkin has a decoder for that.

Image for post
Image for post

Just like that, Challenge #4 fell. I copied the decoded URL to my clipboard, closed out cRARk (still running just in case I got lucky), and moved onto Challenge #5. Mood: 😊

Challenge #5

Browsing to https://room362.com/contest/F5.enc downloaded a 128 byte file that contained what appeared to be random characters.

Image for post
Image for post

Without a single string or magic number to guide me, I assumed the “.enc” file extension was short for “encoded” or “encrypted”. As a result, I went back to the interwebs and searched for “F5 encryption” this time. The very first result was an article on F5’s developer forum about F5 Cookie Encryption — nice! So I dug in deep and read every bit of documentation on the subject. Unfortunately, one F5 developer article filled me with significant doubt on whether Cookie Encryption was the answer:

Image for post
Image for post

This encryption standard called for the final result to be AES encrypted with a private key and then Base64 encoded (which this data wasn’t). Without any other ideas, I began searching for leaked F5 private keys. This lead me to CVE-2012–1493.

Hmmm, not quite the private key I was looking for, but this could be promising (especially since there was a Metasploit module to reference). Within the module’s source code was the referenced F5 private key.

-----BEGIN RSA PRIVATE KEY-----MIICWgIBAAKBgQC8iELmyRPPHIeJ//uLLfKHG4rr84HXeGM+quySiCRgWtxbw4rhUlP7n4XHvB3ixAKdWfys2pqHD/Hqx9w4wMj9e+fjIpTi3xOdh/YylRWvid3Pf0vkOzWftKLWbay5Q3FZsq/nwjz40yGW3YhOtpK5NTQ0bKZY5zz4s2L4wdd0uQIBIwKBgBWL6mOEsc6G6uszMrDSDRbBUbSQ26OYuuKXMPrNuwOynNdJjDcCGDoDmkK2adDF8auVQXLXJ5poOOeh0AZ8br2vnk3hZd9mnF+uyDB3PO/tqpXOrpzSyuITy5LJZBBv7r7kqhyBs0vuSdL/D+i1DHYf0nv2Ps4aspoBVumuQid7AkEA+tD3RDashPmoQJvM2oWS7PO6ljUVXszuhHdUOaFtx60ZOg0OVwnh+NBbbszGpsOwwEE+OqrKMTZjYg3s37+x/wJBAMBtwmoi05hBsA4Cvac66T1Vdhie8qf5dwL2PdHfu6hbOifSX/xSPnVLRTbwU9+h/t6BOYdWA0xr0cWcjy1U6UcCQQDBfKF9w8bqPO+CTE2SoY6ZiNHEVNX4rLf/ycShfIfjLcMA5YAXQiNZisow5xznC/1hHGM0kmF2a8kCf8VcJio5AkBi9p5/uiOtY5xe+hhkofRLbce05AfEGeVvPM9V/gi8+7eCMa209xjOm70yMnRHIBys8gBUOt0f/O+KM0JR0+WvAkAskPvTXevY5wkp5mYXMBlUqEd7R3vGBV/qp4BldW5l0N4GLesWvIh6+moTbFuPRoQnGO2P6D7Q5sPPqgqyefZS
-----END RSA PRIVATE KEY-----

I saved the leaked key to a file called “private.key” and used OpenSSL to try decrypting the “F5.enc” file I previously downloaded. The result of the command was written to the file “plaintext”.

root@kali:~/Desktop# openssl rsautl -decrypt -in F5.enc -out plaintext -inkey private.key

To my relief, the decrypted data was a beautiful looking URL:

https://room362.com/contest/01001101010100110100010001001110.txt

To celebrate, I queued up some Queen and kept chugging along. Mood: 👨🏻 (why yes that is a Freddie Mercury emoji)

Challenge #6

Having learned from my previous mistake, I now knew every hint mattered when it came to the NYST-CTF challenges. So before even browsing to the Challenge #6 URL, I used another favorite tool of mine called CRYPTII to determine what data was hidden behind the binary file name “01001101010100110100010001001110” from the challenge URL.

Image for post
Image for post

Well “䑎” wasn’t what I expected. Undeterred and confident that this wasn’t a pinyin challenge, I searched for another binary to ASCII converter and used the first result.

Image for post
Image for post

Aha! “MSDN” is an acronym for the Microsoft Developer Network which stores 1,000s of developer documents for Microsoft products. With this hint, I was confident the solution for the challenge would be found somewhere in those documents. Now it was time to see what was waiting for me behind the Challenge #6 URL:

8ZuFA0CxVmqxiN86jwdS7d/5qRBW5hsV7NtXzI++5MX/das7W+J3RmUq7SG9IXSpgtCbgV77iztv/t0gBR7qzTgvG0S4TnhGdkvG2LTOTEng0HMcISdvXS2rIIy1CImao0nwjjt89ifOxHk9iI1wzg==

On this page was a Base64 blob. In case you’re not familiar with Base64 encoding, it’s worth mentioning that its general purpose is to encode binary/hexadecimal data into ASCII/printable characters. This is important to remember because several of the most popular online Base64 decoders will only decode the data back to ASCII format, discarding the original binary data I needed. Here’s a couple examples of these less than perfect decoders — First Google Result, Second Google Result, Fourth Google Result.

Image for post
Image for post

An alternative option is to leverage a couple lines of Python to create your very own Base64 decoder. This is the route I took for this challenge.

With the Base64 data successfully decoded back to its binary form, I had to figure out what to do with it. Similar to Challenge #5, I was looking at ~100 bytes of random data.

Image for post
Image for post

Again…with no strings or magic numbers to guide me, I assumed this must be another encrypted blob where some code example or private key was waiting for me on MSDN. So I started searching…a lot.

Thankfully, on the seventh page of my search for “encryption key”, I stumbled onto a peculiar article about a hard coded AES key used to encrypt passwords stored within the Group Policy Preferences Extension Data Structure.

Unfortunately, there wasn’t any example code demonstrating how Microsoft intended for this to be used. As all lazy programmers do, I searched the web to find someone else’s code to steal. Ironically, this turned up several shady InfoSec articles detailing exactly how to abuse this key. From Sean Metcalf’s Blog, I found a PowerShell based implementation which I was able to easily adapt for my needs.

Image for post
Image for post

Booyakasha! Mood: 👲🏻 (not quite Ali G…so sue me)

Challenge #7

At this point, I was just under seven hours into the CTF and I felt pretty confident. Although I ran into a number of wrong turns, they were all easily corrected. Just like the challenges before, I started this one by paying particular attention to the hexadecimal hint within the Challenge #7 URL https://room362.com/73686d6f6f636f6e32303137.txt.

Image for post
Image for post

This time, CRYPTII worked like a charm and converted hexadecimal “73686d6f6f636f6e32303137” to the ASCII string “shmoocon2017”. Now for the actual challenge. Browsing to the previously decrypted URL provided a large Base64 blob. I once again leveraged my Python script to decode the Base64 data to its original form. This time I was stuck staring down a whopping 51,069 bytes of data. While considering the previously solved decryption challenges, I realized that I haven’t seen any XOR ciphers (another common CTF theme). I assumed this data was likely encrypted with a rolling XOR using the 12 byte/character string “shmoocon2017”. In case you’re not familiar with this technique, the Malware Analyst’s Cookbook succinctly describes it as:

The attacker supplies a sequence of bytes to use as the XOR key. The byte at offset 0 of the key is used to XOR the byte at offset 0 of the data to encode. The byte at offset 1 of the key is used to XOR the byte at offset 1 of the data, and so on…until the maximum length of the key is reached. At this time, the algorithm cycles back around to the beginning of the key and uses the byte at offset 0 to XOR the next byte in the data.

Before diving right in, I quickly searched the decoded Base64 data for evidence of repeating strings which often occurs when a key is XOR’d against repeating characters. This static analysis technique is especially useful when the key is XOR’d against a large sequence of null bytes which causes the key to be leaked/exposed. You can test this out using an XOR Calculator.

Image for post
Image for post

My search quickly turned up several repeating sequences between 5 and 12 bytes log. Considering the hint “shmoocon2017” was also 12 bytes long, this was more than enough supporting evidence to convince me to dig further.

Image for post
Image for post

To exercise this hypothesis, I downloaded xortool which supports decrypting rolling XOR protected data.

root@kali:~/Desktop# xortool-xor -s "shmoocon2017" -f 73686d6f6f636f6e32303137.bin > xor_decrypted

The result of this command was redirected to the file xor_decrypted. Now it was time to inspect the fruits of my labor!

Image for post
Image for post

Well @#$%&! Although I expected to see something useful, I ended up with more random garbage. I distinctly remember thinking, “either I messed up big time, or Rob is a sick, cruel man.” As a result, I continued to analyze, experiment, and brute force using EVERY technique I could think of.

Image for post
Image for post
Mood: 😣

By this point, it was now 3:30am (12 straight hours of CTF challenges) and I was physically and emotionally exhausted. In a rage, I closed my laptop, drove home, and caught up on some much needed sleep.

When I finally woke up and learned that no one else had claimed the ticket, it motivated me to go back for a second helping. So what hint was I missing? Should I decrypt the data with the F5 SSH key? Use the GPP key from MSDN? Maybe use both? Or was it something much simpler? Out of an act of desperation, I used the ROT13 cipher on the Base64 data and then decoded it with this script.

Image for post
Image for post

The result was a glorious looking binary file with a Portable Network Graphics image header. Now for the moment of truth!

Image for post
Image for post

Sweet victory — it looks like this was the final challenge! Before rushing to Rob like a giddy schoolgirl, there was one last piece to this puzzle which I couldn’t pass up. WTF was in that hexadecimal string? As a terribly lazy hacker, I refused to type that sucker in by hand so I navigated to a free OCR service that quickly converted this image into a string.

You made it! Send me an email to mubixehakS.org with the subject: ”ShmooCon 2017 Ticket”

Include the following:

54686520617274206f66296c6561646572736869702
0697320736179696e67206e6f2c206e6f7420796573
2e2049742069732076657279206561737920746f207
36179207965732e

So, the character recognition software wasn’t perfect but it got me most of the way there…and the result was inspiring!

Image for post
Image for post

As the CEO of a blossoming cybersecurity startup, I probably should have said “no” to NYST-CTF. However, there’s probably an equally good argument for making time to learn something out of your comfort zone. With this final tidbit of information, I reached out to Rob and seized my much needed ShmooCon barcode as the champion of NYST-CTF 2016.

Conclusion

I can’t thank the community enough for all the positive feedback on Part 1 of my NYST-CTF blog. Without that, Part 2 would have been a much lower priority for me. I hope this style of writeup inspires other CTF players to expose more of the failures they run into instead of only highlighting their successes. As for inspiring InfoSec professionals, I hope this uncensored account motivates you to keep on pushing regardless of the hurdles you run into. Lastly, I’d like to sincerely thank Rob for putting so much effort into an event like this each year. Without NYST-CTF, this entire learning opportunity would not have been possible. Mood: 🕵️

Written by

Ethical Hacker. Malware Connoisseur. CEO at @HuntressLabs.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store