Kiosk #4, Speaker UNPrep
The Kiosk right next to Bushy Evergreen was a neat challenge involving exploiting inputs and breaking simple crypto. There were three challenges, but all you really needed to do was fix one.
The Door
First, I was met with this prompt:
Help us get into the Speaker Unpreparedness Room!
The door is controlled by ./door, but it needs a password! If you can figure
out the password, it'll open the door right up!
Oh, and if you have extra time, maybe you can turn on the lights with ./lights
activate the vending machines with ./vending-machines? Those are a little
trickier, they have configuration files, but it'd help us a lot!
(You can do one now and come back to do the others later if you want)
We copied edit-able versions of everything into the ./lab/ folder, in case you
want to try EDITING or REMOVING the configuration files to see how the binaries
react.
Note: These don't require low-level reverse engineering, so you can put away IDA
and Ghidra (unless you WANT to use them!)
elf@68d8bde496ae ~ $
As tempting as running this in Ghidra was, I went the high-level route.
First, I ran the door binary -- it asked for a password, so I just typed whatever to see how it would react:
elf@68d8bde496ae ~ $ ./door
You look at the screen. It wants a password. You roll your eyes - the
password is probably stored right in the binary. There's gotta be a
tool for this...
What do you enter? > blahblahblah
Checking......
Beep boop invalid password
elf@68d8bde496ae ~ $
Complete with a stall timer to prevent brute forcing. I first ran the strings
command on the binary to see if anything stood out:
elf@68d8bde496ae ~ $ strings ./door
/lib64/ld-linux-x86-64.so.2
@1(I
libdl.so.2
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
dladdr
libpthread.so.0
nanosleep
pthread_mutex_init
<<< snip >>>
NulErrorBox<Any>thread 'expected, found Door opened!
That would have opened the door!
Be sure to finish the challenge in prod: And don't forget, the password is **"Op3nTheD00r"**
Beep boop invalid password
src/liballoc/raw_vec.rscapacity overflowa formatting trait implementation
returned an error/usr/src/rustc-1.41.1/src/libcore/fmt/mod.rsstack backtrace:
-
cannot panic during the backtrace function/usr/src/rustc-1.41.1/vendor/
backtrace/src/lib.rsSomething went wrong: Checking...Something went wrong
reading input: Something went wrong in the environment: couldn't get the
executable name
pthread_mutex_init
<<< snip >>>
Got the password! Another reason why you should never hard-code passwords in your code!
elf@68d8bde496ae ~ $ ./door
You look at the screen. It wants a password. You roll your eyes - the
password is probably stored right in the binary. There's gotta be a
tool for this...
What do you enter? > Op3nTheD00r
Checking......
Door opened!
No onto the Lights...
The Lights
I couldn't write to anything in this directory, but there was a "lab" directory that had copies of the app and read/write access. The lights
binary came with an accompanying lights.conf
file that contained the following:
elf@68d8bde496ae ~ $ cat lights.conf
password: E$ed633d885dcb9b2f3f0118361de4d57752712c27c5316a95d9e5e5b124
name: elf-technician
And running the binary shows:
elf@68d8bde496ae ~ $ ./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? > hi
Checking......
Beep boop invalid password
So the password is encrypted. I couldn't figure out what cipher it was that started with E$, but since I had read/write access to the lights.conf file, I started playing with it. After various modifications to the file, I noticed that if I set the "name" field to the same value as the password field, I get an interesting result!
Re-running the binary with the aforementioned, I come across:
elf@68d8bde496ae ~/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/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**
What do you enter? >
Found the password: Computer-TurnLightsOn
The Vending Machine
This challenge was a bit more intense. It involved attempting to find the decode the algorithm used for encryption before actually solving it. It got me to break out SublimeText to code this in Python!
First, it had an accompanying JSON file, vending-machines.json
:
elf@68d8bde496ae ~/lab $ cat vending-machines.json
{
"name": "elf-maintenance",
"password": "LVEdQPpBwr"
}
Running the binary shows the following. I input the password 'password' to see what it would do:
elf@68d8bde496ae ~/lab $ ./vending-machines
The elves are hungry!
If the door's still closed or the lights are still off, you know because
you can hear them complaining about the turned-off vending machines!
You can probably make some friends if you can get them back on...
Loading configuration from: /home/elf/lab/vending-machines.json
I wonder what would happen if it couldn't find its config file? Maybe that's
something you could figure out in the lab...
Welcome, asdf! It looks like you want to turn the vending machines back on?
Please enter the vending-machine-back-on code > password
Checking......
Beep boop invalid password
However, I discovered something interesting if I remove the JSON file and run the program -- it attempts to rebuild it!
The elves are hungry!
If the door's still closed or the lights are still off, you know because
you can hear them complaining about the turned-off vending machines!
You can probably make some friends if you can get them back on...
Loading configuration from: /home/elf/lab/vending-machines.json
I wonder what would happen if it couldn't find its config file? Maybe that's
something you could figure out in the lab...
ALERT! ALERT! Configuration file is missing! New Configuration File Creator Activated!
Please enter the name > agr0
Please enter the password > AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Welcome, agr0! It looks like you want to turn the vending machines back on?
Please enter the vending-machine-back-on code >
Checking......
Beep boop invalid password
I made a password of a string of A's to see what the password would encrypt to...
elf@68d8bde496ae ~/lab $ cat ./vending-machines.json
{
"name": "agr0",
"password": "XiGRehmwXiGRehmwXiGRehmwXiGRehmwXiGRehmwXiGRehmwX"
}
That's a pretty interesting pattern! It looks like even though my password is all A's, it changes the ciphertext for each character...however the pattern repeats every 8 characters. A becomes X, then i, then G, then R, e, h, m w, and back to X again to repeat the pattern. This looks all too similar to a Vigenere cipher, which basically takes plaintext and XORs it against a key that is repeated over and over again, which would look something like this:
Plaintext : Agr0iscool
Key : PWNPWNPWNP
Ciphertext : Pce0xopdky
And in this case, if you have control over the encryption oracle, you can send repetitive plaintext to at least determine the key length:
Plaintext : AAAAAAAAAAAAAA
Key : PWNPWNPWNPWNPW
Ciphertext : PWNPWNPWNPWNPW
This was still a bit of an oddball because standard Vigenere works with only 26 characters, no spaces, numbers, or even the concept of upper or lowercase. So the fact that the ciphertext has those extra characters makes this a bit more challenging than busting a Vigenere cipher.
However, like I said earlier, I did have access to the encryption oracle...what if I make a lookup table of all possible combinations of the letters and numbers?
First, I removed the JSON file and re-ran the program. This time, I gave the password all combinations of letters and numbers eight times each with the help of a little python3:
Entering that as the password gives me new ciphertext!
9VbtacpgGUVBfWhPe9ee6EERORLdlwWbwcZQAYue8wIUrf5xkyYSPafTnnUgokAhM0sw4eOCa8okTqy
1o63i07r9fm6W7siFqMvusRQJbhE62XDBRjf2h24c1zM5H8XLYfX8vxPy5NAyqmsuA5PnWSbDcZRCdg
TNCujcw9NmuGWzmnRAT7OlJK2X7D7acF1EiL5JQAMUUarKCTZaXiGRehmwDqTpKv7fLbn3UP9Wyv09i
u8Qhxkr3zCnHYNNLCeOSFJGRBvYPBubpHYVzka18jGrEA24nILqF14D1GnMQKdxFbK363iZBrdjZE8I
MJ3ZxlQsZ4Uisdwjup68mSyVX10sI2SHIMBo4gC7VyoGNp9Tg0akvHBEkVH5t4cXy3VpBslfGtSz0PH
MxOl0rQKqjDq2KtqoNicv3ehm9ZFH2rDO5LkIpWFLz5zSWJ1YbNtlgophDlgKdTzAYdIdjOx0OoJ6JI
tvtUjtVXmFSQw4lCgPE6x7
I had a lookup table now. I can in theory chop up the above in blocks of 8, knowing that each block represents a letter or number in the provided plaintext. So:
a = [9, V, b, t, a, c, p, g]
b = [G, U, V, B, f, W, h, P]
and so on. It has to do with the placement too, so if the second character of the plaintext is V, I'll know that the plaintext is 'a'.
Seeing this as a potential to use python, I created the following code:
!/usr/bin/env python3
# First, let's create a substitution cipher generator:
import string
from itertools import cycle
alpha = string.ascii_letters + '0123456789'
# We know the keylength is 8, so we'll repeat the above 8 times
keylength = 8
# The ciphertext we are working with (found from vending_machine.json)
ciphertext = "LVEdQPpBwr"
# Create a cycle object of the key length (for use later)
c_key = cycle(range(keylength))
plaintext = ''
for i in alpha:
plaintext += i*8
subs = ('9VbtacpgGUVBfWhPe9ee6EERORLdlwWbwcZQAYue8wIUrf5xkyYSPafTnnU'
'gokAhM0sw4eOCa8okTqy1o63i07r9fm6W7siFqMvusRQJbhE62XDBRjf2h2'
'4c1zM5H8XLYfX8vxPy5NAyqmsuA5PnWSbDcZRCdgTNCujcw9NmuGWzmnRAT'
'7OlJK2X7D7acF1EiL5JQAMUUarKCTZaXiGRehmwDqTpKv7fLbn3UP9Wyv09'
'iu8Qhxkr3zCnHYNNLCeOSFJGRBvYPBubpHYVzka18jGrEA24nILqF14D1Gn'
'MQKdxFbK363iZBrdjZE8IMJ3ZxlQsZ4Uisdwjup68mSyVX10sI2SHIMBo4g'
'C7VyoGNp9Tg0akvHBEkVH5t4cXy3VpBslfGtSz0PHMxOl0rQKqjDq2KtqoN'
'icv3ehm9ZFH2rDO5LkIpWFLz5zSWJ1YbNtlgophDlgKdTzAYdIdjOx0OoJ6'
'JItvtUjtVXmFSQw4lCgPE6x7')
chomps = []
for i in range(0, len(subs), keylength):
chomps.append(subs[i:i+keylength])
keyset = []
for i, j in zip(alpha, chomps):
keyset.append({i: j})
# Now we have i: j, where i is the letter used and j is the combination of
# character substitutions that letter will have. I'm sure this is a cipher
# of some sort, but still.
result = ''
key_place = next(c_key)
for e, letter in enumerate(ciphertext):
for i in keyset:
if list(i.values())[0][key_place] == letter:
key_place = next(c_key)
for key in i:
result += key
break
print(f"Found password: {result}")
And running it produces the following result: CandyCane1
Nice!