ShellForge

ShellForge G3

ShellForge G3 development is ongoing. You can grab the latest version on svn.
svn co http://svn.secdev.org/shellforge/trunk shellforge
You'll need the ShellForge Library. Get the latest version of the SFlib generator :
svn co http://svn.secdev.org/sflib/trunk sflib
and generate a ShellForge Library :
./gensflib.py -d /path/of/my/choice/sflib

ShellForge G2


ShellForge G2 has been presented at CanSecWest (slides)
This page will be updated in the upcoming weeks, with G2 archive ready.
You still can get a Shellforge G2 pre-release archive.

ShellForge G1

Download version 0.1.15 : shellforge-0.1.15.tar.gz
Download presentation slides for shellforge, done at LSM03.

Intro

ShellForge is a python program that builds shellcodes from C. It is inspired from Stealth's Hellkit.

Some wrapper functions arround system calls are defined in header files. The C program uses them instead of libc calls. ShellForge uses gcc to convert it into assembler. It then modifies it a bit, compiles it, extract code from the object, may encode it and add a loader at the begining.

The available loaders are, for the moment :

Future evolutions :

Example

Here is the hello world program (hello.c).
#include "include/sfsyscall.h"

int main(void) 
{
        char buf[] = "Hello world!\n";
        write(1, buf, sizeof(buf));
        exit(0);
}

We can have the raw shellcode :

$ ./shellforge.py hello.c 
** Compiling hello.c
** Tuning original assembler code
** Assembling modified asm
** Retrieving machine code
** Computing xor encryption key
** Shellcode forged!
\x55\x89\xe5\x83\xec\x24\x53\xe8\x00\x00\x00\x00\x5b\x83\xc3\xf4\x8b\x83\x67\x00
\x00\x00\x89\x45\xf0\x8b\x83\x6b\x00\x00\x00\x89\x45\xf4\x8b\x83\x6f\x00\x00\x00
\x89\x45\xf8\x0f\xb7\x83\x73\x00\x00\x00\x66\x89\x45\xfc\x8d\x4d\xf0\xba\x0e\x00
\x00\x00\xb8\x04\x00\x00\x00\xc7\x45\xec\x01\x00\x00\x00\x53\x8b\x59\xfc\xcd\x80
\x5b\xb8\x01\x00\x00\x00\xc7\x45\xec\x00\x00\x00\x00\x53\x8b\x59\xfc\xcd\x80\x5b
\x5b\xc9\xc3\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x21\x0a\x00

We can test it :

$ ./shellforge.py -tt hello.c 
** Compiling hello.c
** Tuning original assembler code
** Assembling modified asm
** Retrieving machine code
** Computing xor encryption key
** Shellcode forged!
** Compiling test program
** Running test program
Hello world!

** Test done! Returned status=0

We can have the shellcode ready for C inclusion :

$ ./shellforge.py -v0 -C hello.c 
unsigned char shellcode[] = 
"\x55\x89\xe5\x83\xec\x24\x53\xe8\x00\x00\x00\x00\x5b\x83\xc3\xf4\x8b\x83\x67"
"\x00\x00\x00\x89\x45\xf0\x8b\x83\x6b\x00\x00\x00\x89\x45\xf4\x8b\x83\x6f\x00"
"\x00\x00\x89\x45\xf8\x0f\xb7\x83\x73\x00\x00\x00\x66\x89\x45\xfc\x8d\x4d\xf0"
"\xba\x0e\x00\x00\x00\xb8\x04\x00\x00\x00\xc7\x45\xec\x01\x00\x00\x00\x53\x8b"
"\x59\xfc\xcd\x80\x5b\xb8\x01\x00\x00\x00\xc7\x45\xec\x00\x00\x00\x00\x53\x8b"
"\x59\xfc\xcd\x80\x5b\x5b\xc9\xc3\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64"
"\x21\x0a\x00";
int main(void) { ((void (*)())shellcode)(); }

We can use an xor loader to avoid \x00 bytes in the shellcode

$ ./shellforge.py -v0 -x hello.c
\xeb\x0d\x5e\x31\xc9\xb1\x75\x80\x36\x02\x46\xe2\xfa\xeb\x05\xe8\xee\xff\xff\xff
\x57\x8b\xe7\x81\xee\x26\x51\xea\x02\x02\x02\x02\x59\x81\xc1\xf6\x89\x81\x65\x02
\x02\x02\x8b\x47\xf2\x89\x81\x69\x02\x02\x02\x8b\x47\xf6\x89\x81\x6d\x02\x02\x02
\x8b\x47\xfa\x0d\xb5\x81\x71\x02\x02\x02\x64\x8b\x47\xfe\x8f\x4f\xf2\xb8\x0c\x02
\x02\x02\xba\x06\x02\x02\x02\xc5\x47\xee\x03\x02\x02\x02\x51\x89\x5b\xfe\xcf\x82
\x59\xba\x03\x02\x02\x02\xc5\x47\xee\x02\x02\x02\x02\x51\x89\x5b\xfe\xcf\x82\x59
\x59\xcb\xc1\x4a\x67\x6e\x6e\x6d\x22\x75\x6d\x70\x6e\x66\x23\x08\x02

We can use an alpha loader to have an almost alphanumeric shellcode (give me some more time to get rid of the two last non alphanumeric bytes)

$ ./shellforge.py -v0 -R --loader=alpha hello.c 
hAAAAX5AAAAHPPPPPPPPah0B20X5Tc80Ph0504X5GZBXPh445AX5XXZaPhAD00X5wxxUPTYII19hA000
X5sOkkPTYII19h0000X5cDi3PTY19I19I19I19h0000X50000Ph0A0AX50yuRPTY19I19I19I19h0000
X5w100PTYIII19h0A00X53sOkPTYI19h0000X50cDiPTYI19I19hA000X5R100PTYIII19h00A0X500y
uPTYI19I19h0000X50w40PTYII19I19h0600X5u800PTYIII19h0046X53By9PTY19I19I19h0000X50
VFuPTYI19I19h0000X5LC00PTYIII19h0060X5u79xPTY19I19I19I19h0000X5000FPTY19I19h2005
X59DLZPTYI19h0000X500FuPTYI19I19h0010X5DLZ0PTYII19h0006X50Fu9PTY19I19I19I19h0000
X5LW00PTYIII19h0D20X5Lx9DPTY19h0000X5000kPhA0A0X5ecV0PTYI19I19h0B0AX5FXLRPTY19h5
550X5ZZZePTYI19яд

The classic exec /bin/sh :

#include "include/sfsyscall.h"

int main(void)
{
        char *a[] = {"/bin/sh", 0};
        execve(a[0], a, 0);
}

More complex example : to make a shellcode that scans ports of localhost :

#include "include/sfsyscall.h"
#include "include/sfsocket.h"

#define FIRST 1
#define LAST 1024

int main(void) {
        struct sockaddr_in sa;
        int s,i;
        char buf[1024];

        sa.sin_family = PF_INET;
        sa.sin_addr.s_addr = 0x0100007f;

        i=FIRST-1;
        write(1,"begin  [",8);
reopen:
        if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) write(1,"erreur\n",7);
        while(++i < LAST) {
                sa.sin_port = htons(i);
                if (connect(s, (struct sockaddr *)&sa, sizeof(struct sockaddr)) ==  0) {
                    write(1, &i, sizeof(i));
                    close(s);
                    goto reopen;
                }
        }
        write(1,"]end",4);
        close(1);
        exit(0);
}