4. Secret Location - Base: Beginners Quest Google CTF Writeup

4. Secret Location - Base: Beginners Quest Google CTF Writeup

Prerequisites:

If you do not have the below knowledge, you will not understand the solution.

  • You need a good understanding of binary.
  • You need to understand logic gates.
  • You need to be able to read code.
  • You need to understand bitwise functions

Recon:

When we extract the zip files we get two files, "pico.uf2" and "chal.c". A quick google tells us that the "pico.uf2" is a firmware file for the Raspberry Pi Pico. Although I would love to play around with it, I do not have a Raspberry PI Pico. We can also tell that "chal.c" is a c file. We can assume that the "pico.uf2" is a compiled version of "chal.c".

Understanding the code:

"chal.c" has the below code in it.

#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);
        etc..
        etc..

Breaking the code apart

Let's break apart this code bit by bit.

#include

#include <stdbool.h>
#include "hardware/gpio.h"
#include "hardware/structs/sio.h"
#include "pico/stdlib.h"

The above code is the standard way of including libraries or user-made code. We do not need these libraries downloaded.

The binary number initiator

    for (int i = 0; i < 8; i++) { // loops 7 times and intialises gpio pin 0-7
        gpio_init(I);  // Initialise a GPIO Pin i
        gpio_set_dir(i, GPIO_OUT);  //Set's the GPIO pin as OUPUT
    }
    gpio_put_all(0);  //Set's all the pins to 0

The above code initialises an 8-bit binary number from GPIO pin 0-7. All these GPIO pins are set to output only. Their default values are all set to 0 by default.

The encoded message

for (;;) {// for (;;) means it goes forever
        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);
        etc..
        etc..

We can guess that the "gpio_set_mask" and "gpio_clr_mask" are the functions that are generating a binary number. The "sleep_us" function is most likely a pause for us to read the binary output. We can then assume that the binary output is a number for a Unicode character.

This is where the real challenge is. In this code, we have 3 functions: "gpio_set_mask", "gpio_clr_mask" and "sleep_us".

Function sleep_us

The "sleep_us" function makes the code pause for a certain amount of milliseconds.

Funcition gpio_set_mask

According to the documentation, "gpio_set_mask" does "Drive high every GPIO appearing in the mask." Let's break this down. This is when bitwise functions come into play. "Drive high" means turning the pin on/1. "GPIO" refers to the 8-bit binary number we created earlier. "Mask" defines what bits you want to clear or put in.

Note: The mask gets turned into a binary number before any operation occurs. This is when bitwise functions come in. We can deduce that the above description is for the bitwise function of "OR". An example is given below:

OperationValue
1101 0111
OR1101 000 <- bitmask
=1101 0111

Function gpio_clr_mask

According to the documentation, "gpio_clr_mask" does "Drive low every GPIO appearing in the mask." Let's once again break this down. "Drive low" means turn off or set to 0. "GPIO" refers to the 8-bit binary number we created early. "Mask" defines what bits you want to clear or put in.

We can deduce from this statement that the two required bitwise functions are GPIO "AND" "NOT" MASK. An example is given below where MASK equals 87 and the original value is 24.

This is a table for the "NOT" function

Operation (NOT)Value
0101 0111
NOT
MASK =1010 1000

This Inverted BITMASK is the output of the above bitwise function. This is a table for "AND".

Operation (AND)Value
INITAL VALUE1001 1011
INVERTED BITMASK1010 1000
Result1000 1000

Solutions

Syntax

Before we code the solution we need to know the syntax for bitmask operations in python.

OPERATORDESCRIPTIONSYNTAX
&Bitwise ANDx & y
|Bitwise ORx | y
~Bitwise NOT~x
^Bitwise XORx ^ y
>>Bitwise right shiftx>>
<<Bitwise left shiftx<<

SOURCE: geeksfromgeeks

The Code

Now we can code the solution. I am writing up this solution in Python because it is easy. First off you can just paste the encoded message in as it is perfectly fine Python syntax.

...
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);
etc..
etc..

Now we need to create the 3 separate functions. In the "gpio_set_mask" function, all we need to do is create a function that does the bitmask operation of "OR" between gpio and the mask. The Python syntax for "OR" is "|".

gpio = 0
def gpio_set_mask(m):
    global gpio
    gpio |= m
...

In the gpio_clr_mask function, we need to use the bitmask function of GPIO AND NOT M. This can be done in python with the & and ~ representing AND and NOT respectively.

...
def gpio_clr_mask(m):
    global gpio
    gpio &= ~m
...

For the sleep_us function, all we need to do is print out the letter output of the binary GPIO.

def sleep_us(_):
    print(chr(gpio), end="")

Putting this all together we get the python code.

Full Code

gpio = 0

def gpio_clr_mask(m):
    global gpio
    gpio &= ~m
def gpio_set_mask(m):
    global gpio
    gpio |= m
def sleep_us(_):
    print(chr(gpio), end="")


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);THING
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);

CTF Flag

The CTF Flag is: CTF{be65dfa2355e5309808a7720a615bca8c82a13d7}

Closing notes

If you want to try this out yourself go here.

If you want to see an expanded version of the python script go here.

If you want to see all the files in the task go here.

If you want to see some of the other writeups for Google Beginner CTF go here.