9.7 KiB
+++ author = "Maik de Kruif" title = "Electronics Research Lab" subtitle = "Beginners Quest 4 - Google CTF" date = 2021-09-24T16:33:25+01:00 description = "A writeup for challenge 4 of the beginners quests of the Google CTF." cover = "img/writeups/google-ctf/2021/beginners-quest/4/cover.png" tags = [ "Google CTF", "Beginners Quest", "ctf", "hacking", "writeup", "web", "hardware", "hw", ] categories = [ "ctf", "writeups", "hw", ] +++
Story line
Secret Location - Base
"Welcome back AGENT. It seems like you've got a marvelous lead that perhaps gives a clue about where you should head to next. Visit the lab, and talk to that Dr. Klostermann, or is it Cloysterman?, he will know how to decrypt the device.. you would think". ... Dr Klostermann: "Welcome to the technical department AGENT, I’m Dr. Klostermann, and this is my assistant, Konstantin. Let’s not waste any time, is that the device that you’re holding in your hand? Konstantin, start the basic procedure."
Challenge: Electronics Research Lab (hw)
Welcome back AGENT. It seems like you got a lead that perhaps gives a clue about where the next journey on your quest goes. Visit the lab, and talk to Dr. Klostermann, he will know how to decrypt the device.
After solving
You’re taking a stroll in the lab, when Dr. Klostermann is calling your name: "Agent, we’ve discovered the origin of the device. This time you won’t be able to reach your destination by air, but by the new Trans-Sibiriean Railway, as opposed to the old one, which runs along side it at the same time, it is a bit odd. And it goes to Shenzhen. I am sorry agent, but the further you go into this task, the more precautions you will have to take, and remember, the enemy can be anyone. It could be a conductor, the engineer, it could even be our own people that will meet you at the spot you need to be at. Be selective with who you trust. I think you got the point, go now, I got much to do. Agent, much depends on you!."
Attachment
{{< code language="c" title="chal.c" isCollapsed="true" >}}
#include <stdbool.h>
#include "hardware/gpio.h"
#include "hardware/structs/sio.h"
#include "pico/stdlib.h"
int main(void)
{
for (int i = 0; i < 8; i++) {
gpio_init(i);
gpio_set_dir(i, GPIO_OUT);
}
gpio_put_all(0);
for (;;) {
gpio_set_mask(67);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(20);
gpio_clr_mask(3);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(16);
sleep_us(100);
gpio_set_mask(57);
gpio_clr_mask(4);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(25);
sleep_us(100);
gpio_set_mask(5);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(18);
gpio_clr_mask(65);
sleep_us(100);
gpio_set_mask(1);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(64);
gpio_clr_mask(17);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(1);
gpio_clr_mask(6);
sleep_us(100);
gpio_set_mask(18);
gpio_clr_mask(65);
sleep_us(100);
gpio_set_mask(1);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(4);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(64);
gpio_clr_mask(16);
sleep_us(100);
gpio_set_mask(16);
gpio_clr_mask(64);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(4);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(3);
sleep_us(100);
gpio_set_mask(9);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(1);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(8);
sleep_us(100);
gpio_set_mask(8);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(65);
gpio_clr_mask(24);
sleep_us(100);
gpio_set_mask(22);
gpio_clr_mask(64);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(5);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(65);
gpio_clr_mask(16);
sleep_us(100);
gpio_set_mask(22);
gpio_clr_mask(65);
sleep_us(100);
gpio_set_mask(1);
gpio_clr_mask(6);
sleep_us(100);
gpio_set_mask(4);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(66);
gpio_clr_mask(21);
sleep_us(100);
gpio_set_mask(1);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(0);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(24);
gpio_clr_mask(65);
sleep_us(100);
gpio_set_mask(67);
gpio_clr_mask(24);
sleep_us(100);
gpio_set_mask(24);
gpio_clr_mask(67);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(8);
sleep_us(100);
gpio_set_mask(65);
gpio_clr_mask(18);
sleep_us(100);
gpio_set_mask(16);
gpio_clr_mask(64);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(0);
sleep_us(100);
gpio_set_mask(68);
gpio_clr_mask(19);
sleep_us(100);
gpio_set_mask(19);
gpio_clr_mask(64);
sleep_us(100);
gpio_set_mask(72);
gpio_clr_mask(2);
sleep_us(100);
gpio_set_mask(2);
gpio_clr_mask(117);
sleep_us(100);
gpio_put_all(0);
sleep_ms(500);
}
return 0;
}
{{< /code >}}
Recon
When opening the attachment, we can find two files: chal.c
and pico.uf2
.
Before this challenge I had never heard of a uf2
file, but from googling "pico.uf2", I found that it is probably a firmware file for the Paspberry Pi Pico.
While I would have loved to play around with that, I didn't have one at hand. So we'll have to do with the chal.c
file (which is probably the file running in the firmware).
chal.c
In this code, we see a lot of calls to the functions gpio_set_mask()
and gpio_clr_mask()
. When looking up what these to, we find this documentation:
{{< figure src="/img/writeups/google-ctf/2021/beginners-quest/4/doc-functions.png" title="Functions documentation" >}}
{{< figure src="/img/writeups/google-ctf/2021/beginners-quest/4/doc-set-mask.png" title="gpio_set_mask() documentation" >}}
Bitmasks
In the gpio_set_mask()
documentation, we see that it wants a bitmask of GPIO values. So what is a bitmask?
A bitmask is used for logical operations to transform an array of bits using another array. For example, a bitmask can be used with OR:
Operation | Value |
---|---|
1001 0101 |
|
OR | 1111 0000 <- bitmask |
= | 1111 0101 |
Solving
To get the values at every sleep()
, we just have to apply the masks one by one.
But before we can do that, we first need the individual values to work with. To get them, I wrote the following python script.
set_mask_str = "gpio_set_mask"
clr_mask_str = "gpio_clr_mask"
set_masks = []
clr_masks = []
with open("chal.c", "r") as file:
for line in file:
set_mask_loc = line.find(set_mask_str)
clr_mask_loc = line.find(clr_mask_str)
end = line.find(")")
if set_mask_loc != -1:
set_masks.append(
int(line[set_mask_loc + len(set_mask_str) + 1:end]))
if clr_mask_loc != -1:
clr_masks.append(
int(line[clr_mask_loc + len(clr_mask_str) + 1:end]))
All this script does, is read the chal.c
file line by line. If the line contains either the gpio_set_mask()
or the gpio_clr_mask()
function, it gets the location of the parameter and appends it to a list.
In the end we get two lists: set_masks
and clr_masks
.
Applying the masks
Now comes the more difficult part, we have to apply these masks.
For setting the masks, we can use the bitwise OR operation (|
). It works like this:
State | Value |
---|---|
Initial value | 1001 0101 |
Bitmask | 1111 0000 |
Result | 1111 0101 |
As we can see in this table, it now set the bytes passed to gpio_set_mask()
to true.
Clearing the masks is a little harder. For this we'll need to first invert the bitmask (~
), and than use the bistwise AND operation (&
) with the current value. It would work like this:
State | Value |
---|---|
Initial value | 1111 0101 |
Bitmask | 1100 0010 |
Invert bismask | 0011 1101 |
Result | 0011 0101 |
If we turn this into a python script, it would look like this:
current_output = 0
flag = ""
for set_mask, clr_mask in zip(set_masks, clr_masks):
current_output |= set_mask
current_output &= ~ clr_mask
flag += chr(current_output)
print(flag.strip())
{{< code language="py" title="Full code" isCollapsed="true" >}}
set_mask_str = "gpio_set_mask"
clr_mask_str = "gpio_clr_mask"
set_masks = []
clr_masks = []
with open("chal.c", "r") as file:
for line in file:
set_mask_loc = line.find(set_mask_str)
clr_mask_loc = line.find(clr_mask_str)
end = line.find(")")
if set_mask_loc != -1:
set_masks.append(
int(line[set_mask_loc + len(set_mask_str) + 1:end]))
if clr_mask_loc != -1:
clr_masks.append(
int(line[clr_mask_loc + len(clr_mask_str) + 1:end]))
current_output = 0
flag = ""
for set_mask, clr_mask in zip(set_masks, clr_masks):
current_output |= set_mask
current_output &= ~ clr_mask
flag += chr(current_output)
print(flag.strip())
{{< /code >}}
Solution
When executing this code, we get the flag! It's CTF{be65dfa2355e5309808a7720a615bca8c82a13d7}
.