Skip to content

Latest commit

 

History

History
 
 

holiday_hack_challenge_2015

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

SANS Holiday Hack Challenge

John Hammond | Thursday, December 31st, 2015


This document serves the purpose to explain and describe how we unraveled any questions we were able to answer as part of the SANS Holiday Hack Challenge.

I say 'we' referring to us few cadets part of the "Cyber Team" (nicknamed 'objEEdump') at the United States Coast Guard Academy. We attended the SANS Hackfest in Virginia during November of this year, and that is where we first heard of the Holiday Hack Challenge and were excited to participate once it was released.

Below is our writeup, yet note that there are more than plenty of more thorough and fleshed out writeups available on the Internet that actually completed the entire challenges. Here is a list of some of those available:

Other Writeups


Let get started with our own writeup!

The Beginning

Part 1: Dance of the Sugar Gnome Fairies: Curious Wireless Packets

Get the PCAP!

After we retreived the first packet capture, we first opened it up Wireshark to examine the network traffic more closely.

It was easy enough to see the broadcast packets and beacon frames, and we saw the two SSIDs titled December and DosisHome-Guest.

As we looked through the pcap more and more, we eventually found some interesting stuff in the packets sent over DNS. In the raw text they carried, it looked they carried some base64 encoded data! Ha, the trailing = equal signs always give it away.

Interesting Packets

We whipped up a Python script pcap_extract_baseline.py to scrape through all the packets and pick out the interesting stuff. It would use scapy to run through the packets, and the base64 module to decode the data. That made it pretty easy to see what was going on. (We wrote our script before the hand-holding helper one was released in the game).

python #!/usr/bin/env python

from scapy.all import * import base64 as b

pcap = rdpcap('../downloads/giyh-capture.pcap') udp_packets = pcapUDP

for packet in udp_packets:

if DNS in packet[0]:
	carved_data = None
	try:
		carved_data = b.b64decode(packet[0][1].an.rdata)
	except:
		pass

	if ( carved_data ): 
		print carved_data

With the output of our script it was easy to identify commands being executed and even data for a file being passed over the wire.

To make things a little easier on the eyes (not spewing the data for the transferred file everywhere), we actually threw our script into strings, and got to see more clearly the commands being sent across the network.

Strings Output

So to answer the first question...


1) Which commands are sent across the Gnome’s command-and-control channel?

The commands we saw were:

  • iwconfig
  • cat /tmp/iwlistscan.txt

And to answer the second question, we already had a working script... all we needed it to do was scrape out the file data in the loop and then jam it all together. So we created a file handle and added whatever we processed to it.

Here's the jist of our modification, how it added into the loop:

if ( carved_data.startswith('FILE:') ):
	piece_of_file =  carved_data.split('FILE:')[1]
	handle.write(piece_of_file)

Keep in mind the handle variable would have been created at the top of the new script; you can see the whole change in our separate script pcap_extract_image.py

And our code gives us the file! As it turns out it was a JPG image... of the Gnome in your Home!


2) What image appears in the photo the Gnome sent across the channel from the Dosis home?

The image sent across the channel was:

carved_image.jpg

Looks like the text in the image is: GnomeNET-NorthAmerica. We showed it to Josh, and he said we were right!

Correct!

So next we go to talk to Jessica and get the firmware, and move onto the next part!

Part 2: I’ll be Gnome for Christmas: Firmware Analysis for Fun and Profit

Starting Firmware...

When we got the firmware downloaded, we ran binwalk -e to extract out a file system. Easy enough, we had a whole Linux file system on our hands.

binwalk output

To find out a bit more information on the kind of system we were working with, we checked out any release information stored in the /etc directory.

/etc$ cat *release
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='Bleeding Edge'
DISTRIB_REVISION='r47650'
DISTRIB_CODENAME='designated_driver'
DISTRIB_TARGET='realview/generic'
DISTRIB_DESCRIPTION='OpenWrt Designated Driver r47650'
DISTRIB_TAINTS=''

And we saw that the system had a /www directory so we could see some web files. After we changed into that directory, it was clear what the web server was running.

/www$ ls
app.js  bin  files  node_modules  package.json  public  routes  views

Between all the .js JavaScript files and even the blatant node_modules directory name, it was evident.


3) What operating system and CPU type are used in the Gnome? What type of web framework is the Gnome web interface built in?

  • The Gnome is running OpenWRT with an x86_64 CPU.
  • The web interface is built in the Node.js web framework.

While we were hanging out in the /etc directory, we also saw a mongod.conf file, so it seemed the Gnome was running MongoDB.

And this was clear even when we were poking around in the /www directory to see the web server files. Obviously in a lot of the JavaScript source code it was necessary to reference the database, so we could see it was clearly using Mongo.

Mongo

Now earlier, when we saw that mongod.conf file in /etc, we took a look at it to see if it had anything interesting: and it told us the MongoDB files lived in /opt/mongodb.

/etc$ cat mongod.conf
# LOUISE: No logging, YAY for /dev/null
# AUGGIE: Louise, stop being so excited to basic Unix functionality
# LOUISE: Auggie, stop trying to ruin my excitement!

systemLog:
  destination: file
  path: /dev/null
  logAppend: true
storage:
  dbPath: /opt/mongodb
net:
  bindIp: 127.0.0.1

It's a shame there is no logging. ;)

So in the /opt/mongodb directory we can see all of the database files that Mongo is using. Cool!

We actually found the password when we were perusing gnome.0 in hexedit.

First password

We could have totally found it with strings, even, but for whatever reason we were in hexedit first. Regardless, we got the password and had the answer.


4) What kind of a database engine is used to support the Gnome web interface? What is the plaintext password stored in the Gnome database?

  • The Gnome web interface is using MongoDB as its database.
  • The plaintext password stored in the database for the admin user is SittingOnAShelf.

Correct!

We showed Jessica the password, and it looks like we got it right. Next, we'll talk to Dan and Josh Wright and move onto the next part!

Part 3: Let it Gnome! Let it Gnome! Let it Gnome! Internet-Wide Scavenger Hunt

We took a look at the /etc/hosts file within our firmware to hopefully find an IP address of a running SuperGnome. Looks like we got it!

There is even a comment that this SuperGnome is in North America. Okay, that makes sense, considering the image we got in Part 1. So if we tried to poke at this IP address online in a web browser... we get a response, it's up!

$ cat /etc/hosts
127.0.0.1 localhost

::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# LOUISE: NorthAmerica build
52.2.229.189    supergnome1.atnascorp.com sg1.atnascorp.com supergnome.atnascorp.com sg.atnascorp.com

SG 01

Apparently this is SuperGnome 01.

And, since we were able to get a password out of the database, we can log in with those credentials, admin and SittingOnAShelf. Now we can poke around with the live box and see if we can interact with it in any other ways and get more information. Since we've got the source code within the firmware, it should be easy to see everything the web server is actually doing.

But, we've got to find all the other SuperGnomes. Well, uh, how do we do that?

We were stuck here for a good while (so we passed the time going through the "in-game" achievements and talking with all of the NPCs).

Eventually we realized what the big hint to "sho Dan what you've found" was, considering it was repeated enough. We needed to literally Shodan the SuperGnome... as in search in Shodan 'SuperGnome!'

Shodan

That gave us all the information we needed to answer the questions for this part.


5) What are the IP addresses of the five SuperGnomes scattered around the world, as verified by Tom Hessman in the Dosis neighborhood?

The IP addresses of the five SuperGnomes are as follows:


6) Where is each SuperGnome located geographically?

The locations for the SuperGnomes are:


We verified with Tom that all of these IP addresses are in fact in scope. Looks like we can move onto the next part!

Shodan

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

Now that we were in a position where we could really mess with all of the SuperGnomes, we cracked down on finding vulnerabilties and pillaging for whatever we could.

We had looked at SuperGnome 01 extensively before we actually found the others through Shodan, so we downloaded everything off of their /files page.

  • The 20141226101055.zip contained yet another pcap file with a clear text email we could read right in Wireshark, by choosing to Follow TCP Stream. The email was from "c" c@atnascorp.com, to jojo@atnascorp.com, discussing the architecture of the Gnome in Your Home. It was clear they had attached a JPG image, but it was encoded in base64. We were able to retrieve the data and decode it to get the following image.
  • The camera_feed_overlap_error.zip contained an image of obviously a camera feed that was overlapped; we pieced together from reading through the GnomeNET messages that we needed to use this to try and get the 6th image for the boss' office. Since all of the camera pictures have been XOR'd, if we were able to snag all the images we could "unXOR" them and get the boss' picture!
  • factory_cam_1.zip of course had the factory image from this SuperGnome, that we figured we should use to try and retrieve the 6th image from the boss' room, if we were to gather all 5 factory images.
  • gnome.conf was apparently the goal for getting into each SuperGnome; so we downloaded it and started a collection.
  • The gnome_firmware_rel_notes.txt didn't look like it had too much information for us other than the notion of the SuperGnome's sniffer.
  • sgnet.zip looked like it included the source code for some binaries used to manage one or more of the SuperGnomes. We figured we would have to try and break the code at some point, but we it didn't seem like we needed it just then -- but we still pocketed it for safekeeping.
  • The sniffer_hit_list.txt looked interesting, but it seemed like nothing we could work with at the moment.

GiYH Architecture

We perused through the /cameras section but we obviously were not able to view more than two cameras, so we left that alone. We couldn't really interact with the web pages of SuperGnome 01, so we figured we would try looking for vulnerabilities with the others.

Since we had the source code of the webpages, we figured we would start by more thoroughly examining the source code and then beginning to poke at the other SuperGnomes.

We opened up what seemed to be the logic for the webserver, index.js under the /www/routes directory of the firmware.

I'll try and enumerate what vulnerabilities we found and the progression we had followed to compromise some of the SuperGnomes. I hope this will act as a long-winded answer to questions seven and eight: 7) Please describe the vulnerabilities you discovered in the Gnome firmware, and 8) Describe the technique you used to gain access to each SuperGnome’s gnome.conf file.

Something we saw interesting in the source code was this "camera viewer", under /cam that allowed you to look at an individual camera image if you gave it an HTTP GET request variable.

In the firmware's code (SuperGnome 01's rendition of the software), a user apparently by the name of 'Stuart' had commented out a line of code that would just check if the supplied filename variable had the text '.png' in the filename. Under that condition, the filename wouldn't have to have .png as a file extension.. it just had to be somewhere in the string. The current code in SuperGnome 01 would append on the '.png' string no matter what, but the comment led us to wonder... do some of the other SuperGnomes act this way?

// CAMERA VIEWER
// STUART: Note: to limit disclosure issues, this code checks to make sure the user asked for a .png file
router.get('/cam', function(req, res, next) {
  var camera = unescape(req.query.camera);
  // check for .png
  //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
  console.log("Cam:" + camera);
  fs.access('./public/images/' + camera, fs.F_OK | fs.R_OK, function(e) {
    if (e) {
	    res.end('File ./public/images/' + camera + ' does not exist or access denied!');
    }
  });
  fs.readFile('./public/images/' + camera, function (e, data) {
    res.end(data);
  });
});

We naturally moved onto SuperGnome 02 after SuperGnome 01, and SG02 was an interesting case because we could log in with the admin and SittingOnAShelf credentials we initially discovered, but we could not download anything from the /files page. But, we still wanted that gnome.conf file!

So when we were manually testing that /cam page... it did not append a '.png' extension if we supplied one in the string! It just checked to see if the string was in there, just like the commented code had suggested!

After some more tinkering with SuperGnome 02, we saw we were able to upload our own settings file from the /settings page. This let us create a file on the server... could we exploit that?

// SETTINGS UPLOAD
router.post('/settings', function(req, res, next) {
  if (sessions[sessionid].logged_in === true && sessions[sessionid].user_level 99) { // AUGGIE: settings upload allowed for admins (admins are 100, currently)
    var filen = req.body.filen;
    var dirname = '/gnome/www/public/upload/' + newdir() + '/' + filen;
    var msgs = [];
    var free = 0;
    disk.check('/', function(e, info) {
      free = info.free;
    });
    try {
      fs.mknewdir(dirname.substr(0,dirname.lastIndexOf('/')));
      msgs.push('Dir ' + dirname.substr(0,dirname.lastIndexOf('/')) + '/ created successfully!');
    } catch(e) {
      if (e.code != 'EEXIST')
	throw e;
    }
    if (free < 99999999999) { // AUGGIE: I think this is breaking uploads?  Stuart why did you set this so high?
      msgs.push('Insufficient space!  File creation error!');
    }
    res.msgs = msgs;
    next();
  } else
    res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
});

The source code tells us that this functionality will actually create a new directory for us, based off the 'lastIndexOf()' a / forward-slash character. Well then... what is to stop us from creating a directory with the string '.png' in its name? If we did that, we could use that directory as a "jumping off-point" in the /cam page and see if we could get any other files, not just camera images!

When we tried this, we weren't able to actually upload the file (just like it is explained in the source code comments)... but we still created our directory!

SG02

So in combination with that directory we were able to create through the /settings page and the 'camera viewer' service in the /cam page... we were able to find a local file inclusion vulnerability!

Local File Inclusion

Now, we could grab some more interesting stuff from the webserver. Source code, the files in /files we were previously unable to snag (including our goal gnome.conf), and any other logs or info we wanted.

Just like Josh Wright hinted at in-game, we were able to combine that file upload feature to get that LFI attack. So, after pillaging everything we could, we could consider SuperGnome 02 compromised!

Local File Inclusion/Upload

Here's a break down of what we snagged from SuperGnome 02:

  • /www/routes/index.js to see its source code and get ideas for other vulnerabilities
  • gnome.conf, the factory_cam_2.png and all other files (we read another email in the pcap, but again we just pocketed it)
  • /etc/mongod.conf to see how the Mongo database was set up.
    • I tried to snag the database files /var/lib/mongodb/gnome.0 but I could not download them; I assume they are not owned by the current user and are instead owned by the MongoDB user
  • /var/lib/mongod.log ... we were still able to see the log file, though! So I caught the password for stuart: MyBossIsCrazy.
  • .bash_history to see if there were any interesting commands run recently..
  • /var/log/boot.log to get more information about the machine, even the local IP Address and gateway!

After considering SuperGnome 02 complete and even grabbing a new set of credentials out of it, we considered it a success and moved on to SuperGnome 03.

But, SuperGnome 03 seemed out of reach so far. Our admin and SittingOnAShelf login did not work and neither did our newly acquired stuart and MyBossIsCrazy. Dang. We moved onto SuperGnome 04.

SuperGnome 04 is another case where we can log in with admin (couldn't with stuart), but cannot download any of the files (including gnome.conf) from /files.

But it looks like we could actually upload a file to that /files page, which differs from SuperGnome 01 and 02. We took a look at the source code from the firmware, and it looks like the functionality works like this:

// FILES UPLOAD
router.post('/files', upload.single('file'), function(req, res, next) {
  if (sessions[sessionid].logged_in === true && sessions[sessionid].user_level 99) { // NEDFORD: this should be 99 not 100 so admins can upload
    var msgs = [];
    file = req.file.buffer;
    if (req.file.mimetype === 'image/png') {
      msgs.push('Upload successful.');
      var postproc_syntax = req.body.postproc;
      console.log("File upload syntax:" + postproc_syntax);
      if (postproc_syntax != 'none' && postproc_syntax !== undefined) {
        msgs.push('Executing post process...');
        var result;
        d.run(function() {
          result = eval('(' + postproc_syntax + ')');
        });
        // STUART: (WIP) working to improve image uploads to do some post processing.
        msgs.push('Post process result: ' + result);
      }
      msgs.push('File pending super-admin approval.');
      res.msgs = msgs;
    } else {
      msgs.push('File not one of the approved formats: .png');
      res.msgs = msgs;
    }
  } else
    res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
  next();
});

Our eyes were immediately drawn to the eval statement. If we could successfully upload a file and get that eval statement to run, we might be able to get Node.js to run any code for us; maybe even execute a system shell command, like with bash.

It looked like that postproc_syntax variable is even something we supply. We could definitely inject it with some JavaScript code and try to exploit it.

From the webpage, the postproc_syntax variable seemed to be filled from a drop-down list. We obviously wanted to enter different text than what those options were offering us, so we just changed the element in our web browser so we could feed it any data. We did this simply with the 'Inspect Element' option through Firefox.

Input!

Now we could enter any text. If we gave the page a PNG image and actually gave it some JavaScript to execute, we had exploited another vulnerability: remote code execution!

Here's the JavaScript we had the server run, so we could actually run system commands:

require('child_process').exec('<our_command_would_go_here>')

Upload!

We got a response of a JavaScript object, so it seemed like it worked. But... there was no way to really tell. We weren't really able to see the output of the command, and it didn't seem like we were able to successfully run anything that would 'phone home' and prove to us that our commands really were getting in.

We weren't able to get a netcat listener, or a backdoor; we tried -- a lot. But we couldn't just let this go; if we had remote code execution of SuperGnome 04, we had another box down. So we tried something crafty to really see if our commands were running.

What we did is we ran a curl command to have the SuperGnome post its command output to something like an online clipboard; where you could just thow up data and retrieve it later. We used cl1p.net. From there, if the command successfully ran, we could view the output and analyze our results.

It was messy; really messy; but it worked. We actually ended up scripting something so that we would run a curl command on our own and pull the results from cl1p.net -- it was like a simulated shell. Granted, we only had output -- it was not a very stable shell or terminal where we could supply more input with other interactive commands -- but it was still something.

Please check out our code to create that simulated shell. It works off of a sessionid cookie for the SuperGnome 04 so it understands you are logged in and can upload files, and then it automates the exploit for you. Much better than trying to do it through the web pages over and over again.

Shell

With this set up, we could start to pillage. We grabbed the usual, source code and things within /files, including gnome.conf. Those were just plain text files, so they were easy to transfer. Things like the .zip files were not as easy. We could still transfer them through our simulated shell; it just would be much more messy than it already was.

But, since we had a remote code execution on this box, we wondered if we could actually explore more of its database. We could connect to the MongoDB server and dump information, probably with just one single command. We already had the database password: we had it all along, once we had the firmware. It has been stored in app.js of the /www directory for each SuperGnome; and the password did not even change between each one.

So we did some research and cooked up this one line to dump anything interesting from the database; and we got some interesting results!

$ mongoexport --db gnome --host localhost --username gnome --password KTt9C1SljNKDiobKKro926frc --port 27017 -c users
Executing "mongoexport --db gnome --host localhost --username gnome --password KTt9C1SljNKDiobKKro926frc --port 27017 -c users"...
Command executed.
2016-01-03T04:22:21.552+0000    connected to: localhost:27017
{"_id":{"$oid":"56229f58809473d11033515b"},"username":"user","password":"user","user_level":10.0}
{"_id":{"$oid":"56229f63809473d11033515c"},"username":"admin","password":"SittingOnAShelf","user_level":100.0}
{"_id":{"$oid":"5647438777cb0339cd14fd09"},"username":"nedford","password":"AllIWantForXmasIsYourPresents","user_level":100.0}

We found a user nedford and his password AllIWantForXmasIsYourPresents. Ha! This must be the same Nedford we saw in the source code comments, and the individual whose approval we need to upload any files! So, if we log in as him, we can download anything off the /files page easily through the regular web interface.

From there, we got the .zip files downloaded. That led us to answer the final set of questions.

We retrieved the pcap file from SuperGnome 04 and were able to clearly see another email, again from "c" but this time addressed to "psychdoctor@whovillepsychiatrists.com". Interestingly enough, the email is signed from "Cindy Lou Who".

This leads us to the last few answers...

Cindy Lou Who


9) Based on evidence you recover from the SuperGnomes’ packet capture ZIP files and any staticky images you find, what is the nefarious plot of ATNAS Corporation?

It looks as though the nefarious plot of the ATNAS corporation is to use these "Gnome in your Home" Internet-attached devices as a means of spying on over two million homes and peering into living rooms, to see what presents they will have for Christmas. Then, the ATNAS corporation will steal from these homes and sell their gifts for more and more riches!


10) Who is the villain behind the nefarious plot?

The evil mastermind behind this entire operation is none other than the Miss Cindy Lou Who!