[FCSC 2020] Pwning by patching one byte - Patchinko
This challenge was part of the France Cybersecurity Challenge organized by the ANSSI organization.
The goal is to get the flag file by exploiting a binary. This time something is special because we can patch one byte before its execution. So basically, we have to find a way of opening a shell by modifying one byte of the binary.
This challenge wasn’t difficult but I found it really cool !
Checking the assembly
Let’s see what kind of binary it is :
file patchinko.bin patchinko.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ea28c1aa273a99a5c9323b062e3277734bbed0be, not stripped
The first thing I’ve done is opening the binary in Cutter. The Ghidra decompiler was nicely working with this binary. Moreover, it’s not stripped, so I just read the pseudo code :
fgets()is strange because it takes an input of length 4 but then wait check only the first char (to be
There seems to be no buffer overflow available. I didn’t check for ASLR / NX Stack etc because I remembered that I could patch one byte.
Since the libc functions are dynamically linked maybe we can play with the PLT (I’ll explain it a little more in the following part) to make a libc function pointing to another. Oh ! That’s perfect because the
system()function is used in the main.
Exploiting the binary
PLT stands for Procedure Linkage Table. It’s part of the dynamic linking process but I won’t talk much about it here. Here is the PLT of our binary :
Basically, the calls to libc functions in main are just jumps into the PLT table. Each symbol in the table contain a jump to the
.got.pltsection. And this special section contains the addresses of each function after they’ve been looked up.
However, for the first call to a libc function, this lookup isn’t done. In this case, the function offset is pushed on the stack, and then we jump to the lookup procedure.
But what happen if we patch a push ? So if we patch the
push 3of strlen symbol by
push 4, the lookup that will be made is for the system symbol. The consequence of this is simple, the
strlen()function will now be pointing to the
system()function in the shared library.
Why did I chose
strlen()function ? Because, the call to strlen occurs on our input just after the fgets. So it becomes
printf("Is this your first time here? [y/n]\n>>> "); fgets(&s, 4, _reloc.stdin); // We can enter 3 bytes in s iVar1 = strlen(&s); // After the patch, this call is equals to system(&s)
Let’s find the offset of the byte we want to patch :
So we want to replace 0x03 located at offset 0x6c7 (
0x4006c7 - base_address) by 0x04.
Since we have 3 bytes of input, let’s start by doing an
It’s working ! Now we need to find a way to get the flag. We can’t use
cat flagsince we only have 3 bytes.
However, we can try opening a shell by typing
As you can see, it worked ! :)
Of course, it’s not the only way. We could have just modified one byte of any call to jump to system instead of strlen.
But I found this way more interesting as it allows us to understand a part of the dynamic linking process when using shared libraries.
As I said at the beginning, I found this challenge really cool ! :D