About The Project
With IDA Pro’s recent announcement of going to a subscription model it has some revisiting the current state of available decompilers. Off the top of my head you have Hopper, Ghidra, Radare2/Cutter, and of course Binary Ninja. Each of these utilities have their own pros and cons and also considering how frequently you’re spending time performing reverse engineering one may make more sense than the other with regards to monetary investment.
One really neat feature of Binary Ninja is its Shellcode Compiler. The shellcode compiler allows the end user to create PIC shellcode for process injection. Support for different targets (Windows/OSX/FreeBSD/Linux) as well as API bindings make this a very cool component to the Binary Ninja tool suite. As a Radare2 fan, I was curious if similar capabilities existed within the framework and how easily one could integrate said component into a workflow for offensive payload testing. This blog post will dive into ragg2, a shellcode generation utility that’s apart of the Radare2 suite.
Radare2 Shellcode Generation - ragg2
ragg2 is a lesser known utility in the Radare2 suite that is used for shellcode generation. It can be used for generating shellcode from C code as well as a custom format that’s not particularly well documented, and I couldn’t find anything beyond the man page about it. Per-the man page, the code is relocatable so it can be injected into a remote process and executed. For those poking at the inner workings of Linux/OS X processes, this can be particularly interesting. An important note prior to going further, this is a experimental tool, so consider referencing the source code for a deeper understandng when and if things break.
Shellcode Generation & Unix As An IDE
If you’ve never read the blog series “Unix as an IDE”, I highly recommend spending some time going over it. The jist, is how you can leverage your underlying Operating System (in this case Unix/Linux) and built in utilities for improving your development workflow without heavy IDEs getting in the way. For example, lets take “artifact publishing” some IDEs offer. Why not just rsync a directory to a remote server and execute a build script? If something goes awry, debugging a bash script can be easier than looking up documentation about a commerical product. I’m not saying there isn’t value in leveraging an IDE, but sometimes all of those features can get in the way of productivity, but I digress, back to shellcode generation.
As mentioned above, ragg2 can generate position independent shellcode via a C program or a custom format. For this example, we’ll look at a small example in C.
The code below is simple and executed a user defined command, in this case we’ll print the output of /etc/passwd
.
int main() {
char *cmd[3];
cmd[0] = "/bin/cat";
cmd[1] = "/etc/passwd";
cmd[2] = "NULL";
execve(cmd[0], cmd, NULL);
return 0;
}
Shellcode Generation Under The Hood
ragg2 actually wraps gcc + rabin2 (another Radare2 utility) to perform the following:
- Generate the assembly from the source code. The following gcc one-liner is used by ragg2.
gcc -fPIC -fPIE -fpie -fpic -m64 -fno-stack-protector -nostdinc -include /usr/local/include/libr/sflib/linux-x86-64/sflib.h -z execstack -fomit-frame-pointer -finline-functions -fno-zero-initialized-in-bss -o 'shell.c.tmp' -S 'shell.c'
# note, The "shell.c.tmp" file is then copied to "shell.c.s" with the following prepended:
.section .text
.global main.type main, @function
jmp main
- Compile into an object file.
gcc -fPIC -fPIE -fpie -fpic -m64 -nostdlib -o 'shell.c.o' 'shell.c.s'
- Leverage rabin2 to pull the
.text
section.
rabin2 -o 'shell.c.text' -O d/S/'.text' 'shell.c.o'
This entire process is wrapped for us with the ragg2 utility, but understanding the steps involved enables the curious hackerto debug when and if things go wrong.
It can be handy to have a local shellcode runner to ensure your shellcode works as expected prior to adding the additional complexity of process injection.
Also be aware that by default ragg2 will generate x64 bit shellcode. To specify 32 bit leverage the -b
command line flag (ex -b 32). For different architectures, use -a
.
Integrating into Shellcode Runners
After ensuring our code is correct, ragg2 can be executed as follows to generate shellcode from our C code. The z
command line argument generates a C style string that can be used for payload execution. The example shellcode runner here can be used for execution.
After executing the follow command we should be left with a C style string shellcode payload.
The default output of ragg2 is quite verbose and can be seen below.
For generating a C style array to easily swap into shellcode runners, execute
ragg2 -f c -o payload.h shell.c
This will result in the following output.
#define _BUFFER_SIZE 162
const uint8_t buffer[_BUFFER_SIZE] = {
0xeb, 0x58, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0f, 0x1f, 0x00, 0x48, 0x89, 0x7c, 0x24,
0xe8, 0x48, 0x89, 0x74, 0x24, 0xe0, 0x48, 0x89, 0x54, 0x24,
0xd8, 0x48, 0x8b, 0x7c, 0x24, 0xe8, 0x48, 0x8b, 0x74, 0x24,
0xe0, 0x48, 0x8b, 0x54, 0x24, 0xd8, 0xb8, 0x3b, 0x00, 0x00,
0x00, 0x0f, 0x05, 0x48, 0x89, 0x44, 0x24, 0xf8, 0x48, 0x8b,
0x44, 0x24, 0xf8, 0xc3, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x63,
0x61, 0x74, 0x00, 0x2f, 0x65, 0x74, 0x63, 0x2f, 0x70, 0x61,
0x73, 0x73, 0x77, 0x64, 0x00, 0x4e, 0x55, 0x4c, 0x4c, 0x00,
0x48, 0x83, 0xec, 0x28, 0x48, 0x8d, 0x05, 0xdb, 0xff, 0xff,
0xff, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8d, 0x05, 0xd9, 0xff,
0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0x8d, 0x05,
0xd9, 0xff, 0xff, 0xff, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48,
0x8b, 0x54, 0x24, 0x10, 0x48, 0x8b, 0x04, 0x24, 0x48, 0x89,
0xe1, 0x48, 0x89, 0xce, 0x48, 0x89, 0xc7, 0xe8, 0x78, 0xff,
0xff, 0xff, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc4,
0x28, 0xc3
};
Putting it All Together - Build & Run
A simple bash script can wrap the commands discussed above and be used inconjunction with a build system to further improve your shellcode development workflow. While not nearly complete, the idea of putting together a simple template script in conjunction with a wrapper script to substitute values shows a workflow of leveraging Radare2 output to sed replace variables to dynamically build shellcode runners
#define SCSSIZE SIZE_HERE
// Radare2 payload here
unsigned char b64PAYLOAD = "PAYLOAD_HERE";
int main(int argc, char *argv[]) {
printf("[*] Shellcode Size is %d", SCSSIZE);
printf("[*] Shellcode is %s", b64PAYLOAD);
// perform_execution_here
return 0;
}
The build script can be seen below.
#!/bin/bash
cp payload_runner.orig payload_runner.c
ragg2 -z shell2.c > payload && \
clear && \
export PAYLOAD_SIZE=$(wc payload | cut -f 7 -d ' ') && \
export PAYLOAD=$(tail -n 1 payload | base64 -w0) && \
sed -i -r "s/PAYLOAD_HERE/$PAYLOAD/" payload_runner.c && \
sed -i "s/SIZE_HERE/$PAYLOAD_SIZE/" payload_runner.c && \
gcc payload_runner.c -o /tmp/payload_runner;
echo -e "[*] Payload generated!"
Finally, we can execute said payload and alas it works on my Fedora test host.
....truncated.....
abrt:x:173:173::/etc/abrt:/sbin/nologin
flatpak:x:984:981:User for flatpak system helper:/:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
gnome-initial-setup:x:983:980::/run/gnome-initial-setup/:/sbin/nologin
...truncated...
Beyond The Blog
The Radare2 framework provides a lot of functionality for reverse engineering, debugging, malware analysis and shellcode generation. Understanding the fundamentals of how the utilities work is critical to knowing their left and right bounds and where you may run into issues. This is the last blog from Arch Cloud Labs for the year, thank you for reading and if I got something wrong let me know!