Introduction Source Code vs Executable Code Narnia Level 0 – Source Code Narnia Level 0 – Exploitation Narnia Level 0 – Solution


This is an introduction to reverse engineering. I will be going through a series of reverse engineering puzzles developed by OverTheWire and explaining the methodology I used to solve these puzzles.

In the challenges that are provided, you are given an executable and the source code to a program. The goal is to figure out a way to exploit or PWN the executable. Exploitation in this context typically refers to overwriting system memory to achieve some unintended outcome. Lets first talk about the files that are give to you.

Source Code vs Executable Code

Source code is the original language that a program is written in. Source code is then compiled into a set of instructions that the computer processor can execute. This translation from source code to executable code must happen, because computer processors do not understand source code, but they do understand the machine instructions inside executable code. Lets take a look at the difference

Figure 1. Example C Source Code HelloWorld.c

Figure 2. Example C Executable Code HelloWorld.obj

As you can see from the examples above, even for a small “Hello World!” program, machine instructions are not easy to read, which is why high level source code exists in the first place. High level programming languages such as C, C++ and Java, are easier to understand and are human readable. Even without being a programmer, you can probably determine that the source code in figure 1 prints the text “Hello World” to the screen.
Unfortunately for the reverse engineer, most compiled programs do not come with the source code that was used to generate them. This is done for a couple of reasons, first, the source code is not needed for a program to run, the only thing a computer needs is the compiled object code, therefore shipping a program without the source code makes it smaller. Second, people who sell their programs for a profit, don’t want you to see how they created the program, or else anyone could steal the code and compile it for themselves without paying. 

Narnia Level 0 – Source Code

Lets get started…

First we must ssh into the game server using the following credentials:

Server: Username: narnia0 Password: narnia0

The /narnia folder holds all of the challanges for each level, but our current permissions level only allows us access to the level 0 files, which are: narnia0 and narnia0.c . What are these two files? The first one is the compiled program and the second one is the source code.
Lets first look at the source code
Narnia0Source1 Here we see two variables being created, val is initialized to the hex value 0x41414141 and buf is an array that is not initialized to any value, but space has been reserved for 20 1-byte values. The “char” data type in C is defined as 1-byte in length, therefore char buf[20] reserves 20 consecutive 1-byte memory locations. 

Narnia0Source2These are the instructions that tell you what your trying to accomplish. The variable val, has been initialized to the value 0x41414141, the program wants you to somehow change it to the value 0xdeadbeef.
 The scanf function is used for user input. The %24s means that the function will accept a 24 byte character string (1 char = 1 byte) and store that into the buf variable, but if you remember buf was only allocated 20 bytes of memory. This is how we’re going to exploit the program, more on this later.
Narnia0Source4These two lines help you see what values were actually stored in buf and val after scanf() received user input.
Narnia0Source5If you correctly overwrite val’s memory location to hold 0xdeadbeef, then this branching statement gives you a shell (command prompt). Otherwise, it prints “WAY OFF!!!!” and then exits. Don’t despair, it doesn’t matter how close you are, it will always print this statement if it is incorrect.

Narnia Level 0 – Exploitation

As I stated before, we will need to take advantage of

scanf() in order to exploit this program, but to understand how, we first need to understand how programs get mapped into system memory.

Every program gets mapped into memory and uses a data structure known as “the stack” to store information such as variables, arguments and what instruction to execute next. It make sense to store these variables on the stack so that they can be easily and quickly retrieved as the program runs. Here is a graphical representation of what the stack looks like.  Notice the arrow pointing from high memory to low memory, this is because the stack grows towards the lower memory locations. That means that if a programs defines three variables in the following order… int x= 10 int y = 20 int z = 30 …then they will be stored on the stack as such. 

In order to exploit narnia0, this is all the information that we need to know. Other details about the stack such as the return address and old EBP (base pointer) will be explained in detail as we progress through the other levels.  Lets see how the the narnia0 program looks on the stack.

Since the stack grows from high memory to low memory,

val is stored at a higher memory address than buf, because val was declared first in the program. Notice that the main program does not take any arguments on the command line, so those memory locations on the stack have been grey out (in reality they don’t even exist), and since this challenge only require us to manipulate buf and val, I’ve also grayed out Old EBP and Return Address, again we’ll discuss the purpose of these during future levels. How are we going to change val on the stack, so that it reads 0xdeadbeef? Remember the scanf() function that reads in 24 bytes of data? For the visual learners, here’s how that looks in memory.  Each square in the illustration above represents 1-byte. val is of type long and long is defined as being 8-bytes. Again, we see that scanf() is set to take in 24-bytes of input and store them in the memory location of buf. The scanf() function does not check or care how big the destination location is; therefore, by filling up the scanf() buffer, we can overwrite the adjacent memory location of val.

Narnia Level 0 – Solution

Armed with the knowledge above, you may be temped (as I was) to try and solve the puzzle. Attempt 1: NarniaAttemp1 Although this did not work, important information can be gleaned from our failure. The first thing to notice is that value 0x64616564 is no where close to 0xdeadbeef. The answer to why lies on the second line that says “Correct val’s value from 0x4141414141 to 0xdeadbeef”. The 0x portion means that it wants the value of val to be in hex, but instead we passed it ASCII. The second thing to notice is that 0x64616564 is the ASCII equivalent of daed (or dead backwards). The reason our input was displayed backwards is because most x86 architectures store data in little-endian format when it is pushed onto the stack. This means that the least significant byte (far right value) gets stored in the lowest memory location, for example, if we set val to Hello, it would be stored in memory like this… Using this information lets try again. Attempt 2:NarniaAttemp2First we need to figure out how to get hex into our ASCII input. The BASH shell has the ability to represent hex using the \x escape sequence, but in order for echo to parse escape sequence we have to add the -e option. Since hex values outside the ASCII range contain non-printable characters that are not easily copied and pasted, we are going to pipe the output directly to the program. This has the affect of storing our custom string into a buffer and waiting for scanf() to read it. Success? Sort of. The program didn’t yell at us and the val has been overwritten to the correct value, but the program closes immediately without allowing us to use our newly acquired privileges. Attempt 3: SUCCESS! NarniaAttemp3 If you create a command group ( command 1 ; command 2; ) and pipe the result to your program, each command is provided as a separate input to the program. The first input is our custom string which gets fed to the scanf() function. After the program executes system(“/bin/sh”) we are given a command shell, by passing cat to the command shell as a the second input, it keeps the command shell open, we are free to run any commands under the new elevated permissions. (I have scoured the internet high and low to figure out how and why that last part works, but have yet to find a solid answer).  

…Coming Soon Narnia – Level 1…


Categories: hacking