Friday 5 April 2013

Demystifying the Execve Shellcode (Stack Method)

We launched the SecurityTube Linux Assembly and Shellcoding Expert course recently. The course introduces the student to the basics of Assembly Language, Shellcoding, Encoders, Crypters and Polymorphism. I've decided to take up some of the course material and write posts about them. Even if you are not a student of the course, you should be able to follow these posts. Hopefully, this will be a lot of fun for both of us! :)

In this first post, I am going to take a look at probably the most popular shellcode - Execve! There are multiple ways to write shellcode but the most popular implementations use the Stack and the JMP-CALL-POP method. The subject of this post is creating Execve shellcode using the Stack method.

If you have no clue what Shellcode is, then have a look at the explanation on Wikipedia.  I will assume that you understand the basics of IA-32 assembly language and at least know what Shellcode is.

OK, let the games begin! 

Objective: To create an Execve Shellcode which can launch /bin/sh

Lab Setup I am using is Ubuntu 12.04 32-bit Desktop Edition. Most of the steps would remain the same even if you use other flavors of Linux.

Let's look at the whole process in a step-by-step way:

Step 1: Find the system call number for Execve

As you can see from the image above, Execve has system call number 11.

Step 2: We need to call Execve in our shellcode, so we would need to know the arguments it takes as input. Man pages to our rescue!

The 3 arguments must contain the following:

  • filename must point to a string containing the path of the binary we want to execute. In our case, this would be the string "/bin/sh". If you are not from the C world, "point" means the address of the string, and not the string itself.
  • argv[] is the list of arguments to the program. Most programs will use mandatory / option arguments to run e.g. telnet The argument here is "". In our case, we only want to execute "/bin/sh" without any more arguments, so the argument list is just a NULL pointer (0x00000000 address). However, there is a twist :) by convention, the first argument is the filename we want executed. So, argv[] would really be ['/bin/sh'. 0x00000000]
  • envp[] is the list of any additional environment options you want to pass to the program in key:value format. This will be NULL pointer / 0x00000000 for our purpose

 Step 3: Lets map the CPU Registers for the Execve call. The following registers will be used for the system call using INT 0x80

 This would be the mapping then. EAX would contain "11" which is the system call number for Execve as found in Step 1

 Step 4: Let the coding begin! I am using the below skeleton code for our program


I've only defined the TEXT section and the entry point into this executable would be _start. 

Step 5: We will now setup the Stack with all the arguments required for Execve as discussed in Step 3. Remind yourself that the stack for IA-32 grows from High Memory to Low Memory.

Step 6: Let us setup the EBX register first. EBX needs to point to "/bin/sh" in memory. Now the string would have to be '\x0' terminated. Let us setup the NULL first by pushing a 0x00000000 onto the stack. Now we cannot do a "PUSH 0x00000000" because Shellcode cannot contain NULLs as this is the most common bad character. Hence we will have to create 0x00000000 in one of the registers and PUSH the register on the stack. We will use the XOR operation to zero out EAX and then PUSH EAX on the stack:

The stack should look like this right now:

We now need to PUSH "/bin/sh" on the Stack. As the Stack grows from High Memory to Low Memory in x86 we will need to push "/bin/sh" in reverse order :) Also, it would be easier to push data which is a multiple of 4 with the least number of instructions. This is desirable as the smaller the shellcode, the greater the number of scenarios where we can use it. 

Now, "/bin/sh" is 7 bytes and we need to convert it to 8 bytes without messing up the filename. On Linux this is easy to do because "/bin/sh" invokes the same program as "//bin/sh".  Multiple "/" in succession does not cause any problems as you can see below:

Awesome! Now we need to PUSH "//bin/sh" (8 bytes) in reverse on the Stack i.e. "hs/nib//" Here is a quick way in Python to generate the value in hex:

Don't we all just LOVE Python :) Let's add the PUSH in the code now with the hex values above.

After the 2 PUSH instructions, this is how the Stack should look like:

Awesome! Now let us make EBX point to the top of the Stack! This would really mean EBX is now contains the address of "//bin/sh" in memory. This can be easily done by copying ESP into EBX

Step 7: Let us now setup EDX which if you remember should point to a NULL pointer. This can be easily achieved by a PUSH EAX (remember EAX contains 0x00000000) and copying ESP into EDX.

The Stack looks like this right now:

Step 8: The last piece of the puzzle ECX still remains! ECX needs to contain the address of [Address of //bin/sh in memory, 0x0000000] as discussed previously. Currently EBX contains the address of "//bin/sh" in memory so let's PUSH EBX on the Stack

If you notice, the top of the Stack now points to [Address of //bin/sh in memory, 0x00000000] This is exactly what we wanted! So let's write out the code to copy ESP into ECX

Step 9: All the arguments for Execve are now setup on the Stack and EBX, ECX and EDX are pointing to them. The Stack looks like this right now:

Step 10: Let us now call Execve! We setup EAX to contain "11" and invoke Interrupt 0x80

The code for Execve-Stack.nasm is available below for copying:

Now let us Assembly, Link and Test the code:

Let us look at the binary using Objdump (we could also use the .o file) The highlighted hex values are the actual IA-32 opcodes.

We need to extract all the opcodes to create our shellcode. As cumbersome as this looks, I am happy to report there is a shortcut available:

Here is the final extracted Shellcode - pretty much looks like ... any other piece of Shellcode :)

Step 11: I am going to use the following C program to test the shellcode to ensure we have not mistakenly used any hardcoded address and anything else which would disallow this shellcode from running inside another process.

Let us now compile shellcode.c and run it to test!

Fantastic! So our Execve Shellcode is working great! In the next post we will look at automating Shellcode generation for Execve using N arguments using Python. Stay tuned!

If you've never done Assembly Language / Shellcoding etc. before then please checkout my SecurityTube Linux Assembly and Shellcoding course which aims to teach the basics of assembly language on the Linux platform from a security perspective and its application to writing shellcode, encoders, decoders and crypters, among other things. The course material is over 9 hours of HD videos!


  1. Awesome ... Vivek I really appreciate your work man ...thanks a lot ..pls keep us enlighted with "free" stuff ...

  2. Vivek,

    Your way to put the syscall args is the correct one (man execve) and it's greatly elucidative.

    However, after your SLAE course I could make it works with fewer bytes (21) too:

    xor ecx, ecx
    mul ecx
    mov al, 11
    push ecx
    push 0x68732f2f ; "//sh"
    push 0x6e69622f ; "/bin"
    mov ebx, esp
    int 0x80

    Thanks to you!

  3. Vivek good stuff. would you be able to explain the following a bit more.

    int (*ret)() = (int(*)())code;

    I sort of follow it but wanted to make sure i understand it.


      That should answer your question

  4. Compiling your C-Code with

    [ gcc -fno-stack-protector -z execstack test.c -o test ]

    does result in

    [ Shellcode Length: 25; Segmentation fault (core dumped); ]

    Linking under x86 fails.
    [ ld -m elf_i386 -s -o test test.o ] does fix this.

    Great tutorial!

  5. Compiling your C-Code with

    [ gcc -fno-stack-protector -z execstack test.c -o test ]

    does result in

    [ Shellcode Length: 25; Segmentation fault (core dumped); ]

    Linking under x86 fails.
    [ ld -m elf_i386 -s -o test test.o ] does fix this.

    Great tutorial!

  6. im getting segmentation fault?

  7. Ups my works perfect.