Since its first release in November 2006, PowerShell has attracted the attention of many Windows hackers and penetration testers. During penetration test engagements I’ve occasionally used some of the excellent PowerShell tools available myself. However, being mostly a UNIX hacker, I managed to avoid learning PowerShell scripting for quite some time… Until a few weeks ago.
It’s all Python’s fault! After being let down by Python EXE packagers, I decided to give PowerShell a go and develop a port of my letmein.py script (itself based on Raphael Mudge’s metasploit-loader). I was surprised and delighted to learn that PowerShell provides a very convenient way to code such a tool and after only a couple of hours of programming I managed to hack together a first working prototype.
The world is soon to end with you developing tool for Windows
— Fabio Pietrosanti (@fpietrosanti) November 1, 2017
In spite of Internet’s reactions to my new interest 😉 I kept on programming. After that first successful run the rest went smoothly. I added support for different payloads, optimized the shellcode injection mechanism, polished the code, and tested it extensively on different Windows versions and architectures. As a result, I now have a fully working PowerShell implementation of the staging protocol used by the Metasploit Framework. Take a look at the screenshots below for a taste of what it can do:
A word to the wise — compared to state-of-the-art penetration and lateral movement techniques that don’t ever touch disk, the described tool has some obvious drawbacks (i.e., the script itself is written to disk and PowerShell script execution must be allowed via the Set-ExecutionPolicy cmdlet). However, it’s effective enough to meet the needs of most Windows penetration testing scenarios and avoid detection by common anti-malware protections. That’s right: you don’t need fancy packers and encoders to escape AVs, you just need to write your own code! In addition, and perhaps most importantly, this tool served as a starting point that made me discover PowerShell scripting… and I’ve got big plans for the future!
(in the picture above: me having big plans for the future)
I’m not going to share the full code just yet, because that will defeat the main purpose of the tool (that is, escaping anti-malware protections by being kept private), wouldn’t it? However, I would like to briefly discuss how it works so that you, dear reader, may develop your own version if you wish 😉
So, here we go. Depending on the parameters specified on the command line and parsed via a Param() block, the script either connects to a reverse_tcp Metasploit handler or opens a port for a bind_tcp handler to connect to. These operations are fairly easy to implement in PowerShell using .NET Framework’s System.Net.Sockets.TcpClient and System.Net.Sockets.TcpListener classes respectively. Did I mention that PowerShell is powerful?!
After the TCP connection is established, the Metasploit staging protocol handler is called. This function reads first a 4-byte length value and then the shellcode of such length. It then stores the shellcode into a System.Byte array. Note that it’s necessary to prepend some Assembly to the shellcode in order to MOV the socket handle into the EDI register, as documented in letmein.py and here.
It’s finally time to execute the Meterpreter payload. In order to do so our script needs to:
- allocate a memory region with RWX (read, write, execute) permissions via the VirtualAlloc() Windows API function (apparently Meterpreter payloads need write access to memory, therefore it’s not possible to first allocate memory with RW permissions via VirtualAlloc() and then change memory permissions from RW to RX via the VirtualProtect() function as I’ve done in my Invoke-Shellcode patch);
- copy the shellcode to the allocated memory region using the [System.Runtime.InteropServices.Marshal]::Copy method;
- execute the shellcode via the CreateThread() Windows API function;
- wait for the shellcode to finish running before allowing the stager to exit, via the WaitForSingleObject() Windows API function;
- perform final cleanup actions.
The only complication is that in order not to touch disk, Windows API functions are accessed via internal .NET Framework’s methods and reflection, as detailed here. This is indeed convoluted but very effective, and it’s the same technique implemented by PowerSploit’s Invoke-Shellcode cmdlet.
Sorry for not publishing the script, thus violating my usual PoC||GTFO policy… If nothing else, I hope to have aroused your curiosity for PowerShell. Stay tuned to my GitHub page, as I may change my mind!