I attempted Ed Skoudis’ Christmas challenge again this year (SANS – http://pen-testing.sans.org/holiday-challenge/2012).While my submission was not selected (you should read/watch the winning solutions), I decided I would share it anyways.
I had a lot of fun doing the challenge and am thankful to Ed, Tim and anyone else that was involved in making the challenge.
———————————————————————————
This is the story of
***Images Copyright Warner’s Animation (with a slight modification by me)
And the story of how I, a GIAC certified security professional saved Christmas.
Background
The trouble started one day when Santa, feeling depressed, announced he was taking the year off. Mrs. Clause soon had a plan on how to lift Santa’s spirits, which somehow turned into a full on supervisory control and data acquisition (SCADA) penetration testing challenge between the Miser brothers.
The brothers were having a great time, sharpening both their offensive and defensive security skills, until Mother Nature found out about the competition and grounded the boys.
See http://pen-testing.sans.org/holiday-challenge/2012 for the full story.
Unfortunately, Mother Nature didn’t realize that by grounding the boys, she was ruining Mrs. Clause’s plans to save Christmas by having it snow in Southland and warm in the North Pole.
Mrs. Clause came to me and asked if I had any ideas on how we could prevent a year without Santa!
I approached the Misers and they agreed that if I could reach all five levels and answer some questions for them they would carry on with the plan and after the rules of engagement and scope were signed off, the hack was underway.
Executive Summary: Miser Brothers’ Questions and Answers
- Where did you find the remainder of Snow Miser’s Zone 1 URL?
In the picture that Snow Miser tweeted, it could be seen in the reflection of the ice cold drink.
- What is the key you used with steghide to extract Snow Miser’s Zone 2 URL? Where did you find the key?
The key was IceIceBaby! And was found in the user comment tag of the off.jpg extended metadata.
- On Snow Miser’s Zone 3 page, why is using the same key multiple times a bad idea?
If you have known plain text and cipher text you can derive the key that was used to encrypt the plaintext. If that same key was used to encode other plaintext, it can be used to decode the cipher text. (Plaintext XOR Key = Ciphertext, Ciphertext XOR Key = Plaintext, Plaintext XOR Ciphertext = Key)
- What was the coding error in Zone 4 of Heat Miser’s site that allowed you to find the URL for Zone 5?
A PHP header re-direct does not stop the HTTP request from executing the rest of the PHP code and any commands after the redirect will still run. Any results from the code execution will be returned to the client’s browser. The proper way to program the re-direct is to include the exit() command to stop all further PHP code execution of the page.
- How did you manipulate the cookie to get to Zone 5 of Heat Miser’s Control System?
The cookies were ASCII encoded MD5 hashed values. By substituting the value ASCII(MD5(1)) for the cookie, access was granted.
- Please briefly describe the process, steps, and tools you used to conquer each zone, including all of the flags hidden in the comments of each zone page. See Exploitation Narrative.
Exploitation Narrative
Information Gathering
The first step in any penetration test is to gather as much information about the targets as possible. I look at the Miser brothers’ twitter feeds and Mother Nature’s as well for good measure. I downloaded all the pictures and the backup that Heat Miser somehow obtained of Snow Misers Android device.
snowmiser.counterhack.com
I started off this level, with the level that all things start with, 0. Not a very hard level, just browsed to the snowmiser.counterhack.com and was redirected to http://snowmiser.counterhack.com/zone-0-11698563-7582-4A51-B567-B4710BBE783F/ . I then clicked on view source in my browser and collected the first flag:
<!– The flag for this level is 3b5a630fc67251aa5555f4979787c93f –>
Looking at the page, Snow Miser mentions the next level starts with
zone-1-D2E31380-50E6-4869-8A85-XXXXXXXXXXXX
Well, I thought to myself, I guess Dirbuster and Burp Intruder are out. I wonder if Snow Miser gave away any hints.
I recall that there was a picture in Snow Miser’s twitter feed and open it up. When I can finally take my eyes off of the wonderful book in the picture, I focus in on the ice cold glass and see a reflection.
Using my ultra-powerful imaging tool MSPaint, I rotate the image and there I see the some numbers:
F9CDB3AF6226
I quickly go back to my browser and go to the link zone-1-D2E31380-50E6-4869-8A85-F9CDB3AF6226. Success. I clicked on view source and get the flag:
<!– The flag for this level is 38bef0b61ba8edda377b626fe6708bfa –>
Zone-1 says:
“One of my minions, who has been turned into a snowman, messed up and changed the URL for Zone 2. If you have access to this level you can analyze the images and access the next zone.”
I download the on.png, flick the controls to off and download the off.jpg and inspect them with exiftool.
exiftool off.jpg
ExifTool Version Number : 7.89
File Name : off.jpg
Directory : .
File Size : 64 kB
…..
User Comment : IceIceBaby!
Interesting. I wonder if they tried to hide something in the off.jpg.
I run steghide:
steghide –extract –passphrase IceIceBaby! -sf off.jpg
then look at txt file it generated
zone-2-6D46A633-25D7-42C8-AF94-8E786142A3E3
I paste that into my browser and collect the flag:
<!– The flag for this level is b8231c2bac801b54f732cfbdcd7e47b7 –>
There didn’t seem to be any clues on the page to get me to the next level, so I went back to my recon notes. Oh yeah…the Android backup.
I recall that in the SANS 575 course we were shown that there is a lot of good information in .db files, which are often sqlite format. There are a myriad of ways to go through (like SQLite Database Browser, Python with sqlite3) but I stick with a little trick I learned in the Windows Command Line Kung Fu class:
c:\android.data\data>findstr /s “counterhack” *.db
and get the following result back
Snow Miser SnowTalk HMI for the Global Chiller Control System – Zone 3 http://snowmiser.counterhack.com/zone-3-EAB6B031-4EFA-49F1-B542-30EBE9EB3962
I browse to the site and collect the flag:
<!– The flag for this level is 08ba610172aade5d1c8ea738013a2e99 –>
(By the way…did you see how this backup was done? Hint: look in the bersersker.android.apps.sshdroid\home folder)
The zone-3 page says:
To verify your key you can check the previous Zone 4 URL:
zone-4-F7677DA8-3D77-11E2-BB65-E4BF6188709B
20d916c6c29ee53c30ea1effc63b1c72147eb86b998a25c0cf1bf66939e8621b3132d83abb1683df619238
The new Zone 4 encrypted string is: 20d916c6c29ee54343e81ff1b14c1372650cbf19998f51b5c51bf66f49ec62184034a94fc9198fa9179849
Josh explaining crypto starts echoing in my head….known plain text…known cipher text… XOR…same key… and I know what to do.
I re-read one of my favorite posts from one of my favorite blogs, http://blog.commandlinekungfu.com/2011/01/episode-132-enigma.html, and I follow the post by first ASCII hex encoding the old link:
python
import binascii
print binascii.b2a_hex(“zone-4-F7677DA8-3D77-11E2-BB65-E4BF6188709B”)
and set this into a variable
- oldzone4=(0x7a 0x6f 0x6e 0x65 0x2d 0x34 0x2d 0x46 0x37 0x36 0x37 0x37 0x44 0x41 0x38 0x2d 0x33 0x44 0x37 0x37 0x2d 0x31 0x31 0x45 0x32 0x2d 0x42 0x42 0x36 0x35 0x2d 0x45 0x34 0x42 0x46 0x36 0x31 0x38 0x38 0x37 0x30 0x39 0x42)
Then take the verify key from Snow Misers web page,
zone4verify=(0x20 0xd9 0x16 0xc6 0xc2 0x9e 0xe5 0x3c 0x30 0xea 0x1e 0xff 0xc6 0x3b 0x1c 0x72 0x14 0x7e 0xb8 0x6b 0x99 0x8a 0x25 0xc0 0xcf 0x1b 0xf6 0x69 0x39 0xe8 0x62 0x1b 0x31 0x32 0xd8 0x3a 0xbb 0x16 0x83 0xdf 0x61 0x92 0x38)
and I get the “magic” key using a the command from the Command Line Kung Fu article
for ((i=0; $i < ${#oldzone4[*]}; i++)); do printf “%02X” $((${oldzone4[$i]} ^ ${zone4verify[$i]})); done
5AB678A3EFAAC87A07DC29C8827A245F273A8F5CB4BB1485FD36B42B0FDD4F5E05709E0C8A2EBBE851AB7A
Note: If you tried the Hal’s version from the blog post like I did you might have stayed up way past hot chocolate time trying to figure out why it didn’t work, then eventually try Tim’s PowerShell script which works as expected and then finally realize that Hal’s command skips the leading 0. To get the leading 0’s, simply add a %02X to format the output.
Now with that “magic” key, there is no need to actually figure out the plain text encryption password. Thanks to the associative and commutative power of XOR (a XOR b = c, c XOR b = a , b XOR c = a) I take that “magic” key
magickey=(0x5a 0xb6 0x78 0xa3 0xef 0xaa 0xc8 0x7a 0x07 0xdc 0x29 0xc8 0x82 0x7a 0x24 0x5f 0x27 0x3a 0x8f 0x5c 0xb4 0xbb 0x14 0x85 0xfd 0x36 0xb4 0x2b 0x0f 0xdd 0x4f 0x5e 0x05 0x70 0x9e 0x0c 0x8a 0x2e 0xbb 0xe8 0x51 0xab 0x7a)
And the verify for the new link from the Snow Misers notes
newzone4=(0x20 0xd9 0x16 0xc6 0xc2 0x9e 0xe5 0x43 0x43 0xe8 0x1f 0xf1 0xb1 0x4c 0x13 0x72 0x65 0x0c 0xbf 0x19 0x99 0x8f 0x51 0xb5 0xc5 0x1b 0xf6 0x6f 0x49 0xec 0x62 0x18 0x40 0x34 0xa9 0x4f 0xc9 0x19 0x8f 0xa9 0x17 0x98 0x49)
XOR them together
for ((i=0; $i < ${#newzone4[*]}; i++)); do printf “%02X” $((${newzone4[$i]} ^ ${magickey[$i]})); done
7A6F6E652D342D39443436393336372D423630452D344530382D424446312D464544374343373441463333
And converted that to ascii:
python
import binascii
print binascii.a2b_hex(“7A6F6E652D342D39443436393336372D423630452D344530382D424446312D4645 44374343373441463333”)
zone-4-9D469367-B60E-4E08-BDF1-FED7CC74AF33
I pasted that into my browser and picked up the flag for level 4:
<!– The flag for this level is de32b158f102a60aba7de3ee8d5d265a –>
For zone 5, Snow Miser touts a new one-time password (OTP) function to keep everyone out. I recall Heat Miser mentioning something about .svn from my initial information gathering and follow the steps laid out by Tim Medin http://pen-testing.sans.org/blog/pen-testing/2012/12/06/all-your-svn-are-belong-to-us”
wget http://snowmiser.counterhack.com/zone-5-89DE9B26-CF7D-4B07-88DE-7A2F0A7B16FE/.svn/wc.db
–2012-12-11 11:24:03– http://snowmiser.counterhack.com/zone-5-89DE9B26-CF7D-4B07-88DE-7A2F0A7B16FE/.svn/wc.db
Resolving snowmiser.counterhack.com… 204.51.94.72
Connecting to snowmiser.counterhack.com|204.51.94.72|:80… connected.
HTTP request sent, awaiting response… 200 OK
Length: 34816 (34K) [text/plain]
Saving to: `wc.db’
100%[============================================>] 34,816 47.8K/s in 0.7s
2012-12-11 11:24:06 (47.8 KB/s) – `wc.db’ saved [34816/34816]
I look through the database
sqlite3 wc.db ‘select local_relpath, “.svn/pristine/” || substr(checksum,7,2) || “/” || substr(checksum,7) || “.svn-base” as alpha from NODES;’|
noaccess.php|.svn/pristine/41/4134e0e954d144ed932fd639b5a897f9ad47fff9.svn-base
index.php|.svn/pristine/7d/7d63810b0da679648fc20b4f1c84680ac08ec872.svn-base
Then go grab the file to examine how the OTP function works
wget http://snowmiser.counterhack.com/zone-5-89DE9B26-CF7D-4B07-88DE-7A2F0A7B16FE/.svn/pristine/7d/7d63810b0da679648fc20b4f1c84680ac08ec872.svn-base
The OTP takes the current time + or – a minute or two and hashes that with a common seed. I check to make sure the time on my machine is correct and set up my own PHP page to do the same OTP function.
alt;?php
function generate_otp($time) {
$pass = sha1(“$time 7998f77a7dc74f182a76219d7ee58db38be3841c”);
return($pass);
}
$currenttime = date(‘Y-m-d H:i’);
echo $currenttime;
echo “Here is your OTP: “;
echo generate_otp($currenttime);
I access my local copy and access the site to retrieve the flag:
<!– The flag for this level is 3ab1c5fa327343721bc798f116be8dc6 –>
Snow Miser: Mission Complete
heatmiser.conterhack.com
Just like with Snow Miser I started off with level 0. Browse to the heatmiser.counterhack.com and you get redirected and by looking at the source, I get the first flag:
<!– The flag for this level is 1732bcff12e6550ff9ea44d594001418 –>
Also on the page is a note:
We had a security concern where the Zone 1 URL ended up in search engine results. We added a file to prevent the search engines from caching these pages. The system is now secure an no unauthorized users have access to the URL.
Right away, I recall Kevin in the SANS 542 class telling us that a well behaved search engine will look at for a file named robots.txt and follow the directions in it. If it says to “Disallow: /somelink”, the web crawler will not crawl that link. But, Kevin says, a penetration tester should not be well behaved search engines, and always look at the robots.txt file. I access http://heatmiser.counterhack.com/robots.txt and see
User-agent: *
Disallow: /zone-1-E919DBF1-E4FA-4141-97C4-3F38693D2161
Disallow: /zone-2-*
Disallow: /zone-3-*
Disallow: /zone-4-*
Disallow: /zone-5-*
I then goto http://heatmiser.counterhack.com/zone-1-E919DBF1-E4FA-4141-97C4-3F38693D2161 and capture the flag:
<!– The flag for this level is d8c94233daef256c42bb95bd61382e02 –>
While looking at the source I see something interesting.
p>We had an issue with
<!– redacted, too many people clicked on the link and took it offline
<a href=”/zone-2-761EBBCF-099F-4DB0-B63F-9ADC61825D49″>Zone 2</a>
–>
Zone 2 and we had to temporarily remove the link.
Can it be so simple. Did someone actually think that commenting out something that is then put in the hands of an end user would be safe?
Yep, they did. I browse to http://heatmiser.counterhack.com/zone-2-761EBBCF-099F-4DB0-B63F-9ADC61825D49/ and collect the flag:
<!– The flag for this level is ef963731de7e886226fe4a6a6c2971f1 –>
There aren’t many hints on what to access the next level, so it’s back to re-examining the data from the information gathering stage.
I come across the comment by Snow Miser about transparency and look at the picture that Heat Miser posted on Twitter. By closely inspecting the picture, you can see the link in a window beneath the Metasploit console window.
I browse to zone-3-83FEE8BE-B1C6-4395-A56A-BF933FC85254 and retrieved the flag:
<!– The flag for this level is 0d524fb8d8f9f88eb9da5b286661a824 –>
To access zone 4 I clicked on the link provided on level 3. Right away, I got taken to a noaccess.php page. I go back to my Burp session (because I always surf with Burp) and look to see if there is a cookie or other variables that I can tamper with, but instead see that two pages were returned. Curious.
I setup Burp to intercept requests and responses then click the link again. First click gets a 302 response, but looking at the response, specifically the <div id=”content”> section, you can clearly see that this is not the page that was intended.
The next request then returns the noaccess.php page.
Now I understand Snow Miser’s Tweet:
Nice job Fire-for-Brains!http://memegenerator.net/instance/31429299 …
Heat Miser must have forgot to exit() after the redirect and the valid page was sent in addition to the redirect. I go back to the first page returned and in the content section I see
<h3><p>Current Access Level – <strong>Zone 4</strong></p></h3>
Link to <a href=”/zone-5-15614E3A-CEA7-4A28-A85A-D688CC418287/”>Zone 5</a>
I browse to that location and retrieve the flag:
<!– The flag for this level is e3ae414e6d428c3b0c7cff03783e305f –>
Accessing the zone-5 link again redirects to a noaccess.php.
I look in Burp, no hints from the redirector this time, but there is a cookie sent with the value UID=b8c37e33defde51cf91e1e03e51657da
Once again I consult my notes from information gathering. There was a Tweet about 1001 and cookies from Snow Miser. Hmm
In Burp I switch to the Decoder tab. Type 1001 in, do an MD5 hash and then ASCII encode it. The result is b8c37e33defde51cf91e1e03e51657da. The cookie being sent is the value ASCII(MD5(1001)).
Quite often, administration ids are numbered 0,1, 100,101, 1000 or 1001 so I try 0 and put the ASCII encoded MD5 value in for the uid cookie, no luck.
I try 1, which when MD5 hashed and ASCII encoded is c4ca4238a0b923820dcc509a6f75849b. Success. I pick up the flag:
<!– The flag for this level is f478c549e37fa33467241d847f862e6f —>
Heat Miser: Mission Complete
Epilogue
I wrote up the report and handed it to Mrs. Clause for QA. With some minor changes, the report was sent to the Miser brothers who signed off on the test and lived up to their agreement.
Santa was soon back in the Christmas spirit and Christmas was saved for another year. I even got the day off.