[FCSC 2020] Pwning by patching one byte - Patchinko
Introduction
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 !
Explanation
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 :
The firstfgets()
is strange because it takes an input of length 4 but then wait check only the first char (to bey
orno
).
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 thesystem()
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.plt
section. 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 thepush 3
of strlen symbol bypush 4
, the lookup that will be made is for the system symbol. The consequence of this is simple, thestrlen()
function will now be pointing to thesystem()
function in the shared library.
Why did I chosestrlen()
function ? Because, the call to strlen occurs on our input just after the fgets. So it becomessystem(user_input)
:
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 id
.
It’s working ! Now we need to find a way to get the flag. We can’t usecat flag
since we only have 3 bytes.
However, we can try opening a shell by typingsh
.
As you can see, it worked ! :)
Conclusion
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
--
Kn0wledge