Stack5 Exploit-Excercises | Introducing shellcode

When I started my exploiting learning journey, this was probably the challenge that took me the most time to complete. I went nuts trying to solve it, spent hours staring at what looked like the text on the western gates of Moria.

Now that I understand what that took place, it's probably a good idea to document what I learned and link everything that helped me dive into the stack.

First of all, the code which I had to exploit looked like this:


What is remarkable here is the gets() function.
The manual page tells us that gets() takes an argument from stdin and places it into the buffer, until it finds a terminating line or an end of file (EOF), which it replaces by a null byte. It also says that it doesn't check for overrun of the buffer, meaning it's vulnerable to buffer overflow attacks.
So gets() will just continue to store characters given even it overflows the buffer.

Also, if anyone is interested, one of the first network spreading worm attacks was based on an exploit found because of the no-boundary-checking nature of the gets() function :D 

So the gets() function will get the buffer that was created above, but because this function won't check if the buffer is overflowing, we can exploit it 1988 style!

The clues that were given for this challenge were to use someone else's shellcode and to use \xcc in case we had to debug it.

In my case, I didn't use \xcc, but it's useful to know for when I actually want to debug my own shellcode.

How is the stack built

Okay so this was the part where I had to do quite a bit of research.
I already had a picture in mind of what was that happened inside a stack during an overflow, but I didn't really get into details like it was necessary to do in here. The devil is in the details, right?

To be able to explain how a stack works, it's always useful to know in what context we are in instead of having the idea of a floating stack inside the computer.
Let me start from the beginning.

All of the processes that happen inside an operating system happen inside a virtual address space. Virtual address space has to be reserved for the kernel as well as for the user mode space, the first one being a consquence of using virtual address space, since those ones have to work for all of the running software.

So we'd have something like this in the big scale, and by something like this I mean that the space that each one component takes will depend on the operating system.

The kernel space will always map to the same physical addresses, and that will be determined by the operating system writers. The user space, however, will keep changing this mappings as proccesses switch from one to another.

Let's leave the kernel space aside and jump into the user mode space, since there is where the stack resides.

Related image

The user space, as we said earlier, will change each time a new process gets executed. Reminder that it's not the data we're loading here, but physical memory locations that guide us to where the actual data is.
Page tables are what it will be needed by the computer to link the physical addresses that are needed for each process.

Let's keep moving on.

Inside the user mode space we'll find a collection of things, the most important one for us right now is obviously the stack, but let me explain a bit of the others.

The text segment are the instructions that will execute the program, you can't write on it or it will give you a segment violation. This is the same text section we've seen in the malware analysis labs with PEview (if you've read it).

Then we have the BSS segment and data segment. The data segment has also been showed by PEview, and we know it contains global and static data which can be modified by the user. On the other hand, the BSS data will contain global and static variables initialized to zero or just not initialized at all.

Following that, there's the heap. I will skip it and leave it for the future. Just know that the heap is there.

And jumping through a bit more of mappings we will arrive at the stack, and that's where the formal explanation of the stack will start.

The stack

The stack space will be this region inside of the virtual address space in memory which will store the local variables and the function parameters.

alt text

And I'm going to take this opportunity of a stolen image from Malware Unicorn's blog and tell you right now to check it here.
It guided me through times of darkness and she's made a great job at explaining the inner workings of malware. Just go, seriously.

The stack has a property which is commonly refered to as LIFO, or last in first out. That means that the last element we placed on top of the stack ( or we pushed ) will be the first one that gets removed ( or popped ).

The question here now is, when and how does the stack come into play in this challenge?
First of all, I think it would be practical to know why we need the stack, what exactly the stack does that it's so useful.

So let's say you want to write a function that prints out "Hello world!" and you want to do it in the C language.

#include<stdio.h>

int main() {
    printf("Hello world!");
    return 0;
}

But if we give that directly to the machine... it just will not know what to do with it. The machine needs a manual filled with instructions that it'll need to follow to run that code for us. That's why we need assembly, assembly will give the machine the instructions that it will need to run it.

Now let's compile it with gcc, not forgetting the -g option if we want to be able to see our assembly code.

gcc -g hello.c -o hello

Perfect! Now we will be able to see how the machine will see the tasks that are needed to do in order to work. Here we'll use GNU debugger, or gdb.

gdb hello
set disassembly-flavour intel
disas main

  push     rbp
  mov     rbp,rsp
  lea       rdi,[rip+0x9f]        # 0x6f4
  mov     eax,0x0
  call      0x530 <printf@plt>
  mov     eax,0x0
  pop      rbp
  ret

This looks weird, doesn't it? Well, that's right where the machine is interacting with the stack.

Before I talked about instructions, comparing them to the instructions a machine would follow to build the program. Well, sometimes this data has to be saved somewhere before going to the next instruction or it will be lost or malformed, and we don't want that!

So we agree then that, along the process, we will need to register bits of data somewhere, right? What about a place we call the registers? It looks like a self-explanatory place to me, and so it did to the person who made these registers, because that's exactly what they're called.

After the registers, there's also the pointers that point to the registers, telling the machine 'hey! go to do whatever this register asks you now!'.

Let's see the main type of pointers.

ESP --> stack pointer, which points to the top of the stack .

EIP --> instruction pointer, which points to the next instruction to be executed.

EBP --> base pointer, which points to the 'area' where the function parameters and local variables are stored.

That's more than enough knowledge on pointers for us to start doing this challenge.

But, what will happen when we give it more data than it can handle? What if we told the stack that we'll be storing 100 bytes for a 64 byte stack?
It will overflow, if we don't have any implemented security at all!

That is just what this challenge is about.
Let's overflow the buffer.

The exploit

So knowing all that info, it was clear that the stack had the be overflown, no discussion to this point about the purpose of the challenge.

However, it wasn't just like the other challenges, where you could directly overflow the stack via stdin ( or standard input ). No, here we'll make use of some shellcode, which is machine language but in hexadecimal.

So we could just do an overflow, but hm, how could we get hold of the machine? What if we could be root?

The plan was to first fill the buffer up to the point where we reach the EIP, and then we'd have to overwrite the EIP.
For simplicity, I chose to overwrite the EIP with an address that comes just after the ret address, why? Well, because after that, after the EIP, we would then completely overflow what remains of the stack with a NOP slide (or no operation slide, which the shell will just slide through until it finds some more instructions).

Because we don't really know where in the stack our shell code will fall, we can just let the NOP slide stop whenever it finds it :)

Just like this:

Image result for nop sled
Try to understand that picture.

In the Malware Unicorn stack frame picture, you could see a return address. A set of instructions perform some action and when they are done, they go to the return address to ( usually ) get back to the main function.
BUT, in the picture above, the return address in the red line gets totally jumped over because there is a JUMP in the green line.
The JUMP points to a NOP slide, which tells the machine to keep going forward until it finds an instruction, that's why it's called a slide, it doesn't do anything except keep going forward until an instruction is found.

After that, even though in the picture another jump is taken, for this challenge we can write out shellcode at the end of the NOP slide.


I'm totally going to admit that at first, I didn't succeed at all. I went crazy trying to find out what I was missing because it didn't seem to work after I thought I had understood everything.

LiveOverflow saved my life.
Also, I copied his method of writing the exploits to another file, mostly because it seemed much more clean and organized, way better than the mess I was doing :)

Basically, he explained how it was necessary to keep the shell open after executing it, since it closed itself after execution, so a 'cat' was necessary.

I will leave here some interesting reads I found for this challenge. Second one is spanish, but well ¯\_(ツ)_/¯

http://phrack.org/issues/49/14.html
http://www.set-ezine.org/ezines/set/21/0x08.txt
http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/

Comments

Popular Posts