Poisoned Weather Data

Zeek no more!

Instructions in your badge:

Use the data supplied in the Zeek JSON logs to identify the IP addresses of attackers poisoning Santa’s flight mapping software. Block the 100 offending sources of information to guide Santa’s sleigh through the attack. Submit the Route ID (“RID”) success value that you’re given. For hints on achieving this objective, please visit the Sleigh Shop and talk with Wunorse Openslae.

Links from hint:

Once you enter the Sleigh Shop Door, you will be greeted with these bunch:

Sleigh Shop

The Tooth Fairy greets you with the following:

I’m the Tooth Fairy, the mastermind behind the plot to destroy the holiday season. I hate how Santa is so beloved, but only works one day per year! He has all of the resources of the North Pole and the elves to help him too. I run a solo operation, toiling year-round collecting deciduous bicuspids and more from children. But I get nowhere near the gratitude that Santa gets. He needs to share his holiday resources with the rest of us! But, although you found me, you haven’t foiled my plot! Santa’s sleigh will NOT be able to find its way. I will get my revenge and respect! I want my own holiday, National Tooth Fairy Day, to be the most popular holiday on the calendar!!!

Not a very good sign, but all is not lost yet. You should turn to Wunorse Openslae for some hints on defeating the Tooth Fairy, however he has a technical task for you before that:

Wunorse Openslae here, just looking at some Zeek logs. I’m pretty sure one of these connections is a malicious C2 channel… Do you think you could take a look? I hear a lot of C2 channels have very long connection times. Please use jq to find the longest connection in this data set. We have to kick out any and all grinchy activity!

Next, you open the terminal and get to work:

Some JSON files can get quite busy.
There's lots to see and do.
Does C&C lurk in our data?
JQ's the tool for you!
-Wunorse Openslae
Identify the destination IP address with the longest connection duration
using the supplied Zeek logfile. Run runtoanswer to submit your answer.
elf@3222ffd89de4:~$ ls
conn.log
elf@3222ffd89de4:~$ cat conn.log | wc -l
143679
elf@3222ffd89de4:~$

As you can see, it is a rather large log file, so you should use JQ to parse it. Since you are interested in the IP that belongs to the connection with longest duration, you should extract that field, then pipe the output through some unix tools that can help you find the highest value. Next you should run JQ once more to find the IP that belongs to this highest duration:

elf@3222ffd89de4:~$ cat conn.log | jq ".duration" | uniq | sort -g | tail -n 1
1019365.337758
elf@3222ffd89de4:~$ cat conn.log | jq ". | select (.duration == 1019365.337758)"
{
  "ts": "2019-04-18T21:27:45.402479Z",
  "uid": "CmYAZn10sInxVD5WWd",
  "id.orig_h": "192.168.52.132",
  "id.orig_p": 8,
  "id.resp_h": "13.107.21.200",
  "id.resp_p": 0,
  "proto": "icmp",
  "duration": 1019365.337758,
  "orig_bytes": 30781920,
  "resp_bytes": 30382240,
  "conn_state": "OTH",
  "missed_bytes": 0,
  "orig_pkts": 961935,
  "orig_ip_bytes": 57716100,
  "resp_pkts": 949445,
  "resp_ip_bytes": 56966700
}

To submit the answer execute the runtoanswer command on the terminal:

elf@3222ffd89de4:~$ runtoanswer
Loading, please wait......
What is the destination IP address with the longes connection duration? 13.107.21.200
  Thank you for your analysis, you are spot-on.
  I would have been working on that until the early dawn.
  Now that you know the features of jq,
  You'll be able to answer other challenges too.
-Wunorse Openslae
Congratulations!

Fixing the weather

So now you can attack the final objective and you also get some encouraging words from the Krampus in the room:

But there’s still time! Solve the final challenge in your badge by blocking the bad IPs at srf.elfu.org and save the holiday season!

Go to link: SRF

However, you notice that the website needs credentials before you can access it. Luckily, you remember the Elfscrow objective and the pdf document you successfully recovered, in which there was some good clues for how this can be achieved:

SRF document

Link to the readme file: here and the credentials inside:

#### Logging in:
You can login using the default admin pass:
'admin 924158F9522B3744F5FCD4D10FAC4356'

After logging in, you can scroll down to see the Firewall section, which is where you will need to enter the IP addresses which you think are malicious, based on your analysis.

SRF document

Weed out the bad IPs!

In order to find the list of IP addresses (around 100 or so in total, as pointed out in the objective) you need to download the Zeek logs from here, and run your analysis on them. At this point it is worth to go back to Wunorse to see hear his hints for the analysis task:

That’s got to be the one - thanks! Hey, you know what? We’ve got a crisis here. You see, Santa’s flight route is planned by a complex set of machine learning algorithms which use available weather data. All the weather stations are reporting severe weather to Santa’s Sleigh. I think someone might be forging intentionally false weather data. I’m so flummoxed I can’t even remember how to login! Hmm… Maybe the Zeek http.log could help us. I worry about LFI, XSS, and SQLi in the Zeek log - oh my! And I’d be shocked if there weren’t some shell stuff in there too. I’ll bet if you pick through, you can find some naughty data from naughty hosts and block it in the firewall. If you find a log entry that definitely looks bad, try pivoting off other unusual attributes in that entry to find more bad IPs. The sleigh’s machine learning device (SRF) needs most of the malicious IPs blocked in order to calculate a good route. Try not to block many legitimate weather station IPs as that could also cause route calculation failure. Remember, when looking at JSON data, jq is the tool for you!

He provides some very useful hints about LFI, XSS, SQLi and Shell exploits. In order to try to uncover the IP addresses that originate such attacks, I wrote a custom python script, which parsed the Zeek logs looking for signs of such exploits. More specifically for each category of exploits I looked for:

SQLi  > presence of ' in uri, username or user_agent fields

XSS   > presence of < in uri or host fields

LFI   > presence of /passw in uri field

Shell > presence of ':;' or '};' in user_agent field

As an example, if I did a quick and dirty search for LFI exploits via cat and JQ:

cat http.log | jq '.[] |  .uri' | grep /passw
"/api/weather?station_id=\"/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd"
"/api/weather?station_id=../../../../../../../../../../bin/cat /etc/passwd\\\\x00|"
"/api/stations?station_id=|cat /etc/passwd|"
"/api/weather?station_id=;cat /etc/passwd"
"/password/"
"/api/login?id=cat /etc/passwd||"
"/api/weather?station_id=`/etc/passwd`"
"/api/weather?station_id=/../../../../../../../../../../../etc/passwd"
"/gtcatalog/password.inc"
"/gtcatalog/password.inc"
"/api/login?id=/../../../../../../../../../etc/passwd"
"/password-manager-master/beta/index.html"
"/api/weather?station_id=/../../../../../../../../etc/passwd"
"/api/weather?station_id=/etc/passwd"
"/files/passwd.txt"
"/scripts/files/passwd.txt"
"/guestbook/files/passwd.txt"
"/api/login?id=.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./etc/passwd"

Quite easily this search revealed some requests trying to gain access to the passwd file. Next, I took the specified search criteria and implemented them in a python script.

ips_blacklist = set()  # collection of IP addresses deemed to be malicious

# load the json into logs object
logs = json.load(open("http.log"))

# iterate over entries and filter based on identified markers of IoC
for log in logs:
	if ("'" in log['uri'] or "'" in log['username'] or
		"'" in log['user_agent'] or "<" in log['uri'] or
		"<" in log['host'] or "pass" in log['uri'] or
		":;" in log['uri'] or "};" in log['uri']):
		ips_blacklist.add(log['id.orig_h'])

# print IP blacklist for copy pasting into SRF FW
print(",".join(ips_blacklist))

This script loops through the Zeek logs and collects IP_address and user_agent values that we deem malicious. At the end it prints the collected IP addresses in a comma-separated manner in one line, which you can copy paste into the Firewall input field on the SRF website, then click Deny and see if you found enough.

After pasting in the script output and clicking DENY in the SRF firewall, the calculation still failed, most likely because only about 80 or so IP addresses were found via the above script. That seems short of the 100 which was mentioned in the objective. I turn to the hints from Wunorse again and notice this sentence:

If you find a log entry that definitely looks bad, try pivoting off other unusual attributes in that entry to find more bad IPs.

As a next step I extended the script and pivoted by looking for additional malicious IP addresses based on known bad user_agent strings. The idea was to loop through the log again, and see if the current entry’s user_agent matches any of the known malicious user_agent values in our ua_blacklist.

ips_blacklist = set()  # set of IPs found to be malicious
ua_blacklist  = list() # list so that later on we can count items that are added multiple times

# ... code removed for brevity

# collect new malicious agents that match existing ones but not in ips_blacklist
for log in logs:
	if log['user_agent'] in ua_blacklist and log['id.orig_h'] not in ips_blacklist:
    ips_blacklist.add(log['id.orig_h'])
		ua_blacklist.append(log['user_agent'])

This however provided way too many IP addresses, more than 200, so it needs to be reduced somehow. For this I decided to create a third list for whitelisting user_agents strings that are found often enough to signal that it may be benign. After fiddling around with the right threshold, I came to the conclusion that if the same user_agent string is found more than 9 times in the ua_blacklist, then it could be safely added to a whitelist.

The full python script can be found below and also in this Github repo. It prints out 110 IP addresses, which is more or less close to 100, and more importantly is an accepted solution on the SRF Firewall.

import json

ips_blacklist = set()  # set of IPs found to be malicious
ua_blacklist  = list() # list so that later on we can count items that are added multiple times
ua_whitelist  = set()  # set of user_agents that are found to be benign

# load the json into logs object
logs = json.load(open("http.log"))

# iterate over entries and filter based on identified markers of IoC
for log in logs:
	if ("'" in log['uri'] or "'" in log['username'] or
		  "'" in log['user_agent'] or "<" in log['uri'] or
		  "<" in log['host'] or "pass" in log['uri'] or
		  ":;" in log['uri'] or "};" in log['uri']):
		ips_blacklist.add(log['id.orig_h'])
		ua_blacklist.append(log['user_agent'])

# collect new malicious agents that match existing ones but not in ips_blacklist
for log in logs:
	if log['user_agent'] in ua_blacklist and log['id.orig_h'] not in ips_blacklist:
		ua_blacklist.append(log['user_agent'])

# identifiy agents that are found more than 9 times > those should be benign and can be whitelisted
ua_blacklist_counts = { x : ua_blacklist.count(x) for x in ua_blacklist }
for i in ua_blacklist_counts:
	if ua_blacklist_counts[i] >= 9:
		ua_whitelist.add(i)

# identify additional IPs that are in ua_blacklist and not in ua_whitelist
for log in logs:
	if log['user_agent'] in ua_blacklist and log['user_agent'] not in ua_whitelist and log['id.orig_h'] not in ips_blacklist :
		ips_blacklist.add(log['id.orig_h'])

# print out comma separated string for pastin into srf.elfu.org firewall for DENY
print(",".join(ips_blacklist))

Output list of IP addresses that are most likely poisoning the weather API:

229.133.163.235,132.45.187.177,65.153.114.120,22.34.153.164,187.152.203.243,231.179.108.238,220.132.33.81,52.39.201.107,87.195.80.126,118.26.57.38,194.143.151.224,111.81.145.191,42.103.246.250,150.45.133.97,1.185.21.112,79.198.89.109,45.239.232.245,249.90.116.138,250.22.86.40,169.242.54.5,253.65.40.39,34.129.179.28,66.116.147.181,121.7.186.163,44.164.136.41,150.50.77.238,106.93.213.219,81.14.204.154,2.240.116.254,50.154.111.0,92.213.148.0,0.216.249.31,29.0.183.220,53.160.218.44,254.140.181.172,140.60.154.239,102.143.16.184,13.39.153.254,83.0.8.119,34.155.174.167,118.196.230.170,135.203.243.43,49.161.8.58,25.80.197.172,126.102.12.53,2.230.60.70,69.221.145.150,131.186.145.73,84.185.44.166,238.143.78.114,168.66.108.62,27.88.56.114,19.235.69.221,42.127.244.30,37.216.249.50,97.220.93.190,211.229.3.254,80.244.147.207,193.228.194.36,226.102.56.13,33.132.98.193,227.110.45.126,61.110.82.125,230.246.50.221,28.169.41.122,158.171.84.209,75.73.228.192,203.68.29.5,226.240.188.154,249.237.77.152,173.37.160.150,180.57.20.247,42.103.246.130,103.235.93.133,68.115.251.76,9.206.212.33,75.215.214.65,186.28.46.179,187.178.169.123,142.128.135.10,42.191.112.181,148.146.134.52,84.147.231.129,95.166.116.45,123.127.233.97,31.116.232.143,229.229.189.246,44.74.106.131,135.32.99.116,217.132.156.225,42.16.149.112,223.149.180.133,252.122.243.212,249.34.9.16,185.19.7.133,116.116.98.205,250.51.219.47,106.132.195.153,10.155.246.29,56.5.47.137,104.179.109.113,23.49.177.78,48.66.193.176,225.191.220.138,10.122.158.57,253.182.102.55,200.75.228.240,190.245.228.38,233.74.78.199,129.121.121.48

Once you submit this string and hit Deny, the calculator will start running and provide you with the RID that you need to submit for solving the final Objective.

SRF solved

RID: 0807198508261964

The Bell Tower

Once you submit the RID through your personal badge, you get access to The Bell Tower through the door that just opened in the Sleigh Shop. Go up and talk to the fellas for finishing this once and for all!

SRF solved

Santa seems quite grateful and happy:

You did it! Thank you! You uncovered the sinister plot to destroy the holiday season! Through your diligent efforts, we’ve brought the Tooth Fairy to justice and saved the holidays! Ho Ho Ho! The more I laugh, the more I fill with glee. And the more the glee, The more I’m a merrier me! Merry Christmas and Happy Holidays.

Next you get some good news from Krampus:

Congratulations on a job well done! Oh, by the way, I won the Frido Sleigh contest. I got 31.8% of the prizes, though I’ll have to figure that out.

Let’s hope he will share with others, his lifetime supply of cookiez… :) However, quite understandably, The Tooth Fairy is not as jolly as the Krampus:

You foiled my dastardly plan! I’m ruined! And I would have gotten away with it too, if it weren’t for you meddling kids!

Whats more, there is a suspicious note in the corner which seems to suggest we probably cannot rest for too long, before the villains return and try to ruin the holiday once again…

SRF solved

Previous