To my surprise, I’ve noticed that I have missed one previous challenge called passcode. Let’s come back to it.
As we have the source code available to us, we can start by taking a look at it.
From the start we can see two simple mistakes in login() function.
As we know, scanf() will read the data from stdin according to the format parameter and will store it in a location (where location is just an address of the variable) passed by the other parameters. So both passcode1 and passcode2 variables should be dereferenced in order to properly save the variables just like this.
So after that we know that solving this challenge by the classic mean of entering hardcoded password from source won’t work. Let’s fire up gdb to study this binary, looking for potential flaws.
This is the disassembly of two functions that we’re going to mostly work on. We can analyze the welcome() function that will read username and pass it to the name variable. From assembly we can view the part of the code that will read the name.
Here we can see instruction lea performed on edx and ebp-0x70. We can translate it just as load the effective address (calculate it) and store in the edx register. From that information, we know that variable ebp-0x70 is the name variable.
In the next function - welcome(), we can see the part that will make use of scanf() function.
But here, other than in previous example we don’t calculate the address of the variable passcode1, we just use it’s value with mov edx,DWORD PTR [ebp-0x10]. All the same with passcode2 - mov edx,DWORD PTR [ebp-0xc].
So what can we do with this information? After looking for a while, I noticed that the difference between name and passcode1 variable is smaller than 100, which is the size of the name buffer.
That way we can overwrite value of passcode1 by entering 96 characters in name, and using last 4 characters as the part for passcode1. In order to demonstrate it, we can set a breakpoint just before the first scanf() in login() function.
Now we can run the binary, with input consisting of 100 ‘A’ letters.
See? These 4 ‘A’ letters are inside the passcode1 variable. But as we can’t modify the second variable, we still can’t solve this challenge. Let’s look at the source code once again.
While looking at this part I’ve noticed a possiblity. As we can control what’s inside the the passcode1 variable, and the scanf() function that is supposed to write the value into the location at address presentend by the value of passcode1 variable, we are able to write some value into the any address in the binary. If you don’t understand, let’s break it down into simple steps.
We are able to enter the value into the passcode1 by filling the name buffer.
Function scanf() will place the value from stdin into the location of memory represented by the value of passcode.
But now we have to find a suitable target for this technique. Here comes this very interesting exploit that will overwrite the GOT table of this binary. What is GOT?
Global Offset Table is a place in which binary stores the addresses of functions, that are unknown at compilation time and is updated at runtime by dynamic linker. Basically, functions like printf() have their addresses there because they are not static. That’s why dynamic linker will at runtime look for these addresses of these functions, and put them in GOT. If function is used once again, we only have to get the address from the GOT, no need to run dynamic linker twice.
For more information I encourage you to read these papers, as this is only droplet in the ocean of knowledge:
Now we can write down our full plan for attack. Firstly, we can use fflush() function as our victim that we can overwrite. Function that will be overwriting will be system("/bin/cat flag");. So we have to find addresses of both of them. Then we will pass to the passcode1 address of the fflush() function, by simply fullfilling the name buffer. After that using the first scanf() we will be ready to overwrite the fflush() function with system(). After that all should be ready.
But firstly, let’s find the address of the fflush(), what we can do with objudmp -R command.