Sunday, August 11, 2019

SANS HOLIDAY HACK 2018 Walkthrough (Part 3; Questions 7-9)

Overview
Welcome to Part III of the Sans Holiday Hack 2018 Walkthrough!  In this post, I'll go through questions 7 through 9 and their associated terminal challenges.  If you would like to see the first two parts, you can find Part I here and Part II here.

Let's get started!

QUESTION 7
"Santa uses an Elf Resources website to look for talented information security professionals. Gain access to the website and fetch the document `C:\candidate_evaluation.docx`. Which terrorist organization is secretly supported by the job applicant whose name begins with "K"? For hints on achieving this objective, please visit Sparkle Redberry and help her with the Dev Ops Fail Cranberry Pi terminal challenge."

Link to website to gain access to: https://careers.kringlecastle.com/

Optional - talk to Sparkle Redberry and complete her terminal challenge. Sparkle is on the left area of the second floor, by Toy Soldier 3 and SugarPlum Mary.

Talk to Sparkle to find out about her challenge:
Hi, I'm Sparkle Redberry!
Ugh, can you believe that Elf Resources is poking around? Something about sensitive info in my git repo.
I mean, I may have uploaded something sensitive earlier, but it's no big deal. I overwrote it!
Care to check my Cranberry Pi terminal and prove me right?
Click on the terminal to start Sparkle's challenge.

The goal is to find Sparkle's password. Run ls to find a directory called kcconfmgmt and the runtoanswer program to submit our final answer.

cd into kcconfmgmt. The .git directory here tells us that this is the git repository we want to dig around in.  Run "git log" and start browsing through the commit history.

Interesting commit history.


Looks like the password is likely in config.js.  The corresponding commit SHA-1 checksum is "60a2ffea7520ee980a5fc60177ff4d0633f2516b".  Look up the commit using the following command:
git show 60a2ffea7520ee980a5fc60177ff4d0633f2516b
Looking for the password in the commit.

"mongodb://sredberry:[email protected]:27017/node-api" gives us all we need - it looks like Sparkle's password is "twinkletwinkletwinkle".

Run runtoanswer from the home directory and submit "twinkletwinkletwinkle" to finish the challenge!


Talk to Sparkle again to receive hints for Question 7:
Oh my golly gracious - Tangle was right? It was still in there? How embarrassing!
Well, if I can try to redeem myself a bit, let me tell you about another challenge you can help us with.
I wonder if Tangle Coalbox has taken a good look at his own employee import system.
It takes CSV files as imports. That certainly can expedite a process, but there's danger to be had.
I'll bet, with the right malicious input, some naughty actor could exploit a vulnerability there.
I'm sure the danger can be mitigated. OWASP has guidance on what not to allow with such oploads.
Check out the website at https://careers.kringlecastle.com/, and we see that we can input some personal information and upload a CSV. If you try to input characters like ' for SQL or command injection, it'll give you warnings about invalid characters. So they could be sanitizing the input to a degree.

Input form with apparent input sanitization.

However, we still have the CSV upload capability, and we can certainly try injection commands through CSVs (Sparkle hints at this if you finish her terminal challenge). CSVs should be treated as plain text, meaning that whatever values appear in the fields shouldn't be interpreted as something special, like a command to execute.  However, many popular spreadsheet programs that interpret CSVs (e.g. Excel) will treat values as functions/commands if they start with the equal sign character, =.

OWASP's article on CSV injections can be found at https://www.owasp.org/index.php/CSV_Injection

Indeed, if I create the following csv file and open it with a spreadsheet program (in this case, LibreOffice Calc Version: 5.1.6.2), the value starting with the equal sign will be interpreted as a function.

CSV file to input.

Result when I open the file in Libre Calc.

The same thing happens with Excel.  Let's see what happens when I load the following CSV into Excel:



If our assumption is correct, Excel will interpret the second cell of the second row as a function rather than plain text.



Looks like we were right.  Assuming that someone in Kringle Castle will open our uploaded CSV with Excel or other application vulnerable to this method, we can inject a payload that will allow us to achieve arbitrary command injection. To do this, we will use the "=CMD" function.  Excel will interpret "=CMD|'<command>'!A0" by executing the inner command (after giving the user a bunch of warnings, of course).

Since the goal is to obtain the document "C:\candidate_evaluation.docx", we can do that by injecting a command to move the document to a publicly accessible folder. So let's first find a suitable directory.  Let's see what happens when we poke around at random URL paths.

Upon requesting https://careers.kringlecastle.com/test, I received the following message:
404 Error!
Publicly accessible file served from:
C:\careerportal\resources\public\ not found......
Try:
https://careers.kringlecastle.com/public/'file name you are looking for'
Luckily, we just got all the information we need!  We want our injected command to copy the file from the C:\ directory and into C:\careerportal\resources\public\.

Let's craft a payload and test it locally in Excel to make sure it works.  I set two directories "C:\sans" and "C:\sans\dest" on a Windows machine and placed the file "testing.txt" in "C:\sans". The goal is to move "C:\sans\testing.txt" to "C:\sans\dest".


Using the Windows "copy" command, our test payload will look like:
=CMD!' /C copy C:\sans\testfile.txt C:\sans\dest\testfile.txt'!A0
Our CSV file will look like:


Let's see what happens when we try it in Excel.  We get lots of warnings!





After pretending to be an unwary end user and accepting all the warnings, we find that our command succeeded, and "testfile.txt" was moved accordingly.



Let's apply this to the actual Holiday Hack challenge.  Using our starting and target file paths, our real payload will look like "=CMD|'/C copy C:\candidate_evaluation.docx C:\careerportal\resources\public\asdf3irh13f897cs.docx'!A0"

I used a random destination file name to avoid collision with other challengers, just in case, and "/C" will cause CMD.exe to terminate after carrying out our "copy" command.

Let's try submitting a CSV file with the following single line:
1,=CMD|'/C copy C:\candidate_evaluation.docx C:\careerportal\resources\public\asdf3irh13f897cs.docx'!A0,3
Upon successful submission, you'll get the following message:


After a few seconds, try requesting https://careers.kringlecastle.com/public/<your random file name>, and you should be able to download the file.

Open the file, and we will find that only one candidate's name starts with a K - Krampus. In the "Comments" section of his evaluation, we see the following:



So Krampus is linked to Fancy Beaver, a play on APT28's name "Fancy Bear".

Looks like "Fancy Beaver" is our answer to Question 7!


QUESTION 8
This objective was definitely one of my favorites.

"Santa has introduced a web-based packet capture and analysis tool to support the elves and their information security work. Using the system, access and decrypt HTTP/2 network activity. What is the name of the song described in the document sent from Holly Evergreen to Alabaster Snowball? For hints on achieving this objective, please visit SugarPlum Mary and help her with the Python Escape from LA Cranberry Pi terminal challenge."

Link to tool: https://packalyzer.kringlecastle.com/

Optional: talk to SugarPlum Mary and complete her terminal challenge.  SugarPlum is on the left area of the second floor, by Toy Soldier 3 and Sparkle Redberry.
Hi, I'm Sugarplum Mary.
I'm glad you're here; my terminal is trapped inside a python! Or maybe my python is trapped inside a terminal?
Can you please help me by escaping from the Python interpreter?
Click on the terminal to begin the challenge.

Terminal challenge prompt.

It looks like we're in a restricted Python shell that doesn't want us to have full control of the underlying OS. Let's try various techniques to see if we can break out.  First, let's check if we can reload any modules.


Heh, looks like we can't use "import" (or even "__import__").  We also can't use "compile" or "exec". However, it looks like they aren't explicitly blocking "eval".


Had "exec" been available, we would be able to use it to run Python code, such as "__import__("os")".  But we still have "eval", so let's make use of that. "eval" will take an expression and evaluate it as a Python expression (e.g. "2+3" would output "5")

Testing eval.

Looks like "eval" is working properly.  However, "eval('__import__("os")')" still won't work. How about if we break up "__import__("os")" into separate strings? Let's try:
os = eval('__im' + 'por' + 't__' + '("os")')
Loading "os"

Success!  Unfortunately, the shell still isn't letting us call "os.system". The shell could likely be blocking anything with "os.*"  What if we call "os" something else?

Bypassing the "os." string check.
Awesome!  Now remember - we just need to run "i_escaped".  So we'll run "test.system("./i_escaped")"


We did it! Talk to SugarPlum Mary for her hints on Question 8.
Yay, you did it! You escaped from the Python!
As a token of my gratitude, I would like to share a rumor I had heard about Santa's new web-based packet analyzer - Packalyzer.
Another elf told me that Packalyzer was rushed and deployed with development code sitting in the web root.
Apparently, he found this out by looking at HTML comments left behind and was able to grab the server-side source code.
There was suspicious-looking development code using environment variables to store SSL keys and open up directories.
This elf then told me that manipulating values in the URL gave back weird and descriptive errors.
I'm hoping these errors can't be used to compromise SSL on the website and steal logins.
On a tooootally unrelated note, have you seen the HTTP2 talk at at KringleCon by the Chrises? I never knew HTTP2 was so different!
Note: For more information, check out this great youtube video from KringleCon on escaping Python Shells - https://www.youtube.com/watch?v=ZVx2Sxl3B9c

Let's get into Question 8 and check out the packet-capture tool at https://packalyzer.kringlecastle.com/. You'll see the following page:


Go ahead and register an account by clicking the link at the bottom. Afterwards, log in, and you'll see the following screen:


The "Sniff Traffic" button will generate 20 seconds of traffic. Go ahead and click it, so we can see what shows up.


Looks like a lot of the traffic is encrypted over port 443. If you click the "Captures" option at the top right of the screen, you'll be able to download any captures you've generated.


So we won't have much luck with analyzing the underlying traffic if we don't have the underlying SSL keys.  If we had the pre-master key file, we could import it into Wireshark along with a corresponding PCAP to decrypt the traffic.

Let's poke around the site to look for any hints of server-side source code that is publicly available.

The source code for the post-login page at https://packalyzer.kringlecastle.com/ has some interesting comments.


"app.js" sounds like a promising file name.  https://packalyzer.kringlecastle.com/app.js doesn't give anything, and neither does https://packalyzer.kringlecastle.com/api/app.js

However, searching for href values through the source code for https://packalyzer.kringlecastle.com/ provides https://packalyzer.kringlecastle.com:80/pub/ as another directory to try. In fact, https://packalyzer.kringlecastle.com:80/pub/app.js will provide the app.js file!

app.js source code

The first several lines of the app.js file provide lots of great information about the packalyzer tool and site.  The following 3 lines sparked my interest:

So the SSL key log file that we want is located at "__dirname + process.env.DEV + process.env.SSLKEYLOGFILE", since dev mode is set to True.

We also need to figure out what the environment variable values are for $DEV and $SSLKEYLOGFILE.

Farther down the source code for app.js, we see the following router logic:


If you completed SugarPlum's terminal challenge, she mentioned that playing around with the URL gave back some descriptive errors.  The above router logic will parse the requested file path, take the requested directory (the first non-empty value behind a '/'), and do the following:

  1. Treat the uppercase requested directory name as an environment variable and look up its corresponding value. This value will be used as the actual directory name. If there is no corresponding value, then it'll use '/pub/' instead.
  2. Look up the requested file at "__dirname/<actual directory name or pub>/rest/of/path/to/filename"
So if we request "https://packalyzer.kringlecastle.com/home/give/me/file", and if "HOME" is a set environment variable, then it'll look for /give/me/file at "__dirname/<HOME value>".  Let's try it out and see if any error messages come up.

Error: ENOENT: no such file or directory, open '/opt/http2/opt/http2/give/me/file'
Excellent! It looks like the error message shows us the real values for "__dirname" and "$HOME". They're probably both "/opt/http2", but we can double check by trying some additional environment variables.  Let's do "dev" and "sslkeylogfile", since those are the two we need to find the file path to the SSL key log file that we want.  We will request the following:
  1. https://packalyzer.kringlecastle.com/dev/not/a/real/file
  2. https://packalyzer.kringlecastle.com/sslkeylogfile/not/a/real/file
Finding the $DEV environment variable value.

Finding the $SSLKEYLOGFILE environment variable value.

It looks like the value for the "DEV" environment variable is "/dev/", and the value for the "SSLKEYLOGFILE" environment variable is "packalyzer_clientrandom_ssl.log".  And now we can reasonably believe that "__dirname" is "/opt/http2".

So then "key_log_path" = "__dirname + process.env.DEV + process.env.SSLKEYLOGFILE", which is equal to "/opt/http2 + /dev/ + packalyzer_clientrandom_ssl.log", or simply "/opt/http2/dev/packalyzer_clientrandom_ssl.log".  Since "/opt/http2" looks like the web root, we'll want to request "https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log"

SSL key log file.

We got the keys!  Download the file.  Generate a fresh traffic capture from the packalyzer tool and download it (the keys will change every now and then, you may need to re-obtain fresh keys from https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log if needed).  Open up the packet capture with Wireshark.


If you want to make sure that the SSL keys for the traffic are actually stored in the SSL key log file we took from the packalyzer site, we can find a "Client Hello" TLS message and make sure the "Random" value in the handshake appears in the SSL log file.

Wireshark filter: "ssl.handshake.type == 1"

Searching for Random value in TLS Client Hello.

Checking for random value in log file.

A successful hit with grep indicates that we have the right SSL key file. To load the key file, go to Edit > Preferences > Protocols > SSL.

Loading key log file.

Under "(Pre)-Master-Secret log filename", click the browse button and place in your SSL log file. Click "Ok", and we should see that a lot of the traffic was decrypted.

Decrypted traffic after loading key log file.

It looks like the underlying encrypted traffic is HTTP2.  We can use the "http2" Wireshark filter to get a better look at the traffic. The goal is to find a document from Holly Evergreen to Alabaster Snowball.  Let's see if we can find traffic pertaining to Alabaster - he seems like a pretty important guy (a likely admin target).  Try the Wireshark filter "http2 contains alabaster"

Filtering for Alabaster-related HTTP2 traffic.

Right click on one of the packets and go to Follow->SSL Stream.  A search for "alabaster" in the resulting text will show that this HTTP2 datastream contains the site HTML data going to Alabaster, and that Alabaster is an admin for the packalyzer tool!  Maybe if we log in as him, we can find more clues.

Alabaster's HTML.

So then the destination IP of 10.126.0.104 must be Alabaster. Let's look for any cookie values set for this IP by using the filter "http2.headers.set_cookie and ip.dst == 10.126.0.104"

Searching for Alabaster cookies.

Bingo - we can use this session cookie to log in as Alabaster! Head over to https://packalyzer.kringlecastle.com and open the developer's console to set our "PASESSION" cookie value to "806683104222417616978996397946067", the value shown in the traffic capture.  The console command document.cookie="PASESSION=806683104222417616978996397946067" should work for Chrome.  Run the command and reload the page - you should now be in the post-login dashboard as Alabaster!

Logged in as Alabaster using his cookie.

Go to the "Captures" option, and you'll see a file available called "super_secret_packet_capture.pcap". Download it, and open it in Wireshark.

After the initial TCP handshake, it looks like some SMTP traffic starts at packet 4.

Alabaster's captured SMTP traffic.

Let's follow the TCP stream on this packet and see what's going on.

SMTP traffic stream.

It looks like we have the PCAP for email traffic from Holly Evergreen to Alabaster Snowball. The email message mentions an attachment regarding music, so this sounds exactly like what we want. We even have the attachment data, which is encoded in Base64.  Copy the Base64-encoded attachment to a file and then decode it.  A simple "base64 -d filename > outputfile" should do.  Running "file" on the resulting output indicates that the email attachment is a PDF document.

Obtaining the email attachment.

Open the file in a PDF viewer, and let's see if we can find the name of the song.


Looks like "Mary Had a Little Lamb" is mentioned at the end of the document! Submit this answer to Question 8!

For further learning: related KringleCon video on HTTP2 decryption and analysis - https://www.youtube.com/watch?v=YHOnxlQ6zec

Question 9
"Alabaster Snowball is in dire need of your help. Santa's file server has been hit with malware. Help Alabaster Snowball deal with the malware on Santa's server by completing several tasks. For hints on achieving this objective, please visit Shinny Upatree and help him with the Sleigh Bell Lottery Cranberry Pi terminal challenge.
To start, assist Alabaster by accessing (clicking) the snort terminal below:
Then create a rule that will catch all new infections. What is the success message displayed by the Snort terminal?"

Optional - talk to Shinny Upatree and complete his terminal challenge to obtain hints on question 9. Shinny is located just to the right of the stairs leading to the second floor.
Hi, I'm Shinny Upatree.
Hey! Mind giving ole' Shinny Upatree some help? There's a contest I HAVE to win.
As long as no one else wins first, I can just keep trying to win the Sleigh Bell Lotto, but this could take forever!
I'll bet the GNU Debugger can help us. With the PEDA modules installed, it can be prettier. I mean easier.
Click on the terminal to start the challenge.

Terminal challenge prompt.
Looking at the current directory, it looks like they provide "gdb" and "objdump" for us. "objdump" will provide excellent information about the executable. But for now, let's try running the lottery program and see what we're looking at.


It looks like a winning ticket number is selected, and your own ticket number is drawn. If the ticket numbers match, then you win. Rather than run the program until luck falls on our side, let's see if any of the debugging tools provide us with some hints.

Run "objdump --syms sleighbell-lotto" to print the symbol table of the file, which will provide some great information.

objdump output for the lotto program.

The "objdump" manpage provides excellent information on the output from the "--syms" option, but here's essentially what we need to know for the columns:
  1. We're looking at an ELF-formatted executable (you can find this out by running "file" on the program)
  2. The first column of hex digits represents the symbol's value.
  3. The second column contains 7 characters and/or spaces, which indicate flag bits for the symbol. Some flag bits of interest here would be "F", meaning that the symbol is a function.
  4. The third column contains the symbol's associated section.
  5. The last column on the right contains the symbol's name.
Let's go through the symbol table output and pick out any functions of interest (with the "F" flag activated in the second column).  If you want to filter out most of the non-function symbols, piping the "objdump" output to grep "F " is a simple rough way to do so.

You'll see function names like "main" and others that appear to come from standard libraries (e.g. "EVP_sha256@@OPENSSL_1_1_0" and "sleep@@GLIBC_2.2.5").  There appear to be some user-defined functions, like "sorry" and "winnerwinner".

Functions from objdump output.

"sorry" sounds like the function that gets called when your ticket number doesn't match up, and "winnerwinner" sounds like the method that gets called when the ticket number does match up.  So we don't even need to wait for the stars to align and for the ticket numbers to match - we could try calling various functions, starting with "winnerwinner", to see if we can get the victory sequence to run.

Run "gdb sleighbell-lotto" to load the program into "gdb"


Our goal is to jump to an arbitrary function, in this case winnerwinner.  To do so, let's set a break point on "main" and then run the program.

Setting a break point.

The program should stop at "main" and give control back to the debugger (us). Now we can jump to "winnerwinner" by running the command "jump winnerwinner".
gdb should continue from "winnerwinner", which means we won!

Skipping to winnerwinner.

Enjoy the ASCII art and exit the terminal. Talk to Shinny again for hints on Question 9:
Sweet candy goodness - I win! Thank you so much!
Have you heard that Kringle Castle was hit by a new ransomware called Wannacookie?
Several elves reported receiving a cookie recipe Word doc. When opened, a PowerShell screen flashed by and their files were encrypted.
Many elves were affected, so Alabaster went to go see if he could help out.
I hope Alabaster watched the PowerShell Malware talk at KringleCon before he tried analyzing Wannacookie on his computer.
An elf I follow online said he analyzed Wannacookie and that it communicates over DNS.
He also said that Wannacookie transfers files over DNS and that it looks like it grabs a public key this way.
Another recent ransomware made it possible to retrieve crypto keys from memory. Hopefully the same is true for Wannacookie!
Of course, this all depends how the key was encrypted and managed in memory. Proper public key encryption requires a private key to decrypt.
Perhaps there is a flaw in the wannacookie author's DNS server that we can manipulate to retrieve what we need.
If so, we can retrieve our keys from memory, decrypt the key, and then decrypt our ransomed files.
Oh my! Santa’s castle… it’s under siege!
We’re trapped inside and can’t leave.
The toy soldiers are blocking all of the exits!
We are all prisoners!
Let's to the Snort terminal!  The terminal didn't open when I clicked on it on the main holiday hack webpage, but you can still access it from inside the castle.  Head past the Speaker Unpreparedness Room, all the way through the hallway, past Pepper, up the stairs, and up to a door with the Scan-O-Matic badge. This looks like the same scan machine that we bypassed in Question 6, but the QR code PNG file that I used in Question 6 didn't work on this machine, so the backend system is likely different.  Maybe there's a different way we can reach the room?

Remember that zip file that we cracked into?  If you go into the "ventilation_diagram" directory, you'll find two JPG files for ventilation diagrams.  We can follow this map, starting at the vent on the castle ground floor, to the left of the Google booth.  Start with the first floor map and then move on to the second floor map once you get there (the ventilation system will take you up to the second floor). Once you reach the end, you'll find yourself in the secret room with Santa and Hans! Head over to the Snort Terminal to solve this objective.


Let's follow the prompt directions and read "~/more_info.txt" to get more information.

Objective 9 additional information.

We need to write a Snort rule that will block just the malicious traffic - except the domain names will constantly change, so we can't just block on *.baddomain. Let's check some of the recent traffic.

"tshark -r snort.log.pcap" will read in the pcap file.


It looks like the suspicious DNS traffic consists of TXT lookups for hex-ascii string subdomains. Let's run "tshark" again, but this time filtering on some of the DNS-specific fields, so we can see patterns more closely.
tshark -T fields -e dns.qry.type \
-e dns.qry.name -e dns.txt \
-r snort.log.pcap > dns_traffic_info
This tshark command will print out the DNS query type (in integer form), the requested qname, and the TXT response, if any.

tshark filtered output.

We can already see that there is some unaffiliated DNS traffic - google.fr, for instance, and aliexpress.com.

"cut -f1 dns_traffic_info | sort | uniq -c" will show that we're only seeing TXT lookups via DNS (16 is the DNS qtype for TXT records).

Upon further inspection of the longer requested subdomains, it looks like they're of one of two forms:
  1. <int>.<hex ascii>.domain (e.g. "77616E6E61636F6F6B69652E6D696E2E707331.nurgrehbas.org")
  2. <hex ascii>.domain (e.g. "62.77616E6E61636F6F6B69652E6D696E2E707331.nurgrehbas.org")
The hex ASCII strings also appear to be 38 characters long (e.g. "77616E6E61636F6F6B69652E6D696E2E707331"). The returned TXT values also appear to be hex ASCII strings. Whatever malware this is, it's using DNS tunneling through TXT queries/responses to transmit its data.

If you decode the hex ascii string in the subdomain, you get "wannacookie.min.ps1"


The first format of the subdomain (the one starting with the integer) only appears after the second format (the one without the starting integer). The victim sends a request for "77616E6E61636F6F6B69652E6D696E2E707331.domain", and the c2 server responds with a TXT record containing a number (e.g. 64)

Beginning of communication between victim and c2.

The next request from the victim appears to be "0.77616E6E61636F6F6B69652E6D696E2E707331.domain", which gets a TXT response with a bunch of hex ascii data.  It looks like the first c2 response of 64 tells the victim that there will be 64 transactions to download the full data - probably that "wannacookie.min.ps1" PowerShell script.

If we follow the traffic sample all the way to the end, we see that the victim downloads all 64 data chunks (numbered 0 to 63).

The last few transactions.

To stop this, we want to block any DNS request that contains the hex ASCII string
"77616E6E61636F6F6B69652E6D696E2E707331" as a subdomain. So the Snort rule we want will look like:
alert udp $EXTERNAL_NET 53 <> $HOME_NET any (msg:"Wanna Cookie"; content:"|26|77616E6E61636F6F6B69652E6D696E2E707331"; offset:12; depth:42; nocase; sid:3000000; rev:1;)
We want to block bad DNS traffic in either direction, which will typically be over UDP.  Remember that the bad c2 server is answering the DNS queries, so we want to look for traffic to/from external port 53.

We want to flag any DNS traffic for qnames of the form *.77616E6E61636F6F6B69652E6D696E2E707331.*, so we use the "content" keyword. With snort rules, the "content" keyword allows us to specify strings or even specific bytes that we want to match on in the payload (payload here refers to the application-layer payload, which is all the stuff after stripping off the lower layer headers).

In DNS queries and responses, each label in the requested qname is preceded by a single byte that indicates the length of the label. So a lookup for www.google.com would look like "(\x03)www(\x06)google(\x03)com(\x00)", with the null-byte at the end to indicate the end of the qname.  In our case, our target hex ASCII subdomain is 38 characters long, or "0x26" in base 16. Hence, the label will be preceded by the byte "\x26".  Including this byte in the "content" keyword for our Snort rule will help make sure we're looking at the right subdomain.

The requested qname in DNS queries/responses will appear after the first 12 bytes. Thus, we want to start searching after the first 12 bytes, so we put "offset:12".  The "offset" modifier tells us where to start looking in the payload, relative to the beginning. An offset of 0 means start searching at the beginning of the payload, while an offset of 2 means "skip the first two bytes and then start looking".  If we don't specify an offset, it'll default to 0 (search from the beginning of the payload.)

We know the hex ascii subdomain is 38 characters long, but there's a chance there's another label before it - the number indicating the sequence for the victim's requests. This number appears to go from 0 to 64, so it'll be at most 2 digits long - in other words, at most 2 bytes. Since DNS labels are preceded by a single byte indicating the label length, we'll have to account for the possibility of an additional 4 bytes of room (one byte to indicate the length of the number label, up to two bytes for the actual number label, and the byte \x26 to indicate the length of the hex-ascii subdomain).  Hence, we want the "depth" modifier for "content" to be 38 + 3 + 1 = 42.  In Snort rules, "depth" is used with the "offset" modifier to indicate how far past the offset to look. Note that although "offset" can be 0, a "depth" of 0 doesn't make sense. So a "depth" of 42 means "starting from the given offset, our match must occur within 42 bytes".  So technically, the match would occur within the first 12 + 42 = 54 bytes of the payload. The "offset" and "depth" modifiers simply specify which of those 54 bytes to look at.

Lastly, we want the "nocase" modifier to indicate that we don't care about case sensitivity.

Let's add this rule to the Snort rules file! A quick grep for "rules" in "/etc/snort/snort.conf" shows that the rules file we want to edit is at "/etc/snort/rules/local.rules" (Look at the "include" and "var RULE_PATH" lines)

Snort config file lines for rules.

We can edit the file and add our rule there.


Save the file, and you'll see the congratulations message for completing the challenge. The message is:
Congratulation! Snort is alerting on all ransomware and only the ransomware!
Success message.

For fun and practice, let's test the rule by running:
snort -A fast \
-r ~/snort.log.pcap -l ~/snort_logs \
-c /etc/snort/snort.conf
You'll get a lot of output from the snort command, but we want to look at "Action Stats" and make sure we're at least alerting on some packets.

Testing our rule and checking output.

Great! Challenge complete!  Submit the success message "Congratulation! Snort is alerting on all ransomware and only the ransomware!" as the answer to question 9 on https://holidayhackchallenge.com/2018/story.html and on your avatar badge.

You'll get the following message:
Thank you so much! Snort IDS is alerting on each new ransomware infection in our network.
Hey, you're pretty good at this security stuff. Could you help me further with what I suspect is a malicious Word document?
All the elves were emailed a cookie recipe right before all the infections. Take this document with a password of elves and find the domain it communicates with.

While we're at it, we should probably figure out what the payload is from the c2 server (the data transmitted as hex-ASCII responses to the TXT lookups).  We can obtain this by extracting all 64 TXT responses from a distinct domain, concatenating the hex ASCII TXT responses together, and then decoding them.

To make things easier, we can get 5-minutes worth of traffic onto our local machine from http://snortsensor1.kringlecastle.com/ using the username "elf" and password "onashelf"

Pick a pcap file from there and download it for offline analysis.  We can extract the qname and responses using the tshark command:
tshark -T fields \
-e dns.qry.name -e dns.txt \
-r pcapfilename > dns_txt_results
Pick a malicious domain from the output (I'm going to use "grnuebasrh.org") and then filter out the TXT results from it.
egrep '[0-9]+\.[a-fA-F0-9]{38}\.grnuebasrh\.org' \
dns_text_results | sort -V -u | \
cut -f2 | egrep '.+' | \
while read line; do \
echo -n $line >> full_hex_response; \
done
"full_hex_response" will contain one contiguous string of all 64 chunks of hex ascii response data. To decode the hex data, we can use the "xxd" command:
xxd -r -p full_hex_response > txt_payload
It looks like the payload is a big chunk of PowerShell code. Let's beautify it and space things out for easier reading. We'll definitely need this for the later questions. 

That's all for this post. Thank you for joining me on this adventure, and I hope you learned something new!  In future posts, I'll dive through the PowerShell code and the remaining questions for the holiday hack.

No comments:

Post a Comment