Sergei’s Mystery Block
Intro
My friend Sergei got creative and this mysterious block of transparent epoxy. What could be inside?
Inside we see a Pico W, a thicc capacitor from VSYS to GND, a coil hovering over the RP2040 connecting a GPIO to GND, and some kind of glass tube connecting 3V3(OUT) to another GPIO. There are four legs that expose VSYS, GND, and two more GPIOs.
How can the Pico talk to us, and how can we talk back? Since it’s the W version, it can talk WiFi and Bluetooth; the two GPIOs can be used for something with two wires like UART or I2C; there is the built-in LED next to the micro USB port that might react to something; there’s the big coil that might be able to send or receive electromagnetic signals; and finally there is the glass tube that surely is used for something.
Wired
Let’s start with the two GPIOs. A good first guess is that these are used as TX and RX pins for a UART interface with a 115200 baudrate. To test this I hooked them up to an FTD1232 UART <-> USB adapter, and after swapping the RX and TX pins back and forth three times, the folling shows up in picocom
:
*********************************************************************
**************************The last CTF*******************************
*********************************************************************
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
Connecting online...
I just want to be in the office... Try again!
Connecting online... Connecting online...
It keeps repeating that message forever. It seems to be looking for some kind of office network?
Wireless
The message that shows up on the wired interface suggests it uses the Wifi module to talk to the internet. There’s a good chance it tries to talk to Riscure’s guest network. After some fiddling with network configuration on my laptop, the following sets up a wireless network with that SSID, and forwards the wired network so that it can go online, while also making it possible to transparantly monitor what is going on with mitmproxy:
# Use nmcli to setup a hotspot
nmcli dev wifi hotspot ifname wlp61s0 ssid "Riscure Guests" password "internet4you"
# Configure internet and mitmproxy
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
sysctl -w net.ipv4.conf.all.send_redirects=0
iptables -t nat -A PREROUTING -i wlp61s0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i wlp61s0 -p tcp --dport 443 -j REDIRECT --to-port 8080
ip6tables -t nat -A PREROUTING -i wlp61s0 -p tcp --dport 80 -j REDIRECT --to-port 8080
ip6tables -t nat -A PREROUTING -i wlp61s0 -p tcp --dport 443 -j REDIRECT --to-port 8080
# Start mitmproxy
mitmproxy --mode transparent --showhost
The Pico shows up in the hotspot’s network:
$ arp
Address HWtype HWaddress Flags Mask Iface
10.42.0.42 ether 28:cd:c1:0a:e3:9f C wlp61s0 ...
And the following shows up on the UART output:
...
Connecting online...
Connected
Updating current time...
Welcome! Curent time: 2023-10-30 19:57:39 . Have you thought how much time you wasted solving this?
I chose a number.
What number is it? > �
A single request goes to http://just-the-time.appspot.com/. To catch the response with mitmproxy, configure an intercept filter (i
) to catch all responses (~s
). When the response shows up in the flow, navigate to it, edit (e
) the response_body (b
), press q
to go back and a
to send it.
Knowing the URL it fetches from, we can also set up the local DNS service such that the domain goes to 127.0.0.1
instead:
$ cat /etc/hosts 127.0.0.1 just-the-time.appspot.com
$ cat /etc/NetworkManager/dnsmasq-shared.d/hosts.conf address=/.com/10.42.0.1
Once set up, run sudo systemctl reload NetworkManager.service
to reload the config.
Now we can host our own reply server and don’t have to man-in-the-middle it anymore. This can be done with a simple Python script:
import http.server
class VerboseHTTPServer(http.server.HTTPServer):
def __init__(self, server_address, RequestHandlerClass):
super().__init__(server_address, RequestHandlerClass)
self.RequestHandlerClass.server_version = "VerboseHTTP/1.0"
def get_request(self):
= super().get_request()
client, client_address print(f"Received request from {client_address[0]}")
return client, client_address
class VerboseHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b'AAAAAAAAAAAAAAAAAAAAAAA')
if __name__ == '__main__':
try:
= VerboseHTTPServer(('0.0.0.0', 80), VerboseHandler)
server
server.serve_forever()except KeyboardInterrupt:
print('Server stopped.')
Choosing a number
Back to the wired communication.
What number is it? >
Inputting numbers gives a reply Try again..
, inputting something non-numeric gives NaN
. Prefixing a hexadecimal number with 0x
seems to make it accept it as a number as well, as there is no NaN
reply. The same holds true for prefixing a binary number with 0b
.
For some reason there is a short pulse on the UART lines that gets intepreted as a 0xfe
byte, which is the non-printable � shown below. This byte did not seem to have any meaning. It does make it such as that the first input always results in the NaN
response, as it cannot parse the input string containing this byte into a number.
What number is it?
> �1
NaN
I chose a number.
What number is it?
> 1
Try again..
I chose a number.
What number is it?
>
NaN
My first guess was that the value fetched from just-the-time.appspot.com is used as some kind of seed for the number to be input. Knowing Sergei, and the fact that 0x
and 0b
strings also get parsed into numbers, he probably used MicroPython to implement this. So I took a look at how random number generation is implemented there, and the seed can be set. Unfortunately, unlike normal Python’s seed
function, MicroPython’s seed function does not accept any arbitrary string, only a number. There is a sytem in MicroPython’s time
implementation to go from a tuple of date information to a timestamp, so perhaps this is how the time string is converted into a number. I tried setting the string to 1970-01-01 00:00:00
to make this potential seed 0.
The next question is how this number might be generated. I could not find any hint or suggestion as to how, so I tried a few things on a second Pico running MicroPython as well. I tried generating values of various bit-length using getrandbits
, and various ranges using randint
. None of this resulted in a number that gives a different reply.
Since the output very explicitly says I chose a number
, maybe random.choice
is used? Maybe by chosing a random value from the string returned by the GET request. I tried returning AAAAAAA
as the date string, and providing 0x41
as number input. This, again, did not result in any different reply either.
The hovering coil
Next I took a look at the coil suspiciously hanging over the RP2040. Perhaps it provides some side-channel information about what is going on?
At home I have a small Picoscope 2204A (10MS/s, 8kS memory). If there is some electromagnetic signal coming from the coil, it might be possible to pick it up by holding a short piece of turned wire over it, connected to one of the Picoscope probes. The result of this budget setup is shown in Figure 2; the piece of blue wire is the improvised measurement coil.
This very simple setup seems good enough to show that there is something going on between the messages Try again...
and I chose a number
, as shown in Figure 3. This looks suspiciously like the classic RSA simple power analysis example, where depending on whether a bit in the private key is 1 or 0, the operation of the algorithm that depends on the value of that bit leaks information through power consumption for a longer or shorted time, respectively.
So if the longer blocks in Figure 3 mean a 1, and shorter blocks mean a 0, perhaps the number is 0b1101100111000100011010
, which is 3567898 in decimal.
...
> 3567898
Finally!
You have guessed the number correctly! ...
...
> 0b0b1101100111000100011010
Finally!
You have guessed the number correctly! ...
Either way provides us with the You have guessed the number correctly!
reply! Next, we are presented with the following password input question:
You have guessed the number correctly!
Enter password to authenticate: >
The glass tube
So far we have not looked at the glass tube yet. It is connected between the Pico’s 3V3 output, and a GPIO, so it probably makes the GPIO read 1 or 0. Figure 4 shows a closer up picture of the thing.
After some reverse image searching on Google, I found a forum post where they mentioned that this is called a reed switch. These are switches that get pulled together (or apart) when a magnetic field is nearby. Using my cheap microscope, you can see in Figure 6 that indeed, when you hold a magnet nearby, the two plates come together.
Powering the board while holding a magnet holding next to the switch adds some extra output! However, holding it there too long however triggers the second line as shown below, claiming a reboot.1
...
***Hint*** Holdnig the magnet can give you hints sometimes ***Hint*** Magnet sensor is triggered. Rebooting..
There also is a hint for the number guessing, but at this point we already figured that one out.
***Hint*** Guess it! I cannot wait any langer ***Hint***
I chose a number.
What number is it? >
Playing around with the magnet around the point where we are now, the password guessing, the following hint pops up:
***Hint*** Checking password in a blink of an eye ***Hint***
Enter password to authenticate: >
While messing with the magnet during this password check, sometimes a different message pops up. These all seem to be amazing magnet puns!
***Hint*** Did you hear about the magnetic sensor's party? It had a very
attractive guest list! ***Hint***
***Hint*** What do you call a magnetic sensor with a great sense of
humor? A real 'attract-ion'! ***Hint***
***Hint*** Why did the magnetic sensor apply for a job? Because it
wanted to attract success! ***Hint***
***Hint*** What's a magnetic sensor's favorite kind of music? Heavy metal, of course! ***Hint***
But not useful to where we are now.
Maybe the “blink” refers to the LED?
Blinky LED
The onboard LED is on when the board is powered on. After typing some random inputs for the password, the LED seems to faintly, quickly, turn off and on.
This happens as soon as you send exactly 21 characters. I made a script on a second Pico to see if there is any noticable difference in timing between sending the \r
to send the input, and receiving the first byte from the reply. With this I started looking for other lengths that result in different lengths:
Only 21 characters stand out; all other amount of characters result in ~30000 CPU ticks between \r
and first reply byte.
21: {'avg': 397759.9, 'std': 207.689, 'var': 43134.74} ...
I used the same script to see if trying sll possible ASCII bytes at all different locations in the 21 character long password resulted in a longer timing, but this did not show anything useful.
Maybe the LED contains some more information? We cannot measure the LED voltage directly, as it is covered in epoxy. I tried making a slowmotion capture for a few tries, but this did not show anything. Maybe some kind of photodetector is needed? I don’t have that unfortunately…
After being stuck for a while, my friend Thomas pointed out that you can also use an LED as a photodetector. Apparently, an LED is sensitive to wavelengths equal to or shorter than the wavelength it emits. I had some yellow LEDs laying around, so this might work!
With the LED hooked up to a Picoscope probe, the blink is indeed visible on the scope output:
And, with the script in Listing 1 to run through all the possible bytes, and checking what shows up on the scope, the blink gets slightly longer for one of the bytes:
And, once we get all 21 bytes correct, the blink is the longest:
This shows that the password is T1me_i5_not_c0nstant^
, which indeed gives us the next output:
Password is correct!
Updating current time...
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 1Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
LagaLagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
LLagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
Lagavulin 16 is an exquisite whiskey - wait until 2057 to let it age until 50 YO. See you later!
LaLagavulin 16 ...
A date! This brings us back to the HTTP request from earlier. Maybe if we set the year to 2057, we move on?
Welcome! Curent time: 2057-11-16 22:44:13 . Have you thought how much time you wasted solving this?
I chose a number.
What number is it?
> �3567898
NaN
I chose a number.
What number is it?
> 3567898
Finally!
You have guessed the number correctly!
Enter password to authenticate:
> T1me_i5_not_c0nstant^
Password is correct!
Updating current time...
Finally, it is year 2057! Send me a text, and let's have some whiskey!
https://drive.google.com/file/d/14QkjdSn_cTc_aWjrnAiFNW-FRLCPN9_7/view
https://drive.google.com/file/d/14QkjdSn_cTc_aWjrnAiFNW-FRLCPN9_7/view ...
Yes! The link provides a picture, leading us to some treasure?
A bottle!
Lagavulin 16!
Tools used
- Laptop
- Hakko FX-888D
- Picoscope 2204A
- Andonstar AD208
Footnotes
But it does not actually reboot, just hang :(↩︎