Image for post
Image for post

To ring in 2017, Rob Fuller (@mubix) launched his annual New Year Shmoo Ticket CTF which rewards the first person to complete all challenges a coveted Shmoocon Barcode. As a retired CTF junkie, it couldn’t have come at a better time since my usual attempts to gain entry were failing. Armed with the need to succeed, I began this epic journey. Here is the first half of this story.

Challenge #1

Navigating to https://room362.com/contest takes players to a landing page with a short paragraph and a URL to pointing to http://www.iana.org/domains/example.

Image for post
Image for post

Although a CTF by the Internet Assigned Numbers Authority might be quite fun, I was pretty sure this domain was out of scope. So like any curious hacker, it was time to see what was going on under the hood and view the webpage’s source code.

On line 15, I noticed a comment detailing concern about weak a password hash, a potential URL “/contest/source/”, the hash “0F1E9D1C0DD10BC722C34254E51BFF62”, and a possible username “admin”. To confirm my suspicions, I browsed to https://room362.com/contest/source/ and received a basic authentication request which prompted for a user name and password.

Image for post
Image for post

Based on the hash’s 16 byte (32 character) length, I assumed it was probably an LM or NTLM hash which needed to be cracked. There’s probably a dozen different ways to approach this problem. I prefer to take every shortcut possible, skip the elegance, and “follow the quickest path to 0-day” (our CTF Team’s motto). With this in mind, I avoided John the Ripper like the plague and browsed straight to CrackStation where I submitted the hash to their free lookup service.

Image for post
Image for post

Within two seconds, I was given the cracked password and confirmation it was in LM hash format. With this knowledge, I went back to the authentication page and tried the user name “admin” and the password “p4ssword!”. Unfortunately, this didn’t work :( Not be be deterred, I considered that I likely had a bad user name, so I tried “admin”, “administrator”, “root”, “mubix”, and “4021221” (a string from the HTML source code’s comment). Once again, no luck…

At this point in any CTF, it’s easy to over think possible solutions. Since this was just the first of many challenges, I took a step back and reconsidered what I was missing. Then it dawned on me; LM hashes suffer from a weakness where passwords of varying case will yield the same hash. To test this out, try generating an LM hash using the passwords “p4ssword!” and “P4ssWOrD!”. Both of these passwords will produce the same hash. This meant I needed to determine the case-sensitive password to proceed. Rather than leave anything to chance, I wrote a short Python script to brute force all possible combinations of “p4ssword!” and as well as try each of those previously mentioned user names.

When it comes to brute forcing CTF challenges, one of the easiest mistakes to make is failing to stop your code or display success when you solve it. To address this issue, I checked the response codes of each request and only printed the user name and password after receiving 200 OK. Six seconds and 288 login attempts later, I found the user name “admin” and password “P4sSword!”.

Image for post
Image for post

Entering these credentials into the basic authentication prompt brought me to Challenge #2. Mood: 😀

Challenge #2

After authenticating to https://room362.com/contest/source/, players are presented with a QR code. Was this the coveted Shmoocon barcode? Have I already won?

Image for post
Image for post

Considering that anyone who makes it this far would also have the barcode, the likelihood of already winning was low ;) To test the theory, I navigated to an online QR code reader and uploaded the image. This returned the URL “https://cdn1.vox-cdn.com/uploads/chorus_asset/file/3388550/horsetwerk.0.gif

Image for post
Image for post

Needless to say, this was not the end. I navigated back to the Challenge #2 webpage and viewed the HTML source code.

Within the source, there was another comment which pretty bluntly tells players to browse through Adam Baldwin’s GitHub repositories for a tool to solve this sucker. Considering that Adam had 39 repos at the time of this challenge, I immediately felt a little depressed. To improve my odds, I decided to disregard the repos he had forked from elsewhere since the comment said the tools belonged to Adam (bringing the count down to 24). Within a couple minutes of analysis, it became apparent that Adam spends a lot of time on web-hipster things like Node, Bower, Docker, and things of that nature ;) However, one project called DVCS-Pillage seemed especially useful considering our challenge URL is in a directory called “source”.

Why DVCS Pillage Toolkit?

I thought it would be useful to automate some other techniques I found to extract code, configs and other information from a git,hg, and bzr repo’s identified in a web root that was not 100% cloneable. Each script extracts as much knowledge about the repo as possible through predictable file names and known object hashes, etc.

In a nutshell, DVCS Pillage is capable of enumerating data from git repos when stored within protected directories. During this part of the challenge, I took a lucky leap of faith and tried browsing to a few common source code directories to see if this tool may be relevant:

Clearly there was something special about /.git. To poke a little bit further, I tried a git clone, but this failed. With no other alternatives in mind, I downloaded DVCS Pillage and started playing with the shell script gitpillage.sh. In case you’re playing along, this shell script takes a protocol (“http” or “https”) and a “hostname/directory” as arguments (some of those details aren’t clear in the readme).

root@kali:~/Desktop/DVCS-Pillage# ./gitpillage.sh https room362.com/contest/source/
Initialized empty Git repository in /root/Desktop/DVCS-Pillage/room362.com/
Getting refs/heads/master
--2016-12-31 13:37:48-- https://room362.com/contest/source/.git/refs/heads/master
Resolving room362.com (room362.com)... 162.243.126.247
Connecting to room362.com (room362.com)|162.243.126.247|:443... connected.
HTTP request sent, awaiting response... 401 Unauthorized
Username/Password Authentication Failed.
cat: .git/refs/heads/master: No such file or directory

Unfortunately, the basic authentication stopped gitpillage.sh dead in its tracks. After reading the documentation and the script’s source code, it appeared authentication wasn’t supported. Thankfully, gitpillage.sh attempts to use wget or cURL to fetch resources. My solution was a simple modification to the script which forced wget to use a hardcoded username and password. After making this change, I tried to run it again.

root@kali:~/Desktop/DVCS-Pillage# ./gitpillage.sh https room362.com/contest/source/
Reinitialized existing Git repository in /root/Desktop/DVCS-Pillage/room362.com/.git/
Getting refs/heads/master
--2017-01-01 00:24:47-- https://room362.com/contest/source/.git/refs/heads/master
Resolving room362.com (room362.com)... 162.243.126.247
Connecting to room362.com (room362.com)|162.243.126.247|:443... connected.
HTTP request sent, awaiting response... 401 Unauthorized
Authentication selected: Basic realm="Source Code Repository"
Reusing existing connection to room362.com:443.
HTTP request sent, awaiting response... 200 OK

Length: 41 [application/octet-stream]
Saving to: ‘.git/refs/heads/master’
.git/refs/heads/mas 100%[===================>] 41 --.-KB/s in 0s2017-01-01 00:24:47 (53.7 MB/s) - ‘.git/refs/heads/master’ saved [41/41]

Success! This created a git repository/directory called “room362.com”. Now it’s time to see what mysteries lurked within. I started by moving into the room362.com directory and running git log to display the commit history.

root@kali:~/Desktop/DVCS-Pillage/room362.com# git log
commit 1fe22d6d1ac7af9210846b706144fa2e005afcb8
Author: mubix <mubix@hak5.org>
Date: Fri Dec 30 15:10:36 2016 -0500
funny QR codes FTWcommit e53d5f64a91ff1d0a7055352f23d66eb4734cdfd
Author: mubix <mubix@hak5.org>
Date: Fri Dec 30 15:07:10 2016 -0500
EnCom 4 lifecommit 934c6eb04170db36255e3b1af5bd6fad3d983320
Author: mubix <mubix@hak5.org>
Date: Fri Dec 30 15:05:30 2016 -0500
legos!commit dc955b5d407f02981e243559b21aa9dfb6e243b3
Author: mubix <mubix@hak5.org>
Date: Fri Dec 30 15:04:45 2016 -0500
cool QR!commit 0d5132b76c0a7d90cabf1c8bddb147b9c2b0fe21
Author: mubix <mubix@hak5.org>
Date: Fri Dec 30 15:03:14 2016 -0500
initial commit

Although there’s several different ways to inspect the contents of each commit, I used git checkout <commit> and stepped through each commit. Commit e53d5f64a91ff1d0a7055352f23d66eb4734cdfd contained a new image named IMG_0954.jpg which had a QR Code from a previous Barcode Shmarcode competition.

Image for post
Image for post
IMG_0954.jpg

Commit 934c6eb04170db36255e3b1af5bd6fad3d983320 provided another Barcode Shmarcode image named BDKfTj4CIAA6-ZX.jpg.

Image for post
Image for post
BDKfTj4CIAA6-ZX.jpg

Commit dc955b5d407f02981e243559b21aa9dfb6e243b3 gave me another Barcode Shmarcode image named 3.jpg.

Image for post
Image for post
3.jpg

Finally, the initial commit 0d5132b76c0a7d90cabf1c8bddb147b9c2b0fe21 was just a single HTML file containing the riddle:

ShmooCon QR Codes! Can you find one of the QR codes at https://room362.com/contest/QRid.txt?

Navigating to this URL redirects us to Room362 just like my previous failed attempts to discover source code directories. Unsure where to go from here, I decided to investigate the previously discovered pictures. Since these were photos of QR codes, my handy dandy QR code reading website had a hard time extracting the data. To get around this, I installed a QR code app on my phone and discovered the following.

  • encom badge - 10-9d1dac8b-bc12-4b15-b42d-a1f5c9e5bf82-1007
  • legos - 09-28b3a2fd-bdf1-4d8c-afe0-a5843dc93d0b-0001
  • processor - 11-2ea2483e-a039-4d9a-a7ad-64ccc0723e9c-1088

For any ShmooCon regular, these strings should be extremely familiar. For newcomers, these strings are the unique barcode IDs which grant you access to the conference. This gave me the idea to try substituting these IDs into the URL from the riddle — https://room362.com/contest/QRid.txt

Although the first two URLs sent me to the below ASCII art, I knew I was on the right track (which gave me a much needed morale boost).

Sorry.. your pricess is in another QR code...                                       /\
/`:\
/`'`:\
/`'`'`:\
/`'`'`'`:\
/`'`'`'`'`:\
|`'`'`'`:|
_ _ _ _ _ |] ,-. :|_ _ _ _
||| || || || | | |_| ||| || || || |
|`' `' `' `'.| | _'=' |`' `' `' `'.|
: .:; |'-' : .:;
\-..____..:/ _ _ _ _ _ _| _ _'-\-..____..:/
:--------:_,' || || || || || || || `.::--------:
|] .:|:. `' `'_`' `' `' `' `' | '-' .:|
| ,-. .[|:._ '-' ____ ___ | ,-.'-|
| | | .:|'--'_ ,'____`. '---' | | |.:|
| |_| .:|:.'--' ()/,| |`|`.\() __ | |_|.:|
| '=' .:|:. |::_|_|_|\|:: '--' | _'='.:|
| __ .:|:. ;||-,-,-,-,|; | '--' .:|
|'--' .:|:. _ ; || |:| | .:|
| .:|:.'-': || |;| _ |] _:|
| '-|:. ; || :|| '-' | '--|
| _ .:|]. ; || ;||] | _ .:|
| '-' .:|:. : [|| ;||| | '-' .:|
,', ;._____.::-- ;---->'-,--,:-'<'--------;._____.::.`.
(( ( )_;___,' ,' , ; //________( ) ))
`. _`--------' : -,' ' , ' '; //- _ `--------' ,'
__ .--' ;,' ,' , ': // -.._ __ _.- -
`- -- _ ;',' ,' ,' ,;/_ -. --- _,
_,. /-:,_,_,_,_,_,_(/:-\ , ,. _
-' `-'--'-'-'-'-'-'-'-''--'-' `-'`' `'`' `-SSt-

Visiting the last URL completed Challenge #2. Mood: 🤗

Challenge #3

Browsing to https://room362.com/contest/11-2ea2483e-a039-4d9a-a7ad-64ccc0723e9c-1088.txt delivered commentary from some nosy coworkers trying to play with “Bob’s binary”.

Steve: This is bob's binary ---- shhh don't tell anyone: https://room362.com/contest/bob_only.zip
Jen: It didn't seem to work for me, he must have protected it so only he could run it...
Marti: I really hate bob...

As an equally snoopy hacker, I immediately downloaded the referenced zip, only to discover the archive was password protected. I’m not a fan of CTF challenges which make you perform meaningless/open-ended dictionary attacks, so I was stoked to see this challenge built off previous challenge knowledge. The password “P4sSword!” allowed me to extract runme.exe (MD5: 844BF040CAA03CA90512481B703A4CE3).

Before running a shady CTF binary, the first thing I always do is determine what the heck I’m working with. I started with the file command.

root@kali:~/Desktop# file runme.exe 
runme.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows

With this info, I knew I was really working with an x86 Windows PE (32-bit executable file). To derive some additional details, I also leveraged objdump.

root@kali:~/Desktop# objdump -p runme.exe
...edited for brevity...
The Import Tables (interpreted .idata section contents)DLL Name: advapi32.dll
vma: Hint/Ord Member-Name Bound-To
1e40ee 0 CryptAcquireContextW
1e4106 0 CryptGenRandom
1e4118 0 CryptReleaseContext
DLL Name: kernel32.dll
vma: Hint/Ord Member-Name Bound-To
1e4146 0 FreeLibrary
1e4154 0 GetNativeSystemInfo
1e416a 0 GetProcAddress
1e41c8 0 LoadLibraryA
1e41e8 0 VirtualAlloc
1e41f8 0 VirtualFree
1e4206 0 VirtualProtect
1e429a 0 CreateThread
1e42fc 0 GetEnvironmentStringsW
1e4338 0 GetSystemInfo
1e4432 0 WriteFile
[Ordinal/Name Pointer] Table
[ 0] _cgo_allocate
[ 1] _cgo_panic

[ 2] crosscall2

From this output, I made some basic assumptions that this program was likely using crypto, executing a shellcode buffer (VirtualAlloc and VirtualProtect), reading environment variables, and manipulating a file. The references to “cgo” also hinted this executable was written in Golang. To confirm these hypotheses, I dropped the binary into IDA Pro to examine the disassembly (my first time reversing a Go compiled executable). To my surprise, a fair amount of the Golang code was quite easy to follow.

Image for post
Image for post

After 30 minutes of getting familiar with the code base, there were two areas I was particularly interested in debugging:

Image for post
Image for post
Why is this program interested in environment variables?
Image for post
Image for post
What is it doing with this payload?

With my debugger, I set some break points right before the call to main_pull_environmentals() so I could observe the stack/registers and quickly determine what was getting passed around. This led to the discovery that Bob’s binary was querying the %USERNAME% environment variable.

Image for post
Image for post

Right away I thought about the banter between the snoopy coworkers, stating “he must have protected it so only he could run it…”. With this new information gleaned from the assembly, I was pretty sure Bob’s binary was checking for his user name. Rather than reverse it any further, I took another leap of faith by changing %USERNAME% to “bob” and executing runme.exe.

Image for post
Image for post

Voila! This is all that was needed to complete Challenge #3. Mood: 😎

To Be Continued…

As you probably noticed, there’s a lot of steps to each challenge and there are still four more challenges to go! Continue to the second half of the write up which details all the twists and turns it took to win NYST-CTF 2016. If you have any questions about the steps above, don’t hesitate to reach out to me on Twitter at @KyleHanslovan.

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