picoCTF - Low Level Binary Intro Walkthrough
In this playlist, we are presented with Mochi’s Tale. We will be introduced to assembly, debuggers and some Python code that can be exploited in similar ways as assembly code. And there are two reasons why I’m completing this picoCTF playlist before others that are more focused on pentesting stuff:
- I want to learn about reverse engineering and this looks like a nice intro material
- I have a project in mind that requires reverse engineering
Mochi’s Tale
At first we are presented we this game. That’s pretty much so far regarding this game.
Warm Up
Very basic and easy challenges. From binary, hex and decimal translation to other basic commands. You’ll use:
CyberChefcatPythonNetcat
Obedient Cat
Description: This file has a flag in plain sight (aka “in-the-clear”). Download flag.
Just download the document and open it or read it with cat
command and you’ll get the flag.
Flag: picoCTF{s4n1ty_v3r1f13d_2fd6ed29}
Warmed Up
Description: What is 0x3D (base 16) in decimal (base 10)?
Translate 0x3D into decimal and you’ll get 61.
Flag: picoCTF{61}
ASCII Numbers
Description: Description Convert the following string of ASCII numbers into a readable string:
1
2
3
4
0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e
0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c
0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33
0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d
To solve this challenge you can just go to CyberChef and translate it with From Hex recipe.
Flag: picoCTF{45c11_n0_qu35710n5_1ll_t311_y3_n0_l135_445d4180}
Picker I
Description: This service can provide you with a random number, but can it do anything else? Connect to the program with netcat: $ nc saturn.picoctf.net 57630
. The program’s source code can be downloaded here.
To solve this challenge you can use cat
command to check the source code and see what else you can do. There’s a function, win(), that prints the flag iin Hex. Once we have the Hex we can repeat the step in the previous challenge.
Flag: picoCTF{4_d14m0nd_1n_7h3_r0ugh_6e04440d}
Intro to Assembly
The next set of challenges are not difficult but I found them a bit messy at the beginning as it is my first time with assembly. You’ll use:
GoogleCyberChefcatPythonCalculator
Bit-O-Asm-1
Description: Can you figure out what is in the eax
register? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Download the assembly dump here.
cat
the dump to see the code. As we can see, eax
is replaced with 0x30
. Now we only need to translate that to decimal with CyberChef using From Hex + To Decimal as the recipe and will get the flag.
Flag: picoCTF{48}
Bit-O-Asm-2
Description: Can you figure out what is in the eax
register? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Download the assembly dump here.
To solve this challenge just have a look to the dump and follow the trail: eax
has been replaced by DWORD PTR [rbp-0x4]
(a pointer) and this pointer was previously replaced with 0x9fe1a
. Google the translation from 0x9fe1a
to decimal and you’ll get 654874
– the flag.
Flag: picoCTF{654874}
Bit-O-Asm-3
Description: Can you figure out what is in the eax
register? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Download the assembly dump here.
This is the interesting part of the dump:
1
2
3
4
5
<+15>: mov DWORD PTR [rbp-0xc],0x9fe1a
<+22>: mov DWORD PTR [rbp-0x8],0x4
<+29>: mov eax,DWORD PTR [rbp-0xc]
<+32>: imul eax,DWORD PTR [rbp-0x8]
<+36>: add eax,0x1f5
To solve this challenge we to do a bit of maths. Once we have followed the trail and translated everything, we can calculate the final number. These are the steps:
eax
value is the pointerDWORD PTR [rbp-0xc]
which value is0x9fe1a
and this equals to654874
in decimal.- Now we have to multiply
eax
by the pointerDWORD PTR [rbp-0x8]
which value is0x4
and this equals to4
in decimal. - So we have
654874
multiplied by4
that equals to2619496
and this value gets stored ineax
. - And the last operation is to add
0x1f5
that equals to501
to the value stored ineax
. The result is2619997
which is our flag.
Flag: picoCTF{2619997}
Bit-O-Asm-4
Description: Can you figure out what is in the eax
register? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Download the assembly dump here.
This is the interesting part of the dump:
1
2
3
4
5
6
7
<+15>: mov DWORD PTR [rbp-0x4],0x9fe1a
<+22>: cmp DWORD PTR [rbp-0x4],0x2710
<+29>: jle 0x55555555514e <main+37>
<+31>: sub DWORD PTR [rbp-0x4],0x65
<+35>: jmp 0x555555555152 <main+41>
<+37>: add DWORD PTR [rbp-0x4],0x65
<+41>: mov eax,DWORD PTR [rbp-0x4]
To solve this challenge we to do a bit of maths. Once we have followed the trail and translated everything, we can calculate the final number. These are the steps:
- First, we have the pointer
DWORD PTR [rbp-0x4]
pointing to the value0x9fe1a
that equals to654874
in decimal. - Now we are comparing the value of the pointer
DWORD PTR [rbp-0x4]
against the second value,0x2710
that equals to10000
:654874
>10000
. - We skip the
jle
as it is a jump conditional if less or equal to andDWORD PTR [rbp-0x4]
is bigger than0x2710
. - The next line is a
sub
operation. We have to substract0x65
that equals to101
in decimal toDWORD PTR [rbp-0x4]
. The result is654773
and gets stored inDWORD PTR [rbp-0x4]
. - Now we have an inconditional
jmp
that takes us to the line with the offset<+41>
. In this line we assign the value ofDWORD PTR [rbp-0x4]
toeax
.
Flag: picoCTF{654773}
Intro to Debuggers
Now we are taking our first steps with debuggers. It is recommended to use GDB – I believe it comes pre-installed in Kali Linux. You’ll use:
gdbGoogle
GDB baby step 1
Description: Can you figure out what is in the eax
register at the end of the main function? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Disassemble this.
We need to disassemble the file provided. To start dbg
we only need to use the command gdb debugger0_a
. Once we are dubugging the file, we can disassemble it with the command: disassemble main
to disassemble the main
function. This is the output:
1
2
3
4
5
6
7
8
0x0000000000001129 <+0>: endbr64
0x000000000000112d <+4>: push %rbp
0x000000000000112e <+5>: mov %rsp,%rbp
0x0000000000001131 <+8>: mov %edi,-0x4(%rbp)
0x0000000000001134 <+11>: mov %rsi,-0x10(%rbp)
0x0000000000001138 <+15>: mov $0x86342,%eax
0x000000000000113d <+20>: pop %rbp
0x000000000000113e <+21>: ret
As we need the value of eax
, we only need to translate 0x86342
to decimal and we’ll get the flag.
Flag: picoCTF{549698}
GDB baby step 2
Description: Can you figure out what is in the eax
register at the end of the main function? Put your answer in the picoCTF flag format: picoCTF{n}
where n is the contents of the eax
register in the decimal number base. If the answer was 0x11
your flag would be picoCTF{17}
. Disassemble this.
This challenge is pretty easy. First, as we will be running the file, you must grant execution permission to the file with chmod
: chmod 777 debugger0_b
. Then you can load the file in GDB and disassemble it as before. Now we can start with our dynamic analysis:
break *main+59
: This sets a breakpoint right after the loop.run
: This runs the program until the breakpoint.info registers eax
: This prints out the value of theeax
register after running the program until the breakpoint so we will get the value ofeax
after finishing the loop in hex and decimal.
Flag: picoCTF{307019}
GDB baby step 3
Description: Now for something a little different. 0x2262c96b
is loaded into memory in the main
function. Examine byte-wise the memory that the constant is loaded in by using the GDB command x/4xb addr
. The flag is the four bytes as they are stored in memory. If you find the bytes 0x11 0x22 0x33 0x44
in the memory location, your flag would be: picoCTF{0x11223344}
. Disassemble this.
This challenge is not difficult but a bit tricky as we need to deal with little endianness. The steps begin after giving permissions, loading and dumping the file main
:
- First we need to add a break as before right after the value loaded into the main function with
*main+22
. - After this we need to run the program with
run
. - Once we are at the breakpoint we should examine the memory where the constant is loaded in using the command
x/4xb $rbp-0x4
.
We will get the following output:
1
0x7fffffffddbc: 0x6b 0xc9 0x62 0x22
So our flag would be 0x6bc96222
. As you can see, it’s the reverse (byte-wise speaking) of the given number at the beginning: 0x2262c96b
. That’s little endianness.
Flag: picoCTF{0x6bc96222}
GDB baby step 4
Description: main
calls a function that multiplies eax
by a constant. The flag for this challenge is that constant in decimal base. If the constant you find is 0x1000, the flag will be picoCTF{4096}
. Disassemble this.
The steps begin after giving permissions, loading and dumping the file main
:
- First we need to add to breakpoints: one before the function call
break *main+36
and another one after the function callbreak *main+43
. - Now we need to
run
the program until the first breakpoint and get the value ofeax
:info registers eax
and we will get654
. - Now we should
continue
the program until the next breakpoint and get the value ofeax
again but this time would be after the function:info registers eax
and we will get8439870
. - Now, simply dividing
8439870
by654
will get us the value of the constant:12905
.
Flag: picoCTF{12905}
ASCII FTW
Description: This program has constructed the flag using hex ascii values. Identify the flag text by disassembling the program. You can download the file from here.
For this challenge we need to use many of the things we have learnt so far. As before, the steps begin after giving permissions, loading and dumping the file main
:
- If we examine the file, we can see some blocks with hex data – that’s our flag.
- First we have to add a breakpoint at the end of this hex values:
break *main+151
. - Now we have to run the program:
run
. - Once we are at the breakpoint we can examine the memory in
strings
format:x/1sb $rbp-0x30
.
With this command we are examining the data once from the memory address -0x30(%rbp)
.
Flag: picoCTF{ASCII_IS_EASY_7BCD971D}
Interlude
Now we are going back to Python. You’ll use now:
catPythonNetcat
Picker II
Description: Can you figure out how this program works to get the flag? Connect to the program with netcat: $ nc saturn.picoctf.net 62807
The program’s source code can be downloaded here.
This Python script is pretty similar to the first Python script we saw. The only difference is that we cannot use win
to print the flag as there is a filter that doesn’t allow us to use it. BUT we can do the things that are inside the win() function: print(open('flag.txt', 'r').read())
Flag: picoCTF{f1l73r5_f41l_c0d3_r3f4c70r_m1gh7_5ucc33d_0b5f1131}
Picker III
Description: Can you figure out how this program works to get the flag? Connect to the program with netcat: $ nc saturn.picoctf.net 57549
The program’s source code can be downloaded here.
Now we have the same python script but modified. The program limits the functions we can call to a table of names. However, this table is a variable and in the table is included a function called “write_variable”. So, abusing this gap will help us include the win() function into this table. We must have two precautions:
- Maintain the string length
- Write the new variable value with quotes
Right at the beginning of the script we can find all we need:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func_table = ''
def reset_table():
global func_table
# This table is formatted for easier viewing, but it is really one line
func_table = \
'''\
print_table \
read_variable \
write_variable \
getRandomNumber \
'''
def check_table():
global func_table
if( len(func_table) != FUNC_TABLE_ENTRY_SIZE * FUNC_TABLE_SIZE):
return False
return True
- func_table - This is the variable we need to rewrite
- reset_table - The function that fixes the string length
- check_table - The function that makes sure the length of the string is right
Now we need to tear apart the variable inside the function and modify it:
- First replace getRandomNumber with win
- Make sure the length is the same by adding spaces:
1
2
3
4
5
6
'''\
print_table \
read_variable \
write_variable \
win \
'''
- Now put everything in one line and delete the newliners and extra quotes:
1
'print_table read_variable write_variable win '
- Now, rewrite the variable func_table with that value.
- Print the table again and you’ll see the win function instead of getRandomNumber
Flag: picoCTF{7h15_15_wh47_w3_g37_w17h_u53r5_1n_ch4rg3_a186f9ac}
Binary Exploitation
Picker IV
Description: Can you figure out how this program works to get the flag? Connect to the program with netcat: $ nc saturn.picoctf.net 61265 The program’s source code can be downloaded here. The binary can be downloaded here.
Back to binaries! This time we don’t only have a binary but the source code in C as well. If we execute the program it prompts us: Enter the address in hex to jump to, excluding '0x':
. If we examine the source code we can see a win
function. Our aim is to know the address in hex of the beginning of the win
function. Let’s open the file in GDB and disassemble the win
function. Once we have the dump we only need to copy the address in hex to the imput prompt without 0x
: 000000000040129e
.
Flag: picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_14bc5444}
buffer overflow 0
Description: Let’s start off simple, can you overflow the correct buffer? The program is available here. You can view source here. And connect with it using: nc saturn.picoctf.net 61481
This is an easy challenge. I managed to print the flag straight away by entering a bunch of a
s to the imput prompt. This will do the thing for sure but you should also try (as I did) to do it through GDB to have a better understanding on how buffer overflows work.
Flag: picoCTF{ov3rfl0ws_ar3nt_that_bad_ef7314c6}
Local Target
Description: Can you overflow the buffer and modify the other local variable? The program is available here. You can view source here. And connect with it using: nc saturn.picoctf.net 61977
This challenge is a bit tricky. Now it’s no just a matter of messing up the program by entering a bunch of a
s. Now we need to actually change the value of a variable. If we examine the source code, we can see that the value of the variable is 64
and just if it’s 65
would print the flag. To do so we must understand what is happening and what values the program is printing:
- If you enter
aaaaaaaaaaaaaaaaaaaaaaa
the variablenum
would still be64
. - If you enter
aaaaaaaaaaaaaaaaaaaaaaaa
the variablenum
would change to0
what tells us where the limit is. - If you enter
aaaaaaaaaaaaaaaaaaaaaaaae
the variablenum
would change to101
.
So, now that we know the limit and that 101
is the ASCII for the letter e
, it’s easy to think that we have to enter the value 65
in ASCII - and that’s letter A
. Now if you enter aaaaaaaaaaaaaaaaaaaaaaaaA
you’ll get the flag.
Note that if your input is let’s say: aaaaaaaaaaaaaaaaaaaaaaaaaaa
(just a bit longer) the variable num
will be 6381921
. To solve this challenge you would have to convert that number to hex: 0x616161
. This means that we have three 61
that corresponds to the hex value of letter a
- we have three a
s above the limit. Use an ASCII-Hex-Symbol table to help you play around with this.
Flag: picoCTF{l0c4l5_1n_5c0p3_7bd3fee1}
buffer overflow 1
Description: Control the return address. Now we’re cooking! You can overflow the buffer and return to the flag function in the program. You can view source here. And connect with it using nc saturn.picoctf.net 53282
.
Well, last challenge of the playlist! This last one is a mix o previous ones. If we execute the file provided we are prompted to enter a string. Once we enter it, the program jumps somewhere (0x804932f
) and ends. To know where do we have to try to make the programme jump, just disassemble the win
function of the binary file. Once we know we have to make the program jump to 0x80491f6
, let’s fill the input with the qwerty
method: qwertyuiopasdfghjklzxcvbnm
. The output: 0x804932f
– not enough. Second try: qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm
. Output: 0x63787a6c
– bingo!
Now, with CyberChef, we can translate 0x63787a6c
to symbols: cxzl
. Little endian here! Ok, now we know that the string should be qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjk
– time to add 0x80491f6
. As some of these are non printable characters we have to add them like this: \xf6\x91\x04\x08
.
But we cannot enter the string like so, we need to echo
it: echo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjk\xf6\x91\x04\x08 | nc saturn.picoctf.net 53282
and we’ll get the flag.
Flag: picoCTF{addr3ss3s_ar3_3asy_0195a40f}