Google CTF 2019 Beginner's Quest writeup: Part 1

Published on 2019-09-22 by rkevin

I really like the Beginner's Quest section in Google's CTFs. They're easy enough so I can solve them in a few days, and hard enough that I can bash my head in for a bit and feel the pain, which makes finally solving the challenge so much better. It's not something as simple as finding a command line tool and use it, but it's not too hard so I can't make progress. Highly recommended.

I also have a "writeup" for 2018's beginner's quest, but it's in video format, 1.5hrs long and not scripted. That's why I'm going to put the 2019 writeup in actual text format. I'm also experimenting with putting pictures in this blog, and I wrote a script that pastes my screenshot here directly. We'll see how this goes.

This is going to be more of an "explore with me" type post, where I'll try my best to document everything I've done, including the failures. If you want a more straightforward "here's how to solve it" writeup, theres a lot of good ones online. It has been a while since I've solved the challenges, though, so my memory is not perfect and I'll skip some of the really dumb parts.

Please check the challenges out before reading this. This post will contain spoilers (obviously), and if you haven't seen the challenges yet you might be confused.

With all of that out of the way, let's get started!

1

Enter Space-Time Coordinates

First challenge! We get to download two files, log.txt and rand2. rand2 is a binary executable. Let's fire it up in Ghidra and see what we find...

2

... well that was anticlimactic. We didn't have to solve anything, and the flag is just there unobstructed. We could've just used strings to find it as well, I assume.

3

Yep. That said, let's dig a little deeper and see what the program does. Looking at Ghidra this program doesn't seem that malicious, so let's run it!

4

It seems that we can enter some coordinates and if we can guess CTF's fancy redacted coordinates, we're in. How are the coordinates checked?

5

Ghidra tells us it uses the next_destination() function to determine where everyone's coordinates are. What's in there?

6

Uh oh... this seems like a random number generator. Sure enough, if we run the program again we see all the coordinates have changed:

7

That said, not all hope is lost. We see that the seed is preserved across calls to next_destination, and the next random number purely depends on the last one. With that, we can easily predict a future random number just based on the current one!

Let's abuse GDB to let the binary do the job for us. We can set the seed variable to a known value, and call next_destination in GDB directly. We'll fire up GDB first.

8

Now that the program is running, we can set seed to our own value. From ghidra we know seed has type time_t, since it's storing the value by time(). This is equivalent to long long on my system. Unfortunately, GDB doesn't know that:

9

so we'll have to try getting its address, casting it into a long long* and assigning a value that way.

10

It works, and we can get the next address! Using this we can find the right coordinates just based on the program's output alone.

11

We got some coordinates! Let's see if they work...

12

Uh-oh! Something went wrong. Let's check the code again...

13

Huh, interesting. The program generates the y-coordinate first, then the x-coordinate. That means we should use the last x-coordinate as the last random number. Let's try again:

14

Tada! We got it.

Satellite

We got two files again, a README.pdf and a init_sat which is an executable. Running it, it needs some user input that we can't provide.

20

Oh well. Standard procedure, let's fire it up in ghidra.

15

Uh... hello? It's been 2 minutes? Ghidra are you still there?

16

Welp. We're experiencing technical difficulties, please hold.

After ignoring all the errors, we see an enormous list of functions. Whatever this is, this is not straight gcc-compiled C code. We can still find a main function, although it is called main.main:

17

This looks terrifying. However, we did find some hints. There are references like go.itab.*os.File.io.Reader littered all over the place. Go is a programming language, and io.Reader is a type used for basic IO. Could this be a go program?

18

Yep. Can't believe I missed that on the first go(pun not intended). Now, how do we reverse Go programs? We could use ghidra, but it looks complicated. After some googling, I found that go has its own version of objdump: go tool objdump. Let's run that and see what happens:

19

We can see some nice disassembly, but it's not really helping that much. Well, it's a good addition to our toolkit, I guess. Time to go back to ghidra.

Note: I think I solved the challenge by looking at the disassembly rather than ghidra, but we're doing it the easy way.

Looking back at ghidra, while we don't know what each line is doing, we can get a general idea. The bulk of code is wrapped in a while(true) loop:

21

We can see some important function calls. There's a Fprint that presumably prints stuff to stdout, a bufio call that does something to IO(maybe input?), and some if statements that lead either to break;, a call to main.connectToSat, or another Fprintln.

If the code takes the break; route, it breaks out of the while(true) and prints out a message before quitting. That if statement is comparing against a constant:

22

which looks oddly suspicious, like ASCII. (Quick hint: if you see a bunch of hex values from 0x30~0x7F, it's probably ASCII. 0x30~0x39 are numbers, 0x41~0x5a are uppercase letters, and 0x61~0x7a are lowercase letters.) Let's convert it into ASCII, reversing the bytes because little endianness:

23

Huh! This is the "type exit to quit" part! That means the next if statement leading to main.connectToSat is the part we want!

24

Wait, that seems oddly familiar...

25

26

...

27

It turns out that the answer is written in the README.pdf all this time. Note to future rk: actually read everything before you jump in. Thanks, current rk.

Anyways, with that out of the way, we can get to the main.connectToSat part!

28

Hmm, interesting. The URL leads to some base64.

29

What? The text is jumping ahead in my script! I'm very sure the text said something different during the CTF, something along the lines of "good luck, but your flag is not here". I'll talk about the wireshark thing later, because I didn't solve it that way.

Anyway, time to move back to ghidra since we can't find anything. What's in the main.connectToSat function?

30

It's a lot of stuff that I can't fit into one screenshot, so I'll just list some logic and function calls here, ignoring the rest. Usually when trying to figure out program logic, the ifs, loops and calls are the most important, and the rest are just details you can dig through later once you have a general understanding.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
do{
runtime.convI2I()
if(){
runtime.makeslice()
}
bufio.(something) #some sort of input?
runtime.slicebytetostring()
regexp.MustCompile()
regexp.(something)
runtime.convTstring()
fmt.Fprintln()
runtime.makeslice()
bufio.(something) #some sort of input?
runtime.slicebytetostring()
if(){
if(){
}
fmt.Fprintln()
}
runtime.stringtoslicebyte()
} while()

Hmm. The slicebytetostring stuff and makeslice stuff seem standard, because Go deals with them a lot. However, the regexp stuff is very odd. When would you need a regular expression? What is it even matching for?

31

Hmmmmm. This seems extremely suspicious. CTF{\S{40}} would match against anything that looks like CTF{40 characters here}. Why would you need to match the flag? Let's try to modify the binary so it doesn't match the flag anymore, and see if that breaks anything. We can find the regex inside the binary:

32

We can just use a hex editor to change the string.

33

There we go. Now it shouldn't match CTF{} flags. Let's run it again:

34

We got the flag! The flag is probably stored somewhere, and there's a regex that changes the flag to ***s.

After the CTF was over, I was reading some writeups before I realized most people used a completely different way to solve the challenge. They realized that the program was connecting out to the internet to grab the text. Turns out the "connecting to satellite" was actually a connection, who would've thought! I opened Wireshark, started sniffing for packets before entering osmium, and sure enough!

35

The flag was there! No binary modification necessary.

Feedback

Before I write more, I want to get some feedback. This is the first time I'm doing this. Am I being too verbose? Too unorganized? Too confusing? Anything you like and don't like? I'd really appreciate some feedback. Don't worry, I don't bite. Unless I'm eating.

I'll write about the other challenges soon. I plan on at least finishing every challenge in Beginner's Quest 2019, and if enough people want more I'll port some of my old writeups to blog format.

Again, let me know what you think! please