Chapter 3 - Shellcode

Linux Buffer Overflow With Listening Shell

Purpose

To develop a very simple overflow exploit in Linux. This will give us practice with the following techniques:

  • Debugging with gdb

  • Understanding the registers $esp, $ebp, and $eip

  • Understanding the structure of the stack

  • Using Golang to create simple text patterns

  • Editing a binary file with hexedit

  • Using a NOP sled

  • Generating a payload with msfvenom

Disabling ASLR

We will disable ASLR to make this project easier.

In a terminal, execute these commands:

wget -nv https://samsclass.info/127/proj/ED3a.c
wget -nv https://samsclass.info/127/proj/ED3a
chmod a+x ED3a
./ED3a

The Program

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

int copier(char *str);

// compile with gcc -g -z execstack -no-pie -o ED3a ED3a.c
void main(int argc, char *argv[]) {
	if( argc != 2 ) {
     		printf("Usage: %s string\n", argv[0]);
   		exit(0);
	}
	printf("esp: %#010x\n", copier(argv[1]));
	printf("Done!\n");
}
int copier(char *str) {
	char buffer[1000];
        register int i asm("esp");
	strcpy(buffer, str);
	return i;
}

In this program, the main() function accepts a string argument of any length and passes it to the copier routine, which copies it into a buffer 1000 bytes long.

Making a Fuzzer

The fuzzer works, producing a string of "A" characters as shown above.

Fuzzing the Program

Now we can fuzz the program and cause a "Segmentation fault" as shown below.

Using gdb

If we run 1020 characters in the program, when it crashes, the eip value is 0x41414141, as shown above. This means that 1010 characters although causing the segmentation fault don't overflow eip, so we need to use 1020 characters.

Locating EIP

So we know that 1000 characters don't overflow the program, and that 1010 cause the segmentation fault but only 1020 reach the eip register. Although we are quite sure of that, we should put a nonrepeating pattern of bytes in the last 20 bytes of the exploit, after the 1000 characters to see which characters end up in the eip.

Running the program on gdb with our new fuzzer shows us that the program crash ends with an eip of 0x45454545 that is the ASCII code for "EEEE", as shown below.

Getting Shellcode

The shellcode is the payload of the exploit. It can do anything we want, but it must not contain any null bytes (00) because they would terminate the string prematurely and prevent the buffer from overflowing.

Also, it cannot contain Line Feed (OA) or Carriage Return (0D) characters, because we are inputting it at a prompt, and those would terminate the input line prematurely.

Metasploit provides a tool named msfvenom to generate shellcode.

We can see the exploits available for the Linux platform, which bind a shell to a listening TCP port with this command:

sudo msfvenom -l payloads | grep linux | grep bind_tcp

The exploit we want is highlighted above: linux/x86/shell_bind_tcp

We can see the payload options with this command:

sudo msfvenom -p linux/x86/shell_bind_tcp --list-options

The top portion of the output shows the Basic options. The only parameter we really need is "LPORT", the port to listen on, as shown above. This port has a default value of 4444, but we will choose a custom port.

We can generate exploit code for our Golang exploit using the C language format (msfvenom doesnt support golang yet):

sudo msfvenom -p linux/x86/shell_bind_tcp LPORT=31337 -f c

The resulting payload isn't useful for us, because it contains a null byte ("\x00"), as shown above.

That null byte will terminate the string, preventing the shellcode after it from being processed by C programs.

We could use the -b '\x00' switch to avoid null characters, but since we have plenty of room (1000 bytes or so), we can use the -e x86/alpha_mixed switch, which will encode the exploit using only letters and numbers.

The AppendExit=true switch makes the shellcode more eliable.

We can now execute this command:

sudo msfvenom -p linux/x86/shell_bind_tcp LPORT=31337 AppendExit=true -e x86/alpha_mixed -f c

The payload is longer (approximately 230 bytes (the exact length varies).

Constructing the Exploit

The program runs, printing out a long string of characters ending in "1234", as shown below.

Finding the NOP Sled in RAM

Now we need to open the program we are going to exploit in gdb and set a breakpoint at the instruction after the strcpy call.

After setting the breakpoint we can run the attack.

Now we need to see the registers and notice the address at the bottom of the stack fram (EBP).

By looking at the stack frame, we now need to find an address in the middle of the NOP sled (the 90 bytes), avoiding addresses containing "00", "10", or "20" bytes, which will be treated as delimiters and terminate the string prematurely.

In these case, I selected 0xffffcb90 as shown above.

Completing the Exploit Code

Now to complete the exploit code, we need to insert our chose address into the program in the eip variable, in little-endian byte order. This time I will show the exploit in both python3 and Golang (the address in the examples might be different, because I tried different addresses during my trial and error proccess with both languages to come out with an exploit, since all the examples are in python2).

Python3

Golang

Testing the exploit in the Debugger

Testing the exploit outside of the Debugger

Last updated