Building shellcode tutorial
What is shellcode?
Shellcode is a series of location independent bytes that are able to perform a certain task. In other words shellcode can be injected in any places in memory and run. You can input shellcode in your exploit and expect to fulfill your goal without worries about code dependencies. A shellcode is different from common compiled codes because in order to be injected it should have certain characteristics. First a shellcode should not have any null bytes because a shellcode usually is injected as a string data and a null byte plays the role of string termination character. Second a shellcode needs to be very small in order to be able to fit a small buffer.
How to write a shellcode?
Most hackers use previously built shellcode and by a framework like metasploit it is very easy to use common shellcodes for an exploit. Anyhow the need to writing shellcode for a professional hacker is undeniable. Shellcodes are developed using assembly language but any assembly code is not a shellcode according to the reason I explained at the beginning of this tutorial. Below is a basic local exploit shellcode to spawn a shell on linux:
BITS 32 jmp short two ; Jump down to the bottom for the call trick. one: ; int execve(const char *filename, char *const argv , char *const envp) pop ebx ; Ebx has the addr of the string. xor eax, eax ; Put 0 into eax. mov [ebx+7], al ; Null terminate the /bin/sh string. mov [ebx+8], ebx ; Put addr from ebx where the AAAA is. mov [ebx+12], eax ; Put 32-bit null terminator where the BBBB is. lea ecx, [ebx+8] ; Load the address of [ebx+8] into ecx for argv ptr. lea edx, [ebx+12] ; Edx = ebx + 12, which is the envp ptr. mov al, 11 ; Syscall #11 int 0x80 ; Do it. two: call one ; Use a call to get string address. db '/bin/shXAAAABBBB' ; The XAAAABBBB bytes aren't needed.
Shellcode example, taken from The Art of Exploitation book
This code to hackers is like a Picasso masterpiece for painters! First we have a short jump to the end of code. Seems absurd? This is a genius solution to avoid null bytes! We do not have data segment (because shell code should be location independent) in shellcode and to define a variable we should use the stack. To use the stack we cannot use the push '/bin/shXAAAABBBB' instruction because push is just for registers. Thus to save the variable on the stack we use the function calling mechanism. We know that when we call a function the next instruction address is saved on the stack. So we deliberately make a call to save the '/bin/shXAAAABBBB' address on the stack and then pop it in the next instruction. Later we set the null termination character of this string (on X) when the shellcode is executed. You may wonder why we need this text! '/bin/sh’ is the parameter for the spawn 11 system call. The XAAAABBBB is just reserved to be overwritten later in the code by mov instructions.
If you notice, to avoid null byte we used xor ax, ax instead of mov ax, 0. Also to make the shellcode smaller we used lea ecx, [ebx+8]. Because other option was to use two instructions to do the same thing:
add ebx, 8 mov ecx, bx
Assembling this code with nasm gives you the shell spawning bytes for a local exploit:
eb 16 5b 31 c0 88 43 07 89 5b 08 89 43 0c 8d 4b 08 8d 53 0c b0 0b cd 80 e8 e5 ff ff ff 2f 62 69 6e 2f 73 68
Shellcode bytes in hexadecimal
Remote Port binding shellcode
The above shellcode works fine for local exploits but for remote exploits it is useless since it will spawn a shell for the local terminal the program is executing. In order to develop a remote port binding shellcode you need to listen on a port and then duplicate the descriptor of that listener to the standard input, output for the spawned shell. These are done using the system call 102 for port binding (on linux) and the system call 63 to duplicate the descriptor to the standard input, output. The code to do so is not given to make the reader think about it but if you're curious refer to the Art Of Exploitation book. If the shellcode succeeds you can connect to the bound port and execute commands using netcat.