Windows Shellcoding 3 : TCP Reverse Shell using WinSock
So my previous escapades into windows shellcoding led to me choosing to be more ambitious and thus deciding to try and make a Null byte free, Position Independent shellcode and embed that into an windows utility and having the shellcode to be executed by hijacking a ret call.
Since this time I'm planning on connecting to a reverse shell, and using an application which loads ws2_32.dll (winsock2) would be a lot more complex as it is mostly used by browsers, so I decide to use LoadLibraryA from kernel32.dll.
I begin by setting up the stack by adding to esp the 2's complement of 0x648, i.e., subtract 1,608 (0x648) from esp.
Using 2's complement makes sure that there will be no null bytes in the byte code.
Then I retrieve kernel32.dll base address using PEB method.
Once I have the kernel32.dll base address I can now move onto creating the routine for extracting the Number of Exported Functions, Address Table Address, Name Pointer Table Address & the Ordinal Table Address, I discussed more about them in my first shellcoding post.
I made this a callable routine as I intend to use it for when I load ws2_32.dll too.
Now I'd have the all the relevant addresses with this, but how do I search for LoadLibraryA? pushing a string would invite null bytes regardless of how careful I'm being. This was something which was an issue in my previous attempts at shellcoding.
I found my answer while reviewing my class material for cryptology, I can use hashes!
So I researched a bit and found a short hashing algorithm.
This creates a hash which is 4 bytes long and can be easily stored in a 32-bit register, using that I make the hash routine to generate hashes of the names in the Name Pointer Table.
These hashes were compared with the hash of the function I wanted, this latter part was done using a python script I made.
This way I was able get the hash of my desired function for comparison, the x86 hash routine was easily integrated into my previous search & compare routine.
Then I go onto pushing the hash calling the search routine to first compute and then compare the hashes.
Once I've got my function addresses from kernel32.dll, I then push the name of wisock2 library onto the stack in 2's complement form to obfuscate it.
The 2's complement pushing was a convenient little addition I had made to my initial stack pushing script.
This moved the 2's complement string to eax and then negated it before pushing it onto the stack, the prevent null bytes and obfuscated the shellcode.
After extracting the addresses I searched for functions in a similar manner to what I did with kernel32.dll, I searched for WSAStartup, WSASocketA and connect from ws2_32.dll.
Once all function addresses have been stored in the ebp stack, I then call for WSAStartup while pushing the arguments according to Microsoft's documentation:
So I make space for the returned data structure in stack and push the version which I want i.e. 2.2.
This should work fine but if for some reason the return value in eax is not zero, you can additionally search for WSAGetLastError and call it after the error returning function.
It will help understand what exactly is the issue as WSAGetLastError would return the error code which you can look up on Microsoft's website.
Once winsock is successfully started, I create a socket, below in the Microsoft's definition of function for reference.
I make the pushed starting from dwFlags and going to af.
This should create and store the handle to socket in esi without a hitch, still incase there is a value of -1 or 0xFFFFFFFF returned into eax instead of the socket we can always retrieve the exact error code with WSAGetLastError.
Once the socket is created, I would call connect, but for that we need a specific structure called 'sockaddr_in', defined in Microsoft Documentation as follows.
So now not all of the elements are of same sizes so I make sure to adjust for that in my implementation.
I had started using the 2's complement version of things as a quick obfuscation method to help fly under the radar of strings analysers.
Also for the IP, I'm using the one assigned to my Remnux VM which is the other VM in this isolated network.
Once the struct is set up I can now call connect while passing to it the arguments as detailed in Microsoft's function definition.
Optimally, this should return a zero in eax but if it doesn't, the exact error code can always be queried using WSAGetLastError.
While testing the shellcode, this was the only function which was giving me an error repeatedly for invalid size, after cross referencing with the documentation a few times I figured that my order for pushing was causing the issues as I was pushing the name first instead of the namelen.
After connecting I use SetHandleInformation to make sure the socket handle is inheritable and also make it immune to being accidently closed by passing on the flags HANDLE_FLAG_INHERIT & HANDLE_FLAG_PROTECT_FROM_CLOSE.
Now I move onto starting a PowerShell instance using CreateProcessA and passing the handle to the socket to the created process for the I/O.
But CreateProcessA requires STARTUPINFOA, a struct defined by Microsoft as follows:
Even looking at it, this is a big struct, I push the handle to the socket for the standard input, standard output and standard error.
Then LPBYTE was an unfamiliar data type to me so I sift through some more documentation and find that it is a pointer to a byte, and pointers in 32-bit systems are 4 bytes. The two following arguments, i.e., cbReserved2 and wShowWindow however are 2 bytes each.
These both are reserved for use by C-Runtime so we don't touch them.
Now the dwFlags was a bit tricky as the flag I wanted to set was STARTF_USESTDHANDLES, this flag basically signifies that the hStdInput, hStdOutput, and hStdError are to be noted as they contain additional information.
The tricky part is that the flag's value is 0x100, now I can't just push it without introducing null bytes, so after a bit of contemplation I figured a solution.
I push 0xFF into al as that is an 8-bit register, and then increment eax by 1 to turn the value into 0x100.
I also push the other stuff which was null anyways.
The size was calculated as follows:
Handles (12) + CRT Reserved (6) + Show Window (2) + Flags (4) + WindowPos (16) + Title (4) + Desktop (4) + Reserved (4) = 68 bytes
Once that is done I push the size onto stack and pop the pointer to the struct into edi.
Once that is done, I push the 2's complement version of the command line arguments which is just "powershell.exe" to create an instance of it.
I then proceed to call CreateProcessA with creation flag set to CREATE_NO_WINDOW & inherit handles set to true and also pass the pointer to the STARTUPINFOA struct.
After the call I just reset the stack, well as much as I could, I'm running the shellcode under attrib.exe, its going into crash either way :P
I have provided the whole code at the end for refernce, for now lets move onto embedding the shellcode and make sure it is able execute regardless of the address.
I assemble the shellcode and then proceed to strip it of its symbols since they serve no real purpose in the execution of the shell code.
I use CFF explorer to modify the section header of 'attrib.exe' to introduce a new section, lets call it '.rdata'.
The highlighted opcodes are the beginning portion (push ebp mov ebp, esp), honestly I have played around with the shellcode so many times I've practically memorised it XD
So anyways I set the flag for the section to be executable from CFF explorer and rebuild the header and save it in a new file with the same name in the desktop.
I load the modified PE in x32 dbg to view where the section is actually loaded.
It was rather easy to spot in the memory map, then I go to check the section to be able to calculate the offset of my shellcode from the Address of Entry Point.
So now I just traverse into a random function call and use the push this onto the stack to make use of the ret instruction to go to my shell code.
Now I patch the application and save it to test.
I open a listener on my Remnux VM and as soon as I execute the patched PE, I get a connection and no immediate signs on the windows VM.
There is a connection which can be seen from TCP view, but its from an unnamed process with a PID which does not exist.And the only place you'd find the PS instances is if you were looking into the detailed process listing in Task Manager.
So that was it, my little adventure into creating shellcode.
Hope you enjoyed it!
The shellcode can be found here.
Comments
Post a Comment