Chris Long

10 minute read

Question 51 (325)

Microsoft cloud services often have a delay or lag between “index time” and “event creation time”. For the entire day, what is the max lag, in minutes, for the sourcetype: ms:aad:signin? Answer guidance: Round to the nearest minute without the unit of measure.

This is an excellent question! Let’s start putting together a query to show the time and indextime.

You can’t just table the “_indextime” field, so we’ll use eval to create a field:

index=botsv3 earliest=0 sourcetype=ms:aad:signin  
| eval indextime=strftime(_indextime,"%Y-%m-%d %H:%M:%S") 
| table _time, indextime

Now we’ll create two new fields by converting these fields to epoch time:

index=botsv3 earliest=0 sourcetype=ms:aad:signin  
| eval indextime=strftime(_indextime,"%Y-%m-%d %H:%M:%S") 
| eval time=strftime(_time,"%Y-%m-%d %H:%M:%S") 
| eval indextime_epoch=strptime(indextime,"%Y-%m-%d %H:%M:%S")
| eval time_epoch=strptime(time, "%Y-%m-%d %H:%M:%S")
| table time, indextime, indextime_epoch, time_epoch

Now let’s calculate the delta. Indextime is always going to be after the event, so we substract from indextime:

index=botsv3 earliest=0 sourcetype=ms:aad:signin  
| eval indextime=strftime(_indextime,"%Y-%m-%d %H:%M:%S") 
| eval time=strftime(_time,"%Y-%m-%d %H:%M:%S") 
| eval indextime_epoch=strptime(indextime,"%Y-%m-%d %H:%M:%S")
| eval time_epoch=strptime(time, "%Y-%m-%d %H:%M:%S")
| table time, indextime, indextime_epoch, time_epoch
| eval delta=indextime_epoch-time_epoch

Now we’re looking for “the mag lag in minutes”, so I think we can just use the Splunk max() function:

index=botsv3 earliest=0 sourcetype=ms:aad:signin  
| eval indextime=strftime(_indextime,"%Y-%m-%d %H:%M:%S") 
| eval time=strftime(_time,"%Y-%m-%d %H:%M:%S") 
| eval indextime_epoch=strptime(indextime,"%Y-%m-%d %H:%M:%S")
| eval time_epoch=strptime(time, "%Y-%m-%d %H:%M:%S")
| table time, indextime, indextime_epoch, time_epoch
| eval delta=indextime_epoch-time_epoch
| stats max(delta)

We need to convert that to minutes, so we’ll divide by 60:

index=botsv3 earliest=0 sourcetype=ms:aad:signin  
| eval indextime=strftime(_indextime,"%Y-%m-%d %H:%M:%S") 
| eval time=strftime(_time,"%Y-%m-%d %H:%M:%S") 
| eval indextime_epoch=strptime(indextime,"%Y-%m-%d %H:%M:%S")
| eval time_epoch=strptime(time, "%Y-%m-%d %H:%M:%S")
| table time, indextime, indextime_epoch, time_epoch
| eval delta=indextime_epoch-time_epoch
| stats max(delta) as max_lag
| eval minutes=max_lag / 60

There’s your answer, but don’t forget to round!

Question 51 answer:
51

Question 52 (326)

According to Mallory’s advertising research, how is beer meant to be enjoyed? Answer guidance: One word.

I’m just going to search around for a bunch of strings related to this question like “enjoyed” or “meant to be” and see what shakes out.

As I’m searching for things like “research”, I notice there’s a sharepoint/onedrive folder called Documents/Frothly-Shared/Market Research, so I make a note that it might be relevant later.

I make note of search terms for Mallory as well:

  • mkraeusen
  • Mallory Kraeusen

However, hunting around for terms like “research”, “cold”, “warm”, “hot”, etc aren’t turning up anything of use.

We see a bunch of files in that Market Research folder, but we don’t necessarily have the ability to read the contents of those files, so where are we going to view Mallory’s research?

I decide to look for common document extensions:

index=botsv3 earliest=0 mkrae* AND ( *.doc* OR *.ppt* OR *.xls*)

“Frothly_GABF_Deck-2018-MK.pptx” turns up quite a bit, but how can we view the contents? Maybe it got mailed and we can pull it out of the content body?

While searching through the code42 backups, I find something interesting:

index=botsv3 earliest=0 Frothly_GABF_Deck-2018-MK.pptx sourcetype="code42:security"

This ba_advertising_code_overview.pdf has the word “advertising” in it! I search Google for this filename and the first result contains the answer in a PDF.

Question 52 answer:
responsibly

Question 53 (328)

What text is displayed on line 2 of the file used to escalate tomcat8’s permissions to root? Answer guidance: Provide contents of the entire line.

Let’s dig into tomcat8’s activity. I filter out all the lsof, top, and ps data because it’s just noise. We’re left with osquery:results from a few different queries. We get a pretty good idea what’s happening from the process_events:

index=botsv3 earliest=0 index=botsv3 earliest=0 tomcat8 sourcetype!=ps sourcetype!=top sourcetype!=lsof name!=largest_process 
| table _time,name,columns.cmdline, columns.command, columns.uid, columns.username, columns.target_path
| sort + _time

There’s a lot going on here. It’s clear someone is trying to compile and run an exploit (or a few of them). If you’re looking at the columns.uid field, you’ll notice the UID suddently change from 111 (tomcat8) to 0 (root). We can probably assume the privilege escalation was successful around that time, so we just need to work backwards from there.

At 2018-08-20 04:48:38, we see "rm" "/usr/share/tomcat8/.bash_history". It’s probably safe to say the escalation happened before that and the attacker is attempting to cover their tracks. Luckily osquery has been recording their commands!

It looks like colonelnew may have been the utility that got them root access, and chasing back the origin of this file seems to be

/tmp/colonel –base64 decode–> /tmp/colonel.c –compile–> /tmp/colonelnew —run–> root

Therefore, searching for the earliest references to /tmp/colonel.c should get you the answer. I found it in the WinEventLog:Security.

Base64 decode the content that is being echo’ed into the file and grab the contents of the second line!

Question 53 answer:
* Ubuntu 16.04.4 kernel priv esc

Question 54 (329)

One of the files uploaded by Taedonggang contains a word that is a much larger in font size than any other in the file. What is that word?

We know from question 47 that Taedonggang used “hyunki1984@naver.com” as an email address when emailing Grace Hoppy. But here, we’re looking for a file upload. It’s not clear if “upload” here includes email attachments.

I come across the email attachment titled 1534778082419.png and extract the PNG by converting the base64 content, but the font in the image all appears to be the same size. I decide to write a regex for strings containing font-size in case this is something inside of an email:

index=botsv3 earliest=0 font-size 
| rex field=_raw "font-size\:\s?(?<size>[\S]+)" 
| table size 
| sort - size

After reviewing those results, I feel like I’m not going down the right path here.

I start thinking about all the “upload” possibilities:

  • Email (maybe - “attach” would be a more appropriate verb)
  • OneDrive/Sharepoint
  • File upload via backdoor
  • Website file upload

No matter what the file upload is, we’ll have to be able to view the file contents to determine the font size. Generally, this has been achieved in the past by decoding base64 blobs. I decide to search across the entire index for events containing Base64 blobs at least 500 characters in length (excluding email and cloudtrail):

index=botsv3 earliest=0 NOT gawk NOT awk sourcetype!="aws:cloudtrail"  sourcetype!=stream:smtp
| regex _raw="[A-Za-z0-9+\/\=]{500}" 
| eval elength=len(_raw) 
| table elength, sourcetype, source, _raw 
| sort - elength

The first event is from a process on Fyodor’s host decoding a huge base64 blob into “/tmp/definitelydontinvestigatethisfile.sh”, and decoding the base64 leaves us with a binary file with the following written at the bottom:

My My! You are very curious person aren’t you. You are bad person. No one likes a nosey person.. except for us! If this is a BOTS at .conf18 person… congrats! Raise your hand and tell the proctors! You just won a very nice prize. If youare at a regular BOTS. SO SORRY.. Kind of> Tell your proctor and see if there is anything for you #blamebrodsky. If you are sad this photo is bad, sorry. Fyodor bad at photos. It is potato phone

Nice, I wonder what I would have won :-P

Running file on it shows that it’s actually a JPEG:

$ file b.file
b.file: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 300x176, components 3

I notice that the sign is larger than the text on peoples’ shirts, so I yolo try that as an answer, and guess what – it’s the answer!!

Question 54 answer:
Splunk

Question 55 (330)

What Frothly VPN user generated the most traffic? Answer guidance: Provide the VPN user name.

I start with index=botsv3 earliest=0 vpn and come across a stream:http event with a useragent of Cisco AnyConnect VPN Agent for Windows 4.6.01098, so I think this is probably useful information to pivot off of.

I eventually find the cisco:asa sourcetype and notice two separate kinds of events:

One of them is about connection creation and includes a username: Aug 20 15:17:26 FROTHLY-FW1 %ASA-6-302014: Teardown TCP connection 823949 for outside:192.168.8.117/53432 to inside:192.168.9.31/443 duration 0:00:07 bytes 21436 TCP Reset-I (mkraeusen)

The other is about connection teardown and includes the bytes from the connection. HOWEVER, the username doesn’t appear to get extracted properly in these events. Aug 20 15:17:26 FROTHLY-FW1 %ASA-6-302014: Teardown TCP connection 823949 for outside:192.168.8.117/53432 to inside:192.168.9.31/443 duration 0:00:07 bytes 21436 TCP Reset-I (mkraeusen)

We can solve this one of two ways - use a transaction to link the events together or use a regex to extract the username from the teardown events.

I’m going to try using a regex first, then if that fails, use a transaction.

First, I isolate the teardown events: index=botsv3 earliest=0 sourcetype="cisco:asa" action=teardown

As it turns out, not every teardown event has an associated user. The usernames are always surrounded by parantheses (e.g. (bstoll)), so I decide to include that as well:

index=botsv3 earliest=0 sourcetype="cisco:asa" action=teardown "\("

To isolate the bytes and usernames, I write the regex:

index=botsv3 earliest=0  sourcetype="cisco:asa" action=teardown "\(" 
| rex field=_raw "\((?<username>[^\)]+)" 
| table bytes, username

Now let’s try summing the bytes by user:

index=botsv3 earliest=0  sourcetype="cisco:asa" action=teardown "\(" 
| rex field=_raw "\((?<username>[^\)]+)" 
| table bytes, username 
| stats sum(bytes) as sum_bytes by username 
| sort - sum_bytes

This gets us our answer!

Question 55 answer:
mkraeusen

Question 56 (331)

Using Splunk commands only, what is the upper fence (UF) value of the interquartile range (IQR) of the count of event code 4688 by Windows hosts over the entire day? Use a 1.5 multiplier. Answer guidance: UF = Q3 + 1.5 x IQR

Ok, yeah:

I’m going to have to do some Googling to understand what these terms mean and how to calculate them using Splunk.

Howeer, let’s start by isolating the events we care about:

index=botsv3 earliest=0 EventCode=4688 | stats count by host

I come up with 7,427 events.

I do some reading and learn a bit about quartiles. This diagram helped me visualize it:

(Image from https://www.hackmath.net/en/calculator/quartile-q1-q3)

Essentually, “upper fence” is synonymous wiith “Q3”.

I use the perc25() and perc75() functions to calculate the Q1 and Q3 quartiles:

index=botsv3 earliest=0 EventCode=4688 
| stats count by host 
| eventstats perc75(count) as p75 perc25(count) as p25

Then IQR is just p75-p25:

index=botsv3 earliest=0 EventCode=4688 
| stats count by host 
| eventstats perc75(count) as p75 perc25(count) as p25 
| eval IQR=p75-p25

Lastly, the question tells us to use a 1.5 multiplier. We know from reading that the upper fence is Q3, so we can calculate accordingly:

index=botsv3 earliest=0 EventCode=4688 
| stats count by host 
| eventstats perc75(count) as p75 perc25(count) as p25 
| eval IQR=p75-p25 
| eval upperFence=(p75+IQR*1.5)

This gets us to the answer.

Question 56 answer:
1368

Question 57 (332)

What is the CVE of the vulnerability that escalated permissions on Linux host hoth? Answer guidance: Submit in normal CVE format. (Example: cve-2018-9805)

ExploitDB is nice enough to add CVEs for us. We had the entire content of the exploit in question 53, so by searching Google for some of the code, we’ll get pointed to this entry on exploit-db: https://www.exploit-db.com/exploits/44298

You’ll see a CVE field right at the top of the page - there’s your answer!

Question 57 answer:
cve-2017-16995

Question 58 (333)

Another throwback to question 53. By re-using this query, we can see the beginning of the malicious behavior:

index=botsv3 earliest=0 index=botsv3 earliest=0 tomcat8 sourcetype!=ps sourcetype!=top sourcetype!=lsof name!=largest_process 
| table _time,name,columns.cmdline, columns.command, columns.uid, columns.username, columns.target_path, hostIdentifier
| sort + _time

Seemingly out of nowhere, the tomcat8 user was doing recon by running whoami, cat /etc/passwd, etc. We can narrow the timeframe to be anything before Mon Aug 20 11:06:07 2018 UTC.

index=botsv3 earliest=0 index=botsv3 earliest=0 sourcetype=osquery:results hostIdentifier=hoth
| table _time,name,columns.cmdline, columns.command, columns.uid, columns.username, columns.target_path, hostIdentifier
| sort + _time

Yikes! 26,783 events. I’m not sorting through that. Let’s see what commands were run on hoth before the privilege escalation. We know the adversary didn’t have root yet, so let’s filter out events from uid=0:

index=botsv3 sourcetype=osquery:results hostIdentifier=hoth columns.uid!=0
| table _time,name,columns.cmdline, columns.command, columns.uid, columns.username, columns.target_path, hostIdentifier
| sort + _time

We can see the attacker running those recon commands (e.g. whoami), so let’s see if any of the hoth logs help us understand how that’s happening:

index=botsv3 host=hoth whoami earliest=1534762800 latest=1534763160

One single event, and it’s happening over HTTP!

I google saveGangster.action found in the uri_path and am led to this website

Question 58 answer:
cve-2017-9791
comments powered by Disqus