- Prelude: The Quest
- Part One: Wireless Packet Analysis
- Part Two: Firmware Analysis
- Part Three: Hunting Gnomes with Shodan
- Part Four: Global Pwnage
- SuperGnome 1: Password Reuse
- SuperGnome 2: Local File Inclusion, Path Traversal
- SuperGnome 3: NoSQL Injection
- SuperGnome 4: Server-Side JavaScript Injection
- SuperGnome 5:
- Part Five: Meet the Villain
- Or read the entire solution in one LONG page
Part One Challenges:
- Which commands are sent across the Gnome’s command-and-control channel?
- What image appears in the photo the Gnome sent across the channel from the Dosis home?
Useful tools: Scapy, Wireshark
Summary: The gnomes communicate with a Command and Control server using covert DNS traffic; the DNS traffic contains base64-encoded commands from the server to the gnome, and a base64-encoded JPG image is sent from the gnome to the server.
I love that even more than most CTFs, this one is designed to appeal to kids. My 12-year-old daughter has shown an interest in cybersecurity, so this turned into a great way to teach her a few things. Even better, most of the lessons were in response to her own questions.
My tool of choice for packet analysis is Wireshark, a tool I first used when it was still called Ethereal. To understand packet analysis though, it is useful to understand a little bit about how networks work.
Traditionally, network concepts are defined in terms of "layers." At each layer, one device talks to another, and each layer does not care what is happening at the other layers. For some basic background, check out this simplified explanation I gave to my 12 year old.
Upon opening the packet capture file (known as a pcap) in Wireshark, it pretty quickly became apparent that all of the packets fall into two types – 802.11 beacon and probe frames, and DNS traffic. Since the 802.11 traffic is all local (it is a layer 1 / layer 2 protocol, after all, which defines how WiFi devices communicate with one another), we focused on the DNS traffic first.
You may recall a previous post describing DNS as a phone book or contact list, translating human-friendly website names into computer-friendly Internet addresses. What we found was traffic between a local device 10.42.0.18 and a remote server 52.2.229.189. For kicks, I did a nslookup and a whois on the destination; it is an Amazon Web Services instance, so nothing particularly interesting at first glance.
What was interesting though was the content of the traffic. The local device repeatedly sends queries for the address of a server named cmd.sg1.atnascorp.com (which, by the way, does not resolve). The responses are, shall we say, unusual?
The DNS standard defines a variety of "resource record" types. Some common types are "A" for a computer hostname record; "MX" for a mail server; "NS" for a name server; and "TXT" for descriptive text or comments. The first handful of DNS responses in the pcap all contain in the answer a TXT record containing “Tk9ORTo=”
Hmm … might that be base 64-encoded data?
It is indeed. It decodes to “NONE:”. By itself, that doesn’t give us much context, but it is hardly what I would expect in response to a DNS query asking for the address of a website.
The majority of the DNS packets contain similarly encoded responses, so the next step is to decode them all and see what is going on. I was not in the mood to decode over 500 packets by hand, so it’s time to do a little magic.
Our first approach was to use Wireshark to export the DNS records to a text file: File > Export Packet Dissections > as “Plain Text” file…
The resulting export includes over 500 entries that look a bit like this; the bit of data we are interested in is at the bottom, after the field title "TXT:"
Windows provides some useful built-in command line functions. The following command line parses the export file, extracting only the lines containing “TXT: ” then splitting those lines using a colon or space as a delimiter, and finally writing the second split field to a separate file. The result is that the value of the “TXT” field in every DNS answer record (i.e. the base64 encoded data I am interested in) is extracted to a separate file.
for /f "tokens=2 delims=: " %i in ('findstr /c:"TXT: " dns-export.txt') do echo %i >> strings.txt
The result looks a bit like this:
Tk9ORTo=
RklMRTovcm9vdC9QaWN0dXJlcy9zbmFwc2hvdF9DVVJSRU5ULmpwZwo=
RklMRTpTVEFSVF9TVEFURSxOQU1FPS9yb290L1BpY3R1cmVzL3NuYXBzaG90X0NVUlJFTlQuanBn
RklMRTpTVE9QX1NUQVRF
Tk9ORTo=
RklMRTovcm9vdC9QaWN0dXJlcy9zbmFwc2hvdF9DVVJSRU5ULmpwZwo=
RklMRTpTVEFSVF9TVEFURSxOQU1FPS9yb290L1BpY3R1cmVzL3NuYXBzaG90X0NVUlJFTlQuanBn
RklMRTpTVE9QX1NUQVRF
Tk9ORTo=
Which when piped through a base64 decoder yields:
NONE:
FILE:/root/Pictures/snapshot_CURRENT.jpg
FILE:START_STATE,NAME=/root/Pictures/snapshot_CURRENT.jpg
FILE:STOP_STATE
NONE:
FILE:/root/Pictures/snapshot_CURRENT.jpg
FILE:START_STATE,NAME=/root/Pictures/snapshot_CURRENT.jpg
FILE:STOP_STATE
NONE:
However, something is missing here - there’s nothing between the file start, and the file stop commands. A closer look at the packet capture shows why: in between those two packets are a large number of DNS queries containing a slightly different data record:
Note that the data field is labeled “TXT [truncated]:” instead of “TXT:”
Knowing that though, now we can tweak the for loop a bit to extract all the truncated pieces:
for /f "tokens=3 delims=: " %i in ('findstr /c:"TXT [truncated]: " dns-export.txt') do echo %i >> strings.txt
One small gotcha: the last record in this sequence is not truncated. The data being sent to the DNS server was cut into smaller chunks that would fit in the size limit for a DNS record; the last piece of data did not need to be further divided, so is not truncated. It was easy enough to manually extract the final bit of base64 data and add it to the file though. The next step was to base64 decode the entire file and then attempt to open it in an image viewer.
And … nothing. The decoded data did not make up a usable JPG image. A quick binary analysis showed that the file did in fact contain markers (in the forensics world, called "magic numbers") denoting a JPG image file, but also contained repeated instances of the text string “FILE:”. Encoded at the beginning of each DNS record's TXT field was the word “FILE:” followed by a piece of the actual file. Using a hex editor, it was easy enough to remove every instance of this string, but the file still would not open.
Tim Medin's avatar in the quest mentions a blog post by Josh Wright that hints at out-of-order packets. I tried having Wireshark sort the DNS packets based on the time stamp in each record, then exported the data and repeated the above exercise, but that still did not work. My suspicion is that Wireshark applies a sort operation to the records as it displays them, but keeps the original data for exports.
This was getting rather convoluted, so I changed tactics. I had never used scapy before, but a hint within the quest suggested scapy would make this task much easier. And it did: after installing scapy on my Linux system, it took only a few lines of Python code to extract the data, decode it, remove the extraneous FILE: strings, and generate what turned out to be a proper image:
import base64
from scapy.all import rdpcap
p=rdpcap("giyh-capture.pcap")
o=sorted(p, key=lambda ts: ts.time)
y= ""
for num in range(876, 1403):
if hasattr(o[num], "dst") and o[num].dst=="52.2.229.189" and hasattr(o[num], "an"): y += base64.b64decode(o[num].an.rdata[1:])[5:]
jpg=open("snapshot_CURRENT.jpg",'wb')
jpg.write(y)
jpg.close()
from scapy.all import rdpcap
p=rdpcap("giyh-capture.pcap")
o=sorted(p, key=lambda ts: ts.time)
y= ""
for num in range(876, 1403):
if hasattr(o[num], "dst") and o[num].dst=="52.2.229.189" and hasattr(o[num], "an"): y += base64.b64decode(o[num].an.rdata[1:])[5:]
jpg=open("snapshot_CURRENT.jpg",'wb')
jpg.write(y)
jpg.close()
A few notes:
- Josh Wright's blog post describes a very simple way to sort a packet capture file by time, thereby ordering the packets in the same order they were sent.
- By looking at the packets in Wireshark, I see that the data I am interested in is packets 877 to 1403, where the packet is a DNS query, and the destination is 52.2.229.189. Since scapy follows common coding practice of numbers starting at 0 instead of at 1, and since its range syntax will include everything from the first number in the range, to *before* the last number in the range, I changed the offset range to be 876 to 1403.
- The "rdata" field has the field length encoded in the first position, so I use an offset of 1 to skip the first byte (which is byte 0, per the above comment), then base64 decode it.
- The decoded data begins with "FILE:" in each record, so I grab everything from offset 5 to end (again, "FILE:" occupies offsets 0 through 4 of each line), and append it all into one big byte-string, which I then write out to a file.
The result? A picture of a child's bedroom, with the text "GnomeNET-NorthAmerica" printed at the bottom.
- Which commands are sent across the Gnome’s command-and-control channel?Base64-encoded as TXT records in DNS communication are the following commands:
NONE:EXEC:iwconfigEXEC:cat /tmp/iwlistscan.txtFILE:/root/Pictures/snapshot_CURRENT.jpg - What image appears in the photo the Gnome sent across the channel from the Dosis home?
The gnome sent a photo of a child's bedroom, with the text "GnomeNET-NorthAmerica" printed at the bottom.
Update January 8:
I used scapy and Python to solve this challenge. A colleague took a different approach that is elegant in its simplicity:
tshark -n -r c:/pcap/giyh-capture.pcap -T fields -e dns.txt -E header=n -Y dns.flags.response==1 > dns_txt.txt
-n Don’t do name resolution
-r c:/pcap/giyh-capture.pcap Read this trace file
-T fields Output decoded fields
-e dns.txt Include this field
-E header=n Don’t include the field header
-Y dns.flags.response==1 Apply this display filter
> dns_txt.txt Pipe the output to a text file
-n Don’t do name resolution
-r c:/pcap/giyh-capture.pcap Read this trace file
-T fields Output decoded fields
-e dns.txt Include this field
-E header=n Don’t include the field header
-Y dns.flags.response==1 Apply this display filter
> dns_txt.txt Pipe the output to a text file
This quickly and easily extracts the TXT records from all DNS response packets using tshark, a command-line utility included with Wireshark. Having the TXT records, it is a simple matter to base64-decode them all, remote the "FILE:" text from within the truncated records, and export the image file.