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

Search for a command to run...

No comments yet. Be the first to comment.
This is a series of writeups about Google's Beginner's Quest CTF.
This is a write-up to Google's Beginners Quest CTF for questions 1 & 2. Challenge 1 When we first go to the webpage we are greeted with a login page. If we input the wrong password we get an alert that tells us "Wrong Password!". The first thing ...
During in the process of reverse engineering binaries, a common problem arises. How do I reverse engineer stripped binaries? There are no symbols to break on, offsets change, scripts don't work, and you ask yourself why am I doing this? Luckily there...

When I was learning how to tackle pwn challenges in CTFs, I had a tough time finding a single, clear guide that could show me the ropes of actually carrying out these exploits. That's why I decided to put together a complete guide that covers everyth...

Using python to capture and project data

Intro This is a write-up for PicoCTF 2022: Buffer Overflow 1. This is one of my favourite challenges to do. I recommend solving it for yourself before you read this write-up. What is a Buffer Overflow? Before we get started we need to first know what...

What is a BadUSB? A BadUSB is a USB device that acts as a keyboard and injects preprogrammed keystrokes into a computer. A BadUSB is Indistinguishable from a generic keyboard, making it near impossible to detect and patch. You can setup reverse shell...

If you do not have the below knowledge, you will not understand the solution.
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".
"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..
Let's break apart this code bit by bit.
#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.
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.
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".
The "sleep_us" function makes the code pause for a certain amount of milliseconds.
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:
| Operation | Value |
| 1101 0111 | |
| OR | 1101 000 <- bitmask |
| = | 1101 0111 |
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 VALUE | 1001 1011 |
| INVERTED BITMASK | 1010 1000 |
| Result | 1000 1000 |
Before we code the solution we need to know the syntax for bitmask operations in python.
| OPERATOR | DESCRIPTION | SYNTAX |
| & | Bitwise AND | x & y |
| | | Bitwise OR | x | y |
| ~ | Bitwise NOT | ~x |
| ^ | Bitwise XOR | x ^ y |
| >> | Bitwise right shift | x>> |
| << | Bitwise left shift | x<< |
SOURCE: geeksfromgeeks
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.
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);
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.