You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
9.8 KiB
350 lines
9.8 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",
|
|
]
|
|
aliases = [
|
|
"4"
|
|
]
|
|
+++
|
|
|
|
## 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
|
|
|
|
[attachment.zip](/files/writeups/google-ctf/2021/beginners-quest/4/attachment.zip)
|
|
|
|
{{< code language="c" title="chal.c" isCollapsed="true" >}}
|
|
|
|
```c
|
|
#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](https://raspberrypi.github.io/pico-sdk-doxygen/group__hardware__gpio.html):
|
|
|
|
{{< 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.
|
|
|
|
```py
|
|
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:
|
|
|
|
```py
|
|
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" >}}
|
|
|
|
```py
|
|
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}`.
|
|
|