Monday, January 11, 2016

SANS Holiday Hack Challenge 2015 - Part 0x02



This post is the second of the SANS Holiday Hack Challenge 2015 series, which includes part 04 of the Challenge, specifically Web Application Pen-Testing to the SuperGnomes: 01, 02, 03 and 04. I've left the SuperGnome 05 for the next post, because it requires a different approach.

I've changed all IP addresses to localhost, because the targets used in the Challenge were using AWS, and those IPs can be assigned to other tenants.


Part 4: There’s No Place like Gnome for the Holidays: Gnome Pwnage

7) Please describe the vulnerabilities you discovered in the Gnome firmware.

- Passwords are stored in the Database in clear text. As previously shown, the passwords are stored in clear text, it’s not using a Salt value with a one-way function, e.g. PBKDF2.

- route/index.js: 
  • router.post(“/“): The Login function uses unsanitized user supplied data in a mongodb query: “ db.get('users').findOne({username: req.body.username, password: req.body.password}, function (err, user)”. Changing the username and/or password values to something like ‘{“$gt”:””}’ or ‘{“$ne”:””}’ will result in the following mongodb query: “db.users.findOne({username: “admin”, password: {“$ne”:””} })”, returning the admin JSON object. A better approach is shown below (but, commented out):  db.get('users').findOne({username: (req.body.username || "").toString(10), password: (req.body.password || "").toString(10)}, function (err, user) “. The method “toString()” escapes special character from the user data, e.g. "{'$ne':''}".toString(10)” results in  '{\'$ne\':\'\’}'.
  • router.post(“/settings’): This function creates a directory based on user supplied data in the parameter “filen”:
          “”"
           var filen = req.body.filen;
           var dirname = '/gnome/www/public/upload/' + newdir() + '/' + filen;
           ...
           fs.mknewdir(dirname.substr(0,dirname.lastIndexOf('/')));
          “”"

The newdir() function returns a random value (8 characters) and fs.mknewdir() creates a directory with the path provided as a parameter (it creates all directories needed to comply with the dirPath parameter). The problem is the user supplied data as an input to fs.mknewdir(), the variable “filen” can have any value, including “newDir/data/file” or even worst “../../../newDir/file”. As a result, an attacker can provide a directory with a specific path to be created (as long as the node.js process has permissions to write to the provided path).

  • router.post(“/files”): This function executes an eval() function on user supplied data:
          “”"
           var postproc_syntax = req.body.postproc;
             …..
           result = eval('(' + postproc_syntax + ')');
          “”"

The eval() function executes JavaScript code/expressions. In this particular case, if the user supplied data, in the “postproc” parameter, is JavaScript code, then eval() will execute this code, and store its result in the “result” var. 

  • router.get(‘/cam’): There is a potential LFI in this function:

          “”"
           var camera = unescape(req.query.camera);
           //if (camera.indexOf('.png') == -1) // STUART: Removing this...I think this is a better solution... right?
       camera = camera + '.png'; // add .png if its not found
          ...
       fs.access('./public/images/' + camera, fs.F_OK | fs.R_OK, function(e)
          “”"


The code is adding a “.png” extension to the user supplied data (camera), and later is verifying the existence of the supplied value concatenated to  “./public/images/". First, this code is vulnerable to PATH transversal, submitting something like this: “../../../../etc/../gnome/www/public/images/1” works. 

A better approach could be to validate the user supplied input against the content of “./public/images”.  However, it’s still not easy to exploit this vulnerability, because node.js doesn’t have the termination null () problem as PHP does. There is an interesting piece of code, but it’s commented out: “if (camera.indexOf('.png') == -1)”, If this piece of code is enabled, then the vulnerability is easier to exploit, because this code is only checking for the presence of “.png” in the camera variable, but the extension could be anywhere in the path: “../foo.png/../files/“ (indexOf() returns the index of the first occurrence  of the specified value, and -1 means that the value is not found).  


8) ONCE YOU GET APPROVAL OF GIVEN IN-SCOPE TARGET IP ADDRESSES FROM TOM HESSMAN IN THE DOSIS NEIGHBORHOOD, attempt to remotely exploit each of the SuperGnomes.  Describe the technique you used to gain access to each SuperGnome's gnome.conf file.  YOU ARE AUTHORIZED TO ATTACK ONLY THE IP ADDRESSES THAT TOM HESSMAN IN THE DOSIS NEIGHBORHOOD EXPLICITLY ACKNOWLEDGES AS “IN SCOPE." ATTACK NO OTHER SYSTEMS ASSOCIATED WITH THE HOLIDAY HACK CHALLENGE."

- SG-01: 

The username and password found in the gnome database are enough to download all files from the “files” section:

  • 20141226101055.zip (PCAP file)
  • camera_feed_overlap_error.zip (png file)
  • factory_cam_1.zip (png file)
  • gnome.conf
  • gnome_firmware_rel_notes.txt
  • sgnet.zip (sgstatd source code)
  • sniffer_hit_list.txt

Gnome Serial Number: NCC1701

- SG-02: 

The username and password retrieved from the gnomeDB were used to access the SuperGnome. However, this time I couldn’t just download all files from the files menu, because the system prevented me to download those file (it was disabled). 

However, using the configuration upload form from the settings menu, I created a directory with a “.png” in the middle of the name:

"curl -v -b data  -d "filen=..%2Fdir.png%2Fdata" -d "file=gnome.conf"  http://${IP}/settings"

Where data is a valid cookie. With the above script, the system created the directory “dir.png” in “/gnome/www/upload/". Then using the following script I was able to download all files from the files menu:


The vulnerabilities exploited are: "router.post(“/settings’)”, which allow me to create a directory with a custom name (“dir.png”) and "router.get(‘/cam’)” using a LFI. The problem was that “if (camera.indexOf('.png') == -1)” was enabled. 

The script used can be found here: https://github.com/skysec/SANSHolidayHack2015/tree/master/part04/sg-02/scripts, and the files are:

  • 20150225093040.zip 
  • factory_cam_2.zip 
  • gnome.conf 
  • gnome_firmware_rel_notes.txt 
  • sgnet.zip 
  • sniffer_hit_list.txt

Gnome Serial Number:  XKCD988

- SG-03: 

I couldn’t authenticate using the username and password retrieved from the Gnome database. So, I crafted and sent a new form with username and password using a JSON body as follow:


This was possible due to the vulnerability found in the authentication function, as explained in the previous question: “ db.get('users').findOne({username: req.body.username, password: req.body.password}, function (err, user)"

The script used can be found here: https://github.com/skysec/SANSHolidayHack2015/tree/master/part04/sg-03/scripts, and the files are:

  • 20151201113356.zip 
  • factory_cam_3.zip 
  • gnome.conf 
  • gnome_firmware_rel_notes.txt 
  • sgnet.zip 
  • sniffer_hit_list.txt

Gnome Serial Number: THX1138 

- SG-04: 

The file section has the file upload option enable. This section has a vulnerability in an eval() function, with the postproc parameter. I was able to craft a request with JavaScript code in this parameter to include the files in the /gnome/www/files directory in the response, using the result var:



The JavaScript reads a file, using the synchronous version of readFile(), and encodes the result using BASE64. The result of this code is stored in the variable “result”, which is sent back to the client as part of the server response in HTML. The advantage of base64, is the ability to download binaries files as part of the HTML response page.Then a bit of “prost-processing”, parsing the HTML to retrieve the base64 data, and decode the base64.

The scripts used can be found here: https://github.com/skysec/SANSHolidayHack2015/tree/master/part04/sg-04/scripts, and the files are:

  • 20151203133815.zip 
  • factory_cam_4.zip 
  • gnome.conf 
  • gnome_firmware_rel_notes.txt 
  • sgnet.zip 
  • sniffer_hit_list.txt

Gnome Serial Number: BU22_1729_2716057  


No comments:

Post a Comment