Tags: misc
Rating:
Welcome to the Beginner's Quest. The Beginner's Quest is aimed for new CTF-goers who may not feel ready for the main event.
These writeups are done by Team IrisCS skat, also known as Shawn Duong. You can find my website at shawnduong.github.io and you can email me at shawnduong@pm.me.
I hope you enjoy reading these writeups as much as I enjoyed writing them!
Links: golden disc
Alright, let's get started! It looks like we're given a zip file. Let's go ahead and unzip it and see what we're dealing with.
I first downloaded the challenge off of the website. Because it's a zip file, I use the unzip
utility to extract the contents of the file into my current working directory. Then, I listed all of the files in my current working directory using ls
, and then found out what types of files I'm dealing with by using the file
utility and passing the *
wildcard, which essentially means "everything," as in I want to find out the filetype of everything.
One of the files, rand2
, is an ELF. ELF stands for Executable and Linkable Format, which basically means that it's an executable program file. In order to execute it, we need to set the appropriate mode, so we use the chmod
utility (change mode) and pass +x
(+executable) to specify that we want to make this file executable.
The other file, log.txt
, is an ASCII text file. ASCII stands for the American Standard Code for Information Interchange, which basically means that it's readable text that we can understand as humans and not a bunch of binary machine jargon. We can read it using the cat
utility.
Let's go ahead and run rand2
to see what it does.
It looks like it takes two inputs and then performs a comparison on them.
During debugging, we can have a look at the value of registers involved in comparisons and directly set them to certain values in order to ensure that a comparison is done between two equal values.
Let's get started by debugging the program through gdb
, the GNU Debugger.
We're going to set a breakpoint at main
using break
, so the program will halt execution when it reaches that function. Then, we're going to run the program using r
. Once it reaches the breakpoint, it will halt execution. We're then going to perform a disassembly on the current stack frame to view the instructions underneath using disas
.
Here's the full disassembly.
(gdb) disas
Dump of assembler code for function main:
=> 0x0000555555554872 <+0>: push rbp
0x0000555555554873 <+1>: mov rbp,rsp
0x0000555555554876 <+4>: push rbx
0x0000555555554877 <+5>: sub rsp,0x38
0x000055555555487b <+9>: mov DWORD PTR [rbp-0x34],edi
0x000055555555487e <+12>: mov QWORD PTR [rbp-0x40],rsi
0x0000555555554882 <+16>: mov edi,0x0
0x0000555555554887 <+21>: call 0x5555555546e0 <time@plt>
0x000055555555488c <+26>: mov QWORD PTR [rip+0x200805],rax # 0x555555755098 <seed>
0x0000555555554893 <+33>: lea rdi,[rip+0x229] # 0x555555554ac3
0x000055555555489a <+40>: call 0x5555555546b0 <puts@plt>
0x000055555555489f <+45>: mov QWORD PTR [rbp-0x18],0x0
0x00005555555548a7 <+53>: jmp 0x555555554946 <main+212>
0x00005555555548ac <+58>: mov rax,QWORD PTR [rbp-0x18]
0x00005555555548b0 <+62>: lea rdx,[rax*8+0x0]
0x00005555555548b8 <+70>: lea rax,[rip+0x2007a1] # 0x555555755060 <destinations>
0x00005555555548bf <+77>: mov rdx,QWORD PTR [rdx+rax*1]
0x00005555555548c3 <+81>: mov rax,QWORD PTR [rbp-0x18]
0x00005555555548c7 <+85>: mov rsi,rax
0x00005555555548ca <+88>: lea rdi,[rip+0x205] # 0x555555554ad6
0x00005555555548d1 <+95>: mov eax,0x0
0x00005555555548d6 <+100>: call 0x5555555546c0 <printf@plt>
0x00005555555548db <+105>: mov rax,QWORD PTR [rbp-0x18]
0x00005555555548df <+109>: lea rdx,[rax*8+0x0]
0x00005555555548e7 <+117>: lea rax,[rip+0x200772] # 0x555555755060 <destinations>
0x00005555555548ee <+124>: mov rax,QWORD PTR [rdx+rax*1]
0x00005555555548f2 <+128>: lea rsi,[rip+0x1c6] # 0x555555554abf
0x00005555555548f9 <+135>: mov rdi,rax
0x00005555555548fc <+138>: call 0x5555555546d0 <strcmp@plt>
0x0000555555554901 <+143>: test eax,eax
0x0000555555554903 <+145>: jne 0x555555554913 <main+161>
0x0000555555554905 <+147>: lea rdi,[rip+0x1d5] # 0x555555554ae1
0x000055555555490c <+154>: call 0x5555555546b0 <puts@plt>
0x0000555555554911 <+159>: jmp 0x555555554941 <main+207>
0x0000555555554913 <+161>: mov eax,0x0
0x0000555555554918 <+166>: call 0x55555555481a <next_destination>
0x000055555555491d <+171>: mov rbx,rax
--Type <RET> for more, q to quit, c to continue without paging--c
0x0000555555554920 <+174>: mov eax,0x0
0x0000555555554925 <+179>: call 0x55555555481a <next_destination>
0x000055555555492a <+184>: mov rdx,rbx
0x000055555555492d <+187>: mov rsi,rax
0x0000555555554930 <+190>: lea rdi,[rip+0x1b5] # 0x555555554aec
0x0000555555554937 <+197>: mov eax,0x0
0x000055555555493c <+202>: call 0x5555555546c0 <printf@plt>
0x0000555555554941 <+207>: add QWORD PTR [rbp-0x18],0x1
0x0000555555554946 <+212>: cmp QWORD PTR [rbp-0x18],0x5
0x000055555555494b <+217>: jbe 0x5555555548ac <main+58>
0x0000555555554951 <+223>: lea rdi,[rip+0x1a0] # 0x555555554af8
0x0000555555554958 <+230>: mov eax,0x0
0x000055555555495d <+235>: call 0x5555555546c0 <printf@plt>
0x0000555555554962 <+240>: lea rax,[rbp-0x20]
0x0000555555554966 <+244>: mov rsi,rax
0x0000555555554969 <+247>: lea rdi,[rip+0x1b5] # 0x555555554b25
0x0000555555554970 <+254>: mov eax,0x0
0x0000555555554975 <+259>: call 0x5555555546f0 <__isoc99_scanf@plt>
0x000055555555497a <+264>: lea rdi,[rip+0x1af] # 0x555555554b30
0x0000555555554981 <+271>: mov eax,0x0
0x0000555555554986 <+276>: call 0x5555555546c0 <printf@plt>
0x000055555555498b <+281>: lea rax,[rbp-0x28]
0x000055555555498f <+285>: mov rsi,rax
0x0000555555554992 <+288>: lea rdi,[rip+0x18c] # 0x555555554b25
0x0000555555554999 <+295>: mov eax,0x0
0x000055555555499e <+300>: call 0x5555555546f0 <__isoc99_scanf@plt>
0x00005555555549a3 <+305>: mov eax,0x0
0x00005555555549a8 <+310>: call 0x55555555481a <next_destination>
0x00005555555549ad <+315>: mov rdx,rax
0x00005555555549b0 <+318>: mov rax,QWORD PTR [rbp-0x20]
0x00005555555549b4 <+322>: cmp rdx,rax
0x00005555555549b7 <+325>: jne 0x5555555549db <main+361>
0x00005555555549b9 <+327>: mov eax,0x0
0x00005555555549be <+332>: call 0x55555555481a <next_destination>
0x00005555555549c3 <+337>: mov rdx,rax
0x00005555555549c6 <+340>: mov rax,QWORD PTR [rbp-0x28]
0x00005555555549ca <+344>: cmp rdx,rax
0x00005555555549cd <+347>: jne 0x5555555549db <main+361>
0x00005555555549cf <+349>: lea rdi,[rip+0x18a] # 0x555555554b60
0x00005555555549d6 <+356>: call 0x5555555546b0 <puts@plt>
0x00005555555549db <+361>: lea rdi,[rip+0x1c6] # 0x555555554ba8
0x00005555555549e2 <+368>: call 0x5555555546b0 <puts@plt>
0x00005555555549e7 <+373>: mov eax,0x0
0x00005555555549ec <+378>: add rsp,0x38
0x00005555555549f0 <+382>: pop rbx
0x00005555555549f1 <+383>: pop rbp
0x00005555555549f2 <+384>: ret
End of assembler dump.
Some interesting instructions to point out here. At main+259
and main+300
, we have scanf
function calls. This is where the program gets our input for the x and y coordinates. At main+322
and main+344
, we have comparisons done between some value (rdx
) and our inputs (rax
).
Let's go ahead and set breakpoints at main+322
and main+344
and continue execution of our program with c
. We can enter in whatever data we want when the scanf
function gets our input for now, since we'll change it later in the debugger. When we approach the breakpoint, we'll find out what our input (rax
) is being compared to (rdx
) by having a look at all of the registers. We can get information about registers using info registers
, or i r
for short.
The program is performing a comparison between our input, decimal 123
or hex 0x7b
, and the decimal 191410283849669
or hex 0xae162df9a7c5
. The comparison between these will obviously result in "not equal", to which we can expect the following instruction located on main+325
to jump execution to main+361
and leave the program without giving us our flag. We can't let that happen, so let's ensure that the comparison evaluates to "equal."
We're going to directly set the rax
register equal to the value of the rdx
register using set
. Then, we're going to continue execution of the program until the next breakpoint, at which we'll do the same thing.
We got it!
Instead of ensuring that the comparisons are equal to each other, we can straight up just jump over them using jump
, essentially skipping them and going straight to the part of the program that prints us our flag.
We're going to, once again, set breakpoints at main+322
and main+344
, as these are where our comparisons are occurring.
However, immediately after approaching a breakpoint, we're going to jump over the following jne
instructions located on main+325
and main+347
to the following instructions located on main+327
and main+349
, respectively.
And just like that, we have the flag.
This one is less fun than the other two, but hey -- it works. A program file doesn't just contain machine instructions, but it also contains other data such as as the things that the instructions might print to the screen. We can use the strings
utility to extract anything remotely human-readable and not machine jargon. We can use this in conjunction with grep
to find the flag. We know that the flag is in some sort of CTF{}
format, so we can specify that and the .*
wildcard (meaning "anything") to come up with an expression for the flag.
Less fun, but it works.
CTF{welcome_to_googlectf}
Next stop: Satellite (Blue)