STHACK2022 - Catch the bird, a trip from web to IRL
- Challenge author: ajani
- Category:
web
,physical
The challenge started with the following Post Card
We also had the following scenario:
We quickly realized we needed to listen on the Radio Frequency 101.1Mhz inside the “Hotel de Ville”. This was achieved using a Xiaomi Android Application, we recorded everything broadcasted on the frequency in this file:
We can hear the well known military code, which gave us this .onion website: http://sthackmdbidwkyebhklnfnwmqhj3oxnke5inylzrtwzenr752tn77oad.onion:80, we accessed it using Tor.
After some time exploring the website we discovered “a feature” to send a mail with an extract of the current code when an error occured on the website.
We found two ways to trigger an error:
- trying to login using a wrong password:
LoginController.php
- while injecting and breaking context inside the shop part of the website:
Lexer.php
The team spent too much time trying to find a Server Side Template Injection since it was using a twig
template in line 21@LoginController.php.
However while we were fuzzing the category
field, we discovered the ‘~
’ was working to concatenate two strings in Twig and tried the following requests:
Query | Output | Indicator |
---|---|---|
category=category == ‘forgery’ | Displays the product “Coated Something” | True |
category=category == ‘forg’~’ery’ | Displays the product “Coated Something” | True |
category=category == ‘forg’~’ery’~’7’*7 | Don’t display but do not throw an error | False |
We had an oracle !! However we didn’t manage to find an interesting primitive allowing us to execute commands.
So we used our favorite tool “Burp Suite Intruder” and fuzzed with the top 100 functions in PHP and Symfony and some weird behaviors started to appear.
The function constant
was recognised by the parsing engine, then it clicked, the Lexer.php
file was located in a folder called expression language
. We tried to confirm the function using several global constants.
However it’s only giving us the information about the existence of a constant it would be better to extract their value. Especially the value of the constant self::PASSWORD
@LoginController.php.
First, we attempt to access the constant located in another file on the server. Thankfully the server errors were quite verbose and give us half of the payload :)
The trick here was to add several backslash \\\\
Now that we confirmed the access to the PASSWORD
constant, we tried to read the characters one-by-one like any array with array[0] == 'A'
. But the pain was only starting, Expression Language constant cannot be accessed like an array… We finally found an alternative using Regular Expressions and created a small script which was … too slow, remember we are using Tor to connect to the .onion website and the shared network on site was a bit laggy.
Once again we turned to our Intruder and fuzzed with the charset a-zA-Z0-9
, with the following payload: category=constant("\\App\\Controller\\LoginController::PASSWORD") matches "/^Y[FUZZED_CHAR_ADDED_HERE]/" && category == 'forgery'
and we got the following quite quickly: Ybhr5vmjJD
after switching to our 4G network for a more reliable access.
Now we can access the Admin Panel and manage the products. Only one product (deaddrop) as available for purchase.
Dead Drops is an anonymous, offline, peer to peer file-sharing network in public space. Anyone can access a Dead Drop and everyone may install a Dead Drop in their neighborhood/city. A Dead Drop must be public accessible. - The Dead Drops Manifesto
The product coordinates were really close to us and the challenge was listed in the categories web
and physical
. We went there with a laptop and other tools like a lockpick set. Obviously the deaddrop wasn’t embbeded into the “Hotel de Ville”’s wall, it was cemented to a pad lock ;)
Some interesting encrypted files were stored on the deaddrop and we had the key to decrypt them using the command openssl aes-256-cbc -d -in whisky.txt.aes256cbc -out secrets2.txt.new
And now for the final step: a new onion URL from one of the decrypted file (maltapyzyfnwvgkl4se7tlhlrohule77cgnaguy2nouxyvosovjldbyd.onion). It seems to be an aunction website, but the site is not yet opened. The opening is scheduled for the 23rd May but something smells fishy, the Javascript was heavily obfuscated, and we are still waiting for our flag.
Let’s discuss methodology to apprehend this problem:
Having spent wayyyy too much time on this challenge, we used a quite radical approach and changed our VM internal date slowly to arrived at the opening date: date -s "2021-03-23 16:58:22"
and then we got our flag.
Overall the challenge was nice and interesting to resolve with my teammates. Having a bit of physical without resorting to use a full arsenal of HackRF, Proxmark or a crowbar was quite refreshing :)