Having Fun with the SANS DFIR 2015 Network Forensics Challenge

In a brief pause between my Mongo + Elasticsearch + MHN post series, I wanted to finally publish a post I’ve had in limbo for a bit (mainly due to contest rules). Recently, SANS offered their DFIR Monterey 2015 Network Forensics Challenge, with a really sick prize of an OnDemand course. These courses aren’t cheap, and this was a fun challenge.

The challenge consisted of 6 questions that increased in difficulty. SANS provided the PCAPs and log files for analysis, and specific questions for each.

Before continuing, I’d encourage you go check out other write ups by Eric Gershman, and da_667, who offer some great insight into their analysis methods. Eerily, we approached some of these the same way. Others I approached a bit differently, and wanted to share that here with other forensicators.

Ok, to begin!

Question 1: At what time (UTC, including year) did the portscanning activity from IP address 123.150.207.231 start?

  • Difficulty: Easy
  • Evidence: SWT-syslog_messages
  • Evidence Type: syslog (text file)

My first step for this question was to get an idea of when activity first shows up for that IP, and work backwards from there. The command line is very useful here:

:~$ grep 123\.150\.207\.231 SWT-syslog_messages | head -5

Our result:

Ok, look’s like Aug 29 09:58:55 is a good time to start with. Now, the question also asks for the time, in UTC, and including the year. Obviously, our line entry doesn't contain this information, so we have to dig a bit deeper. In situations such as this, I like to throw a couple shots in the dark and see what comes back; it's fast, easy, and I can always go to the file metadata if it doesn't work. But for syslogs, there's one line particular I want to see. Check it out:

:~$ grep rtc_cmos SWT-syslog_messages

Our result:

We can see that the system clock (CMOS device rtc0) was set to 11:07 UTC on Aug 29, 2013, when our log read Aug 29, 07:07. This puts us in the year 2013, with a clock that is four hours behind UTC. Daylight Savings is in effect during this time, so I believe we’re operating in EST5EDT; EDT giving us UTC-4:00.

Question 2: What IP addresses were used by the system claiming the MAC Address 00:1f:f3:5a:77:9b?

  • Difficulty: Easy
  • Evidence: nitroba.pcap
  • Evidence Type: PCAP

This is where, for me, the challenge starts to move into Wireshark. We’ll be back in the command line shortly, but I liked using Wireshark to visualize pcaps.

Once the pcap is loaded, we can use a simple Wireshark filter to look for traffic where our MAC Address of interest is the source:

eth.src == 00:1f:f3:5a:77:9b

This returns 187 out of 95000+ packets, so I’ll start with a small subset to work with. What I’m looking for is areas of IP address identification or broadcasting. Typically these look like ‘xxx.xxx.xxx.xxx is at xx:xx:xx:xx:xx:xx'. The top and bottom frames in this screenshot are actually right on the money, and give us our first two IP addresses:

So far we have 169.254.90.183 and 192.168.1.64. But hey, it looks like Wireshark detected the duplicate use as well! That's a fun little signal to keep in our back pocket for quick browsing. Now, before we dust off our hands, let's browse through the rest of our packets. It looks like further down, we have another IP of interest: 169.254.20.167. See below:

Examining the packet information gives us our MAC Address of interest, so I believe we now have a third IP address. Now, knowing what I know, let’s try a different, more mature Wireshark filter:

eth.addr == 00:1f:f3:5a:77:9b && arp.isgratuitous == true

Wow..7 packets. And all of interest. Including our duplicate IP warning!

Quick Note: The 169.254.0.0/16 range is actually related to link-local IPv4 addresses that are used when DHCP cannot be contacted. As this traffic appears to come from Apple devices, here’s a link to Apple’s explanation on the topic.

Question 3: What IP (source and destination) and TCP ports (source and destination) are used to transfer the “scenery-backgrounds-6.0.0–1.el6.noarch.rpm” file?

Full Disclosure: Because I’m a copy/paste fool (I hate entering data directly into Survey Monkey, I know I submitted this one wrong. But I wanted to display what I think was the correct answer :))

  • Difficulty: Medium
  • Evidence: ftp-example.pcap
  • Evidence Type: PCAP

Just judging by the pcap name alone, we’re going to be examining some FTP traffic in Wireshark. Knowing that we’re examining some FTP traffic, we can use a Wireshark filter to quickly hone down our file name and get our starting reference:

ftp contains "rpm"

Gives us:

It looks like four different RPMs were interacted with during this session. Our file of interest first appears, as you can see, in frame 5846. Immediately, we’ve got two IP addresses: 192.168.75.29 is our client, and 149.20.20.135 is our server.

Using this data as a reference point, I’m going to remove the filter and back up a few frames. Check out this screenshot:

It’s hard to describe how much evidence is in that one screenshot. So much goodness. Let’s break it down piece by piece:

  • In Frame 5840, we see the Request for PASV from our client
  • In Frame 5841, the server responds with (149,20,20,135,119,8)

Ok, pause. This is where a bit of FTP understanding comes into play. The issuing of the PASV command tells the server to wait for the client (192.168.75.29 in this case) to establish the data connection. The server, in response, says "OK, I'm waiting. By the way, here's what you'll need for that transmision: (149,20,20,135,119,8)".

Let’s break this little nugget down further. Now when I say break this down, I mean go all the way back to October 1985, and let’s take a look at the RFC for File Transfer Protocol. Here’s a link for you. And I quote:

DATA PORT (PORT)
The argument is a HOST-PORT specification for the data port to be used in data connection. There are defaults for both the user and server data ports, and under normal circumstances this command and its reply are not needed. If this command is used, the argument is the concatenation of a 32-bit internet host address and a 16-bit TCP port address. This address information is broken into 8-bit fields and the value of each field is transmitted as a decimal number (in character string representation). The fields are separated by commas.
A port command would be:
PORT h1,h2,h3,h4,p1,p2
where h1 is the high order 8 bits of the internet host address.

Well, would you look at that syntax! Looks oddly familiar…Ok, let’s attack this data point again:

  • h1-h4: 149,20,20,135 = 149.20.20.135.

Ok, check.

Well, these obviously aren’t our ports. Google time. I quickly found an interesting calculation post on stackoverflow, which briefly explains that to get the port calculation, perform p1 * 256 + p2. Looking back at the RFC, this is one of those "duh" moments - 8-bit fields and all. Duh.

Substituting in our numbers, this gives us 119*256+8 = 30472. With our calculations in hand, let's look at this a different way.

  • Client says “I want passive mode”
  • Server responds “Ok client, I’m entering passive mode. You can initiate your connection at IP address 149.20.20.135, using port 30472.”
  • Client then says “Ok, let’s get this going.”

Why doesn’t Wireshark show this for us? Well, it does. Just not as nicely as I’ve explained it to you :) Scroll back up and take a look at frame 5843. Any number in there look familiar? We’re seeing a connection from our client to our server, to port 30472, from port 51851. And there's the other half of PASV mode: The client will initiate from its own arbitrary port. Now there's a lot of theory behind why FTP was setup this way, but let's summarize it that with all of these arbitrary ports flying around, firewalls had a hell of a time back in the day :)

So in conclusion, our file is transferred from 149.20.20.135:30472 to 192.168.75.29:51851

This was a fun question!

Question 4: How many IP addresses attempted to connect to destination IP address 63.141.241.10 on the default SSH port?

  • Difficulty: Medium
  • Evidence: nfcapd.201405230000
  • Evidence Type: Netflow. A text file is also provided that is the data in “long” output format.

Seeing the corresponding text file, I was happy to get back to the command line. Here’s what we know so far:

  • Destination IP address: 63.141.241.10
  • Destination port: 22

Grep quickly came to our aid:

:~$ grep 63\.141\.241\.10\:22 nfcapd.201405230000.txt | head -5

Ick. Ok, likes like we have more than just port 22 on our host of interest. Furthermore, it looks like we have our IP, 63.141.241.10 in both the source and destination columns. We only care about the latter. Also, it looks as though the entries capture both IP and port. Time to refine our command line kung-fu a bit. This command might be ugly..but it works:

:~$ grep -w 63\.141\.241\.10\:22 IVS-netflow-2014-05-23/nfcapd.201405230000.txt | awk '{ print $5 }' | cut -d':' -f1 | sort -u

We’re achieving a few things with this command:

  • First, the -w flag filters only on exact matches. No more port 2222, 2202, etc.
  • Second, Just give me column 5 — the source IP (indirectly, this gives me 63.141.240.10 as the destination).
  • Third, separate the IP from the port with a cut statement.
  • Finally, sort and unique. Your numbers will be off if you don’t sort.

Nice! Now, in simplicity form, just throw a wc -l pipe at the end, and we've got a total of 49 IP addresses.

Question 5: What is the byte size for the file named “Researched Sub-Atomic Particles.xlsx

  • Difficulty: Hard
  • Evidence: stark-20120403-full-smb_smb2.pcap
  • Evidence Type: PCAP

Now this is the first question I attacked, and I got it fairly quickly. I don’t know if that was the intention or not, but Wireshark makes life easy. Again, it was a first shot in the dark — and it worked! Here’s how I approached:

Wireshark allows for exporting of objects of a certain type. Check out the Menu screenshot below:

Using the SMB/SMB2 option, Wireshark will parse through the PCAP and look for SMB/SMB2 objects. Once I run it, here’s the output I get:

Now, this is no short order. There’s a LOT of data in here. I just did a quick scroll through, looking for anything from the filename. Well, what do you know:

Using the beauty of Wireshark, we can actually export this file as well. Take a look:

We can just navigate to file properties, and find our byte size as well:

So, our file is 13,625 bytes in size. Now, if you look back at the Wireshark SMB/SMB2 screenshot above, you’ll see that we had the data 13625 along with the file name. Well, that was quick!

Now, I need to give another shoutout to da_667, and ask you to look at reaching this answer via another method. I feel that method is more “forensic-ish”, but I happened to get lucky. You can also use the Wireshark SMB object list to find our file at packet 110725, and then go there for some more research. There’s a lot of data in this packet:

Question 6: The traffic in this Snort IDS pcap log contains traffic that is suspected to be a malware beaconing. Identify the substring and offset for a common substring that would support a unique Indicator of Compromise for this activity. Bonus: Identifying the meaning of the bytes that precede the substring above.

  • Difficulty: Very Hard
  • Evidence: snort.log.1340504390.pcap
  • Evidence Type: PCAP

This question took a bit more digging, but it was another fun one. Loading up the PCAP, I first started to look for a pattern from packet to packet. Now, let’s dissect this pcap. Here’s a screenshot of frames 1–20:

Each line looks very, very similar. Based on what I’m seeing, it appears as though several machines from the 10.3.56|59 subnet are beaconing to 184.82.188.7. Let me filter on just one IP, to see if I can discern a pattern from one machine, and apply it to the population. Scale in, scale out. I'm going to pick on the first IP, and here's our filter:

ip.src == 10.3.59.24

Ok, this brings us down to 59 packets. A bit easier to manage. Now, let’s examine the data field, and see if we can’t start to discern a pattern. I’m going to paste screenshots of data from the first three packets in this filter. Let me know when you see it:

Three separate data fields, but something looks familiar: ULQENP2 is consistent across all of our fields. This is going to be hard to describe, but if you look at all 59 packets in this filter, and just scroll down, this frame of text appears to "stay static" while all other data around it changes. It's a weird way to describe something, but I think we've found a pattern. You can take the same method and expand it to the entire PCAP, and that particular string remains static.

To answer the question correctly, our substring of interest begins at offset 0x46, is 7 bytes in length, and ends at offset 0x4C. The hex variant of our string is 554C51454E5032.

Bonus: Let’s tackle the bonus part of the question. Now, as I read the question, we are curious to know what the bytes preceding our new IOC/substring are. Well, examnining the data portion again, it’s a total of four bytes that precede our substring, without fail. Packet #1, above, for example, is 4FE6C274. Whenever I see a four-byte string, I immediately think of timestamp. Let's see if we can't get lucky, once again. I'm using a quick Ruby turn here:

irb> require 'date' 
=> true
irb> Time.at('4FE6C274'.to_i(16)).to_datetime
=> #<DateTime: 2012-06-24T02:32:04-05:00 ((2456103j,27124s,0n),-18000s,2299161j)>

Well, what do you know. Does this match up with our packet information?

Yep!

So, to answer the bonus, it appears that the four bytes preceding the IOC substring are a timestamp of the beacon.

In Conclusion

A big thank you to SANS for putting on challenges like this. It’s not everyday forensicators run into challenges like this on a daily basis, but bringing so many questions, protocols, and methods of analysis together is a lot of fun, and helps keep everyone fresh.

I approached each question in a fun way, and would love to get some feedback from others as there’s never one way to approach a problem. I’m still not certain any of the answers are correct! I’ve also got the data set still, if anyone would like to go back after the fact and try the challenge themselves.


Originally published at www.505forensics.com on February 5, 2015.