Introduction

Narnia a series of reverse engineering puzzles developed by OverTheWire and is an introduction to reverse engineering. Below is the methodology used to solve these puzzles.

Login and Execution

In the previous level, Narnia 0, we covered the difference between Source Code and Executable Code. All Narnia binaries and source files are located in /narnia/. Using the password we obtained in Narnia 0, we can now login as user narnia1 and change to the narnia directory. Then reviewing the source code and the binary, we can determine how to exploit this particular level. Let’s get started.

When listing the directory we can see all levels of Narnia. The concern of this walkthrough is narnia1.c and narnia1. Viewing this directory listing we can see that narnia1 when executed runs as the user narnia2 and is executable by the user narnia1. Running ./narnia1we see the response “Give me something to execute at the env-variable EGG”. With this we can gleam some information from it’s output. Env-Variable is expecting the environment variable EGG to contain data. Let’s break down the source code.

Source code and breakdown

cat is an essential tool for any Linux distribution and can assist users in reading files. This in turn allows us to read the narnia1.c source file. Running the command cat narnia1.c from the /narnia/ directory we get the following source code.

cat /narnia/narnia1.c -Source code minus the narnia comments.

If you’ve never seen the C programming language before it might be a little foreign to you. Let’s break it down line by line and hopefully get an idea of what’s happening in our binary. I have copied the code to VSCode for ease of commenting.

Lines 1-2

The #include <stdio.h> is used to import all standard input and output functions defined in the stdio header file, such as printfand NULL.

Line 3

Every C program has a main() function and is the entry point of the binary once the source code has been compiled. Within main is where we get to the real meat of the program.

Lines 3-4

Within main() we see the above code which declares the ret()function as a prototype to use further down in the code. Because C programming is procedural, defining it first will save us from an error upon compilation. Now let’s visit the if statement.

Lines 6-9

The above 4 lines of code are a conditional statement and will execute a specific set of instructions in the code. This case it is pulling in the environment variable “EGG”, if it is empty, or rather NULL in this case, it will print “Give me something to execute at the env-variable EGG” and exit(1) will then exit the program.

Lines 10-16

If the environment variable “EGG” has data, the conditional statement covered in lines 6-9 will be skipped and will then print “Trying to execute EGG!”. The program will assign the data in the environment variable “EGG” into ret, then execute the data, and then exit.

Hopefully with all that explained above we can start diving into exploiting the narnia1 binary using what we’ve gathered from the code breakdown.

Debugging and Analysis

Leaving the “EGG” environment variable empty we receive the expected output based on the code review. But what happens when we give it something to execute. To set an environment variable in Linux we can run the simple command export EGG=cd. To view the env variable we just set, run echo $EGG.

Setting ‘cd’ as our env variable for EGG

With data now set on variable “EGG”, we can now run ./narnia1 and see what happens.

Look at that, we have a “Segmentation fault”. This means we have an abnormal condition that caused the program to exit. Lucky for us, GDB is on the narnia host, loading the binary into gdb using gdb narnia1 we can start looking at what the binary is doing. Once gdb has loaded, we can run the command disassemble main which is going to print the memory addresses and assembly code for everything happening in the main() function we saw above.

(gdb) disassemble main

Your memory addresses will differ so refer to the image above for the rest of the article. From memory address 0x08048476 to 0x0804848f this is the conditional statement discussed previously that prints a msg to the screen and exits the program if “EGG” has no data. Since we have data in the “EGG” variable it will be skipped and move to the 0x0804849b memory location where it will print “Trying to execute EGG!” to the console. Moving down to memory address 0x080484a8 through 0x080484b3, this is where the program calls getenv(“EGG”) in the program, makes space on the stack, and assigns the data in “EGG” to the EAX register. Looking at the 0x080484b6 memory address we see the program calling *%eax which is the ret() function being executed. Our first stop to see whats happening is to set a break point on that call and see what is on the stack.

Using the memory address of eax, we set a breakpoint. This will allow us to stop program execution as the environment variable “EGG” is put on the stack. Set your breakpoint on the (gdb) prompt using break *0x080484b6. Then proceed to run the command by simply typing runat the (gdb) prompt. The program should stop at the set breakpoint.

(gdb) run

This allows some analysis of the stack. While we are stopped on the 0x080484b6memory address, let’s view what’s on the stack at that address. Using the x/25x $eax command we can see (in hex) the next 25 values that are set for the eax register.

(gdb) x/25x $eax

Looking at the first address on the left (0xffffdea7), we see the first value 0x5f006463. If we convert this from hex to ascii, we can see the last two bytes 64 and 63 convert to d and c respectively. If you recall from earlier, we made our “EGGS” variable equal to cd, with memory being in little-endian format this matches our environment variable data. Knowing that ./narnia1 is executable by user narnia1 and runs as user narnia2, let’s use this ability to put data on the stack to get a shell as user narnia2.

Exploit

We know that shellcode is what we are after since we want to spawn a shell as user narnia2. The site shell-storm.org has a plethora of different OS shellcodes available. Running uname -a we can see the host is an x86_64 bit based OS so any x86 or x86_64 shells are possibly usable. After a few frustrating shellcode tests and failures later, I found shellcode-607 worked wonderfully. Within this page is the hex code we are after. Copying the shellcode value and removing any quotes in the value, we end up with something like \xeb\x11\x5e\x31\...\x8a\xe2\xce\x81. Using this information we can now prepare our exploit. To place our shellcode into the “EGG” variable we use python to get the hexcode in an acceptable format.

With our environment variable set, we can now execute the ./narnia1 binary and get the password for user narnia2.

If you missed out on Narnia Level 0 click the button on the left to check it out. If you’re all caught up and want to see more, check out the Narnia Level 2 by clicking the button on the right.

You were not leaving your cart just like that, right?

Enter your details below to save your shopping cart for later. And, who knows, maybe we will even send you a sweet discount code :)