Being curious

24 Dec 2020

Kringlecon 2020 Terminals Walkthrough

Walkthrough of Kringlecon 2020 terminals & minigames.

Overview

The below entry only covers solutions to the terminals and minigames from Kringlecon 2020. Solutions to the objectives are published seperately.

PDF copy of my full walkthrough is published here

Terminals

Kringle Kiosk

Location: Castle Approach

Kringle Kiosk Hiya hiya - I'm Shinny Upatree! Check out this cool KringleCon kiosk! You can get a map of the castle, learn about where the elves are, and get your own badge printed right on-screen! Be careful with that last one though. I heard someone say it's "ingestible." Or something... Do you think you could check and see if there is an issue?

Should be easy, we just have to run /bin/bash.

Basic command injection using a ; character does the trick

Kringle Kiosk Solution

After solving the challenge and returning to Shinny Upatree he unlocks the details for Objective 2 (Investigate S3 Bucket).

Reattaching TMUX

Location: Castle Approach

Reattaching Tmux Howdy - Pepper Minstix here! I've been playing with tmux lately, and golly it's useful. Problem is: I somehow became detached from my session. Do you think you could get me back to where I was, admiring a beautiful bird? If you find it handy, there's a tmux cheat sheet you can use as a reference. I hope you can help!

Command tmux attach-session based on the supplied hint https://tmuxcheatsheet.com/ did the trick.

Reattaching TMUX solution

After solving the challenge and returning to Pepper Minstix he unlocks the details for Objective 4 (Operate the Santavator).

Linux Primer

Location: Courtyard

Linux Primer Sugarplum Mary? That's me! I was just playing with this here terminal and learning some Linux! It's a great intro to the Bash terminal. If you get stuck at any point, type hintme to get a nudge! Can you make it to the end?

Solving the terminal requires stepping through multiple Linux based questions.

  1. Perform a directory listing of your home directory to find a munchkin and retrieve a lollipop!: ls
  2. Now find the munchkin inside the munchkin: cat munchkin_19315479765589239
  3. Great, now remove the munchkin in your home directory: rm munchkin_19315479765589239
  4. Print the present working directory using a command: pwd
  5. Good job but it looks like another munchkin hid itself in you home directory. Find the hidden munchkin: ls -al
  6. Excellent, now find the munchkin in your command history: grep munchkin .bash_history
  7. Find the munchkin in your environment variables: env | grep munchkin
  8. Next, head into the workshop: cd workshop/
  9. A munchkin is hiding in one of the workshop toolboxes. Use “grep” while ignoring case to find which toolbox the munchkin is in: *grep -i munchkin .txt
  10. A munchkin is blocking the lollipop_engine from starting. Run the lollipop_engine binary to retrieve this munchkin: chmod +x lollipop_engine; ./lollipop_engine
  11. Munchkins have blown the fuses in /home/elf/workshop/electrical. cd into electrical and rename blown_fuse0 to fuse0: cd electrical/; mv blown_fuse0 fuse0
  12. Now, make a symbolic link (symlink) named fuse1 that points to fuse0: ln -s fuse0 fuse1
  13. Make a copy of fuse1 named fuse2: cp fuse1 fuse2
  14. We need to make sure munchkins don’t come back. Add the characters “MUNCHKIN_REPELLENT” into the file fuse2: echo ‘MUNCHKIN_REPELLENT’ » fuse2
  15. Find the munchkin somewhere in /opt/munchkin_den: find /opt/munchkin_den/ -iname ‘munchkin
  16. Find the file somewhere in /opt/munchkin_den that is owned by the user munchkin: find /opt/munchkin_den/ -user munchkin
  17. Find the file created by munchkins that is greater than 108 kilobytes and less than 110 kilobytes located somewhere in /opt/munchkin_den: find /opt/munchkin_den/ -size +108k -size -110k
  18. List running processes to find another munchkin: ps aux
  19. The 14516_munchkin process is listening on a tcp port. Use a command to have the only listening port display to the screen: netstat -ato
  20. The service listening on port 54321 is an HTTP server. Interact with this server to retrieve the last munchkin: curl http://127.0.0.1:54321
  21. Your final task is to stop the 14516_munchkin process to collect the remaining lollipops: kill -9 33271

Redis Bug Hunt

Location: Kitchen

Redis Bug Hunt Hi, so glad to see you! I'm Holly Evergreen. I've been working with this Redis-based terminal here. We're quite sure there's a bug in it, but we haven't caught it yet. The maintenance port is available for curling, if you'd like to investigate. Can you check the source of the index.php page and look for the bug? I read something online recently about remote code execution on Redis. That might help! I think I got close to RCE, but I get mixed up between commas and plusses. You'll figure it out, I'm sure

Method used to obtain the source of index.php was a variation of the remote code execution approach described at https://book.hacktricks.xyz/pentesting/6379-pentesting-redis. Approach taken:

  • Access the redis-cli. The password for redis was found in the /etc/redis/redis.conf file.
  • Flushing the database to remove any stray entries (flushall).
  • Using config set to move the database location into the Webroot (/var/www/html)
  • A short php script () was created as a new value for redis to store.
  • The database file name can then be accessed via a straight-forward curl command. This triggers the php script and displays the bug.
# We found the bug!! # \ / # .\-/. # /\ () () # \/~---~\.-~^-. # .-~^-./ | \---. # { | } \ # .-~\ | /~-. # / \ A / \ # \/ \/ # #####hhc:{"hash": "448eb84c8ba266ef64af84a5d2ac66f765591c4d44d1f6ca2baf2666ed97c875", "resourceId": "a7b9af8e-cef5-45b8-b45d-e351a047e9c2"}#####

After solving the terminal challenge Holly provides hints for Objective 8 (Broken Tag Generator).

Speaker Unprep

Location: Talks Lobby

Speaker Unprep Ohai! Bushy Evergreen, just trying to get this door open. It's running some Rust code written by Alabaster Snowball. I'm pretty sure the password I need for ./door is right in the executable itself. Isn't there a way to view the human-readable strings in a binary file?

This terminal has three challenges. First step is to open the door, next is to turn on the lights & the final is to enable the vending machines.

Opening the Door

Opening the door requires analysing the door binary using strings. The passed (Op3nTheD00r) was embedded in the file.

Speaker Unprep - Opening the door

Turning on the Lights

After opening the door Bushy Evergreen provides follow-up hints to help turn on the lights. Solving the challenge requires using the application versions in the lab directory.

Solving the problem requires manipulating the configuration file. The lights application will load the username and password values from lights.conf. Any encrypted values will be automatically decrypted by the application. The encrypted password was put into the username field. When the application was run it auto decrypts the username value and echo it back to the user.

elf@0396ea837d14 ~/lab $ cat lights.conf 
password: bob
name: E$ed633d885dcb9b2f3f0118361de4d57752712c27c5316a95d9e5e5b124
<RUN LAB APPLICATION>
>>> CONFIGURATION FILE LOADED, SELECT FIELDS DECRYPTED: /home/elf/lab/lights.conf
---t to help figure out the password... I guess you'll just have to make do!
The terminal just blinks: Welcome back, Computer-TurnLightsOn
<RUN MAIN APPLICATION>
elf@0396ea837d14 ~/lab $ ../lights 
The speaker unpreparedness room sure is dark, you're thinking (assuming
you've opened the door; otherwise, you wonder how dark it actually is)
You wonder how to turn the lights on? If only you had some kind of hin---
 >>> CONFIGURATION FILE LOADED, SELECT FIELDS DECRYPTED: /home/elf/lights.conf
---t to help figure out the password... I guess you'll just have to make do!
The terminal just blinks: Welcome back, elf-technician
What do you enter? > Computer-TurnLightsOn
Checking......
Lights on!

Vending Machines

Password to re-enable the vending machines was determined through trial and error. If the configuration file (vending-machines.json) is deleted the application will re-generate this using user input when run. This allows the original encrypted value to be determined through a chosen plaintext attack.

Experimenting determined:
  • Individual characters are being substituted – final password will be as long as the encrypted password (LVEdQPpBwr decrypts to a 10-character password)
  • System is working in 8-character blocks (using a 16-character password of A’s shows two matching 8-byte blocks). This indicates the encrypting is more complicated than a simple substitution cipher.

After trial and error, the password was determined to be CandyCane1.

Present Sorter

Location: Workshop

Present Sorter Hey there, KringleCon attendee! I'm Minty Candycane! I'm working on fixing the Present Sort-O-Matic. The Sort-O-Matic uses JavaScript regular expressions to sort presents apart from misfit toys, but it's not working right. With some tools, regexes need / at the beginning and the ends, but they aren't used here. You can find a regular expression cheat sheet here if you need it. You can use this regex interpreter to test your regex against the required Sort-O-Matic patterns. Do you think you can help me fix it?

Hint links given:

Direct link is https://present-sorter.kringlecastle.com/.

Answers:

  1. Matches at least one digit - \d
  2. Matches 3 alpha a-z characters ignoring case - [a-zA-Z]{3,}
  3. Matches 2 chars of lowercase a-z or numbers - [a-z1-9]{2}
  4. Matches any 2 chars not uppercase A-L or 1-5 - [^A-L1-5]{2}
  5. Matches three or more digits only - ^[0-9]{3,}$
  6. Matches multiple hour:minute:second time formats only - ^(?:[1-9]{1}|0[1-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$
  7. Matches MAC address format only while ignoring case - ^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$
  8. Matches multiple day, month, and year date formats only - ^[0-3][0-9].(0[1-9]|1[0-2]).[0-9]{4}$

After solving Minty Candycane provides follow-on hints for Objective 6 (Splunk challenge).

Scapy Prepper

Location: Netwars

Scapy Prepper Welcome to the roof! Alabaster Snowball here. I'm watching some elves play NetWars! Feel free to try out our Scapy Present Packet Prepper! If you get stuck, you can help() to see how to get tasks and hints.

Solving the terminal challenge required answering several questions. Correct answers to each question are shown below in bold.

Answers:

  1. Start by running the task.submit() function passing in a string argument of ‘start’ - task.submit(“start”)
  2. Submit the class object of the scapy module that sends packets at layer 3 of the OSI model - task.submit(send)
  3. Submit the class object of the scapy module that sniffs network packets and returns those packets in a list - task.submit(sniff)
  4. Submit the NUMBER only from the choices below that would successfully send a TCP packet and then return the first sniffed response packet to be stored in a variable named “pkt” - task.submit(1)
  5. Submit the class object of the scapy module that can read pcap or pcapng files and return a list of packets - task.submit(rdpcap)
  6. The variable UDP_PACKETS contains a list of UDP packets. Submit the NUMBER only from the choices below that correctly prints a summary of UDP_PACKETS - task.submit(2)
  7. Submit only the first packet found in UDP_PACKETS - task.submit(UDP_PACKETS[0])
  8. Submit only the entire TCP layer of the second packet in TCP_PACKETS - task.submit(TCP_PACKETS[1][TCP])
  9. Change the source IP address of the first packet found in UDP_PACKETS to 127.0.0.1 and then submit this modified packet- a = UDP_PACKETS[0]; a[IP].src = “127.0.0.1”; task.submit(a)
  10. Submit the password “task.submit(’elf_password’)” of the user alabaster as found in the packet list TCP_PACKETS - task.submit(’echo’)
  11. The ICMP_PACKETS variable contains a packet list of several icmp echo-request and icmp echo-reply packets. Submit only the ICMP chksum value from the second packet in the ICMP_PACKETS list - task.submit(ICMP_PACKETS[1][ICMP].chksum)
  12. Submit the number of the choice below that would correctly create a ICMP echo request packet with a destination IP of 127.0.0.1 stored in the variable named “pkt” - task.submit(3)
  13. Create and then submit a UDP packet with a dport of 5000 and a dst IP of 127.127.127.127. (all other packet attributes can be unspecified) - p = Ether()/IP(dst=‘127.127.127.127’)/UDP(dport=5000); task.submit(p)
  14. Create and then submit a UDP packet with a dport of 53, a dst IP of 127.2.3.4, and is a DNS query with a qname of “elveslove.santa”. (all other packet attributes can be unspecified) - p = Ether()/IP(dst=‘127.2.3.4’)/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname=‘elveslove.santa’)); task.submit(p)
  15. The variable ARP_PACKETS contains an ARP request and response packets. The ARP response (the second packet) has 3 incorrect fields in the ARP layer. Correct the second packet in ARP_PACKETS to be a proper ARP response and then task.submit(ARP_PACKETS) for inspection - ARP_PACKETS[1][ARP].op=2; ARP_PACKETS[1][ARP].hwsrc=ARP_PACKETS[1].src; ARP_PACKETS[1][ARP].hwdst=ARP_PACKETS[1].dst; task.submit(ARP_PACKETS)

CAN-Bus Investigation

Location: Netwars

CAN BUS investigation Hiya hiya - I'm Wunorse Openslae! I've been playing a bit with CAN bus. Are you a car hacker? I'd love it if you could take a look at this terminal for me. I'm trying to figure out what the unlock code is in this CAN bus log. When it was grabbing this traffic, I locked, unlocked, and locked the doors one more time. It ought to be a simple matter of just filtering out the noise until we get down to those three actions. Need more of a nudge? Check out Chris Elgee's talk on CAN traffic!

Hints given:

After visualising the file first step is to summarise it using basic shell tools

elf@fe68d2429a57:~$ cat candump.log | cut -d ' ' -f 3 | cut -d '#' -f 1 | sort | uniq -c
     35 188
      3 19B
   1331 244

Based on the motd hint there is one lock, one unlock and another lock. Code 19B appears three times so this is likely it:

elf@fe68d2429a57:~$ cat candump.log | grep 19B | grep -v 244
(1608926664.626448) vcan0 19B#000000000000
(1608926671.122520) vcan0 19B#00000F000000
(1608926674.092148) vcan0 19B#000000000000

After submitting a couple of attempts at the highlighted timestamp we have the answer:

elf@8ae40d2894c3:~$ ./runtoanswer 122520
<SNIP>
Your answer is correct!

Minigames

Christmas Lights

Christmas Lights Put it in the cloud," they said... "It'll be great," they said... All the lights on the Christmas trees throughout the castle are controlled through a remote server. We can shuffle the colors of the lights by connecting via dial-up, but our only modem is broken! Fortunately, I speak dial-up. However, I can't quite remember the handshake sequence.

Solving the minigame requires interacting with the telephone and manually entering the modem handshake values. A link was given to a modem handshake recording as a hint.

Listening to the clue at gives the below sequence

  • Baa DEE brrr
  • Aaah
  • Wewewwwwrrwrr
  • beDURRdunditty
  • SCHHRRR

The correct answer can also be determined by analysing the page source at https://dialup.kringlecastle.com/. Key things taken from analysing the source files (dialup.js & index)

  • Answer is checked by sending a GET request to checkpass.php?i=SECRET&resourceId=RESOURCEID
  • RESOURCEID is specific to the player. This is how the game knows if the player solved the challenge. This value can be extracted from the browser console.
  • SECRET is progressively populated as the user interacts with the buttons. The full correct secret value is ‘39cajd3j2jc329dz4hhddhbvan3djjzz’. Both items need to be sent for a successful response.

After completing the Minigame Fitzy Shortstack mentions that Santa trusts Shiny Upatree. This came in handy for Objective 5 (Open HID lock).

The Elf Code

Location: Dining Room

Elf Code Hello - my name is Ribb Bonbowford. Nice to meet you! Are you new to programming? It's a handy skill for anyone in cyber security. This challenge centers around JavaScript. Take a look at this intro and see how far it gets you!

Solving each level of the minigame required writing a short JavaScript. Solutions used for each of the levels are below.

Level 1 – The Elf C0de
elf.moveLeft(10)
elf.moveUp(12)
Level 2 - Trigger The Yeeter
elf.moveTo(lever[0])
elf.pull_lever(elf.get_lever(0)+2)
elf.moveLeft(4)
elf.moveUp(10)
Level 3 - Move To Loopiness
for (i = 0; i < 3; i++) {
  elf.moveTo(lollipop[i])
}
elf.moveUp(1)
Level 4 - Up Down Loopiness
for (i = 0; i < 3; i++) {
  elf.moveLeft(3)
  elf.moveUp(11)
  elf.moveLeft(3)
  elf.moveDown(11)
}
Level 5 - Move To Madness
elf.moveUp(8)
elf.moveLeft(10)
weird = elf.ask_munch(0)
var clean_weird = weird.filter(function(value, index, arr) {
  return (typeof value == "number")
});
elf.tell_munch(clean_weird)
elf.moveUp(2)
Level 6 - Two Paths, Your Choice

Solution used was to talk to the munchkin. Solution using the lever was attempted but continued to fail with weird Javascript console errors.

for (i = 0; i < 4; i++) {
  elf.moveTo(lollipop[i])
}
elf.moveLeft(8)
elf.moveUp(2)
weird = elf.ask_munch(0)
console.log(weird)

function getKeyByValue(object, value) {
  return Object.keys(object).find(key => object[key] === value);
}
elf.tell_munch(getKeyByValue(weird, 'lollipop'))
elf.moveUp(2)
Level 7 - Yeeter Swirl
function move(a) {
  1 == a || 5 == a ? elf.moveDown(a) : 2 == a || 6 == a ? elf.moveLeft(a) : 3 == a || 7 == a ? elf.moveUp(a) : (4 == a || 8 == a) && elf.moveRight(a)
}
for (i = 1; 9 > i; i++) move(i), elf.pull_lever(i - 1);
function YourFunctionNameHere(one_argument) {
    some_desired_data = 0
    one_argument.forEach(function(deeper_argument) {
        deeper_argument.forEach(function(an_item) {
            if (typeof an_item == "number") {
                some_desired_data = some_desired_data + an_item
            }
        })
    })
    return some_desired_data
}
elf.moveUp(2)
elf.moveLeft(4)
elf.tell_munch(YourFunctionNameHere)
elf.moveUp(2)
Level 8 - For Loop Finale
var lever_sum = 0,
  lever_num = 0,
  move_right = !0;
for (i = 1; 12 > i; i += 2) {
  !0 == move_right ? elf.moveRight(i) : elf.moveLeft(i), move_right = !move_right;
  var lever_value = elf.get_lever(lever_num);
  lever_sum += lever_value, lever_num++, elf.pull_lever(lever_sum), elf.moveUp(2)
}

function getKeyByValue(object, value) {
  return Object.keys(object).find(key => object[key] === value);
}

function YourFunctionNameHere(one_argument) {
    var lollipop_key = ''
    one_argument.forEach(function(deeper_argument) {
        am_i_a_key = getKeyByValue(deeper_argument, 'lollipop')
        if (am_i_a_key) {
            lollipop_key = am_i_a_key
        }
    })
    return lollipop_key
}
elf.tell_munch(YourFunctionNameHere)
elf.moveRight(12)

Snowball Fight

Location: UNPreparedness Room

Snowball Fight Howdy gumshoe. I'm Tangle Coalbox, resident sleuth in the North Pole. If you're up for a challenge, I'd ask you to look at this here Snowball Game. We tested an earlier version this summer, but that one had web socket vulnerabilities. This version seems simple enough on the Easy level, but the Impossible level is, well... I'd call it impossible, but I just saw someone beat it! I'm sure something's off here. Could it be that the name a player provides has some connection to how the forts are laid out? Knowing that, I can see how an elf might feed their Hard name into an Easy game to cheat a bit. But on Impossible, the best you get are rejected player names in the page comments. Can you use those somehow? Check out Tom Liston's talk for more info, if you need it.

After experimenting key items needed to solve the game here:

  • Game users the Player Name as a random seed to determine the board layout. Starting a game with the same player name will always yield the same board layout.
  • The HTML comments of the board game on impossible provide 624 random player names chosen and discarded. Using a Mersenne Twister predictor the actual used Player Name can be determined. HTML of the game can be viewed through the browser code inspector.

To win a game on impossible:

  1. Spawn a new game on impossible difficulty. Feed the discarded player names into a Mersenne Twister predictor to determine the actual name.
  2. Spawn a new separate game on easy difficulty. Use the predicted player name.
  3. Win the game on easy difficulty. It will show where all the opponents forts are.
  4. Use the knowledge of where the player forts are to win on impossible difficulty.

After winning the game on impossible difficulty Tangle Coalbox provides several key hints for Objective 11b.