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.
336 lines
8.9 KiB
336 lines
8.9 KiB
3 years ago
|
+++
|
||
|
author = "Maik de Kruif"
|
||
|
title = "Secret Location - Base"
|
||
|
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",
|
||
|
]
|
||
|
categories = [
|
||
|
"ctf",
|
||
|
"writeups",
|
||
|
"hacking",
|
||
|
]
|
||
|
+++
|
||
|
|
||
|
## Story line
|
||
|
|
||
|
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)
|
||
|
|
||
|
## 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`
|
||
|
|
||
|
{{< 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 >}}
|
||
|
|
||
|
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}`.
|