Introducing SharpSploit: A C# Post-Exploitation Library
Today, I’m releasing SharpSploit, the first in a series of offensive C# tools I have been writing over the past several months. SharpSploit is a .NET post-exploitation library written in C# that aims to highlight the attack surface of .NET and make the use of offensive .NET easier for red teamers.
SharpSploit is named, in part, as a homage to the PowerSploit project, a personal favorite of mine! While SharpSploit does port over some functionality from PowerSploit, my intention is not at all to create a direct port of PowerSploit. SharpSploit will be it’s own project, albeit with similar goals to PowerSploit.
The Appeal of C#
There seems to be a trend developing on the offensive side of the security community in porting existing PowerShell toolsets to C#, particularly with the recent releases from my SpecterOps teammates, including: @harmj0y's GhostPack toolset and @0xthirteen's SharpView. And SharpSploit is another piece to that puzzle. With the added security features in PowerShell (ie. ScriptBlock Logging, AMSI, etc.), it makes sense that red teamers are investing in other options. And C# is the logical next step from PowerShell, seeing that they both are based on the .NET framework and porting toolsets from PowerShell to C# is fairly easy to do.
However, C# does not come without its own set of issues from an offensive perspective. It certainly seems as if optics into .NET are on the way, and from an operator usability perspective we lose quite a bit of flexibility moving from a scripting language like PowerShell to a compiled language like C#.
We also need to start worrying about .NET versions. You’ll find .NET v3.5 on a majority of Windows OS versions by default, but newer Windows 10 and Server 2016 systems will only have .NET v4.0+ installed by default. Another “gotcha” is that .NET is not enabled by default on all Windows OS versions either, you’ll find that it needs to be explicitly enabled on Windows Server 2008 and earlier server OS versions. SharpSploit attempts to deal with this by targeting .NET v3.5 and v4.0 to get the most coverage possible, but you’ll need to be careful to use the correct version on the correct system.
To Console or Not to Console?
The most significant difference you will see between SharpSploit and most other offensive C# libraries that have been released so far, is that there is no SharpSploit.exe
! SharpSploit is designed as a library, so there is only a SharpSploit.dll
.
My intention is for SharpSploit to be primarily used as a library for operators to reference in their own toolsets; however, I anticipate some limitations from this implementation that will likely force me to add a console-based interface eventually. For instance, Cobalt Strike’s execute-assembly
module expects an application to have an EntryPoint (i.e. “main” function) to execute, so SharpSploit
currently does not operate easily with Cobalt Strike. This is a great example of some of the flexibility issues with offensive C# we will have to solve in the transition from PowerShell.
Try not to worry about this too much for now, you’ll see some other creative methods for SharpSploit execution from me here in the near future. :) And I will likely add a follow-up post at some point on convenient methods for executing SharpSploit functions.
SharpSploit
So what exactly does SharpSploit
include? Let’s dive in! SharpSploit
currently includes 4 key high-level namespaces: Credentials
, Enumeration
, Execution
, and LateralMovement
. We’ll walk through the details of each of these individually.
SharpSploit.Credentials
The SharpSploit.Credentials
namespace includes any classes that deal with…well, credentials! Currently, this includes all Mimikatz
functionality as well as token manipulation.
SharpSploit.Credentials.Mimikatz
The SharpSploit.Credentials.Mimikatz
class implements the ability to execute any Mimikatz
command. SharpSploit
’s implementation uses an adapted version of @subtee's PELoader and borrows from @xorrior's implementation as well to load @gentilkiwi’s excellent Mimikatz project.
I’d be remiss if I didn’t also mention @harmj0y’s SafetyKatz project. The primary difference is that SafetyKatz provides convenience for using Mimikatz
on a minidump of lsass.exe, while SharpSploit.Credentials.Mimikatz
opens the user up to any Mimikatz
command.
The following are the primary functions implemented in the SharpSploit.Credentials.Mimikatz
class:
Command()
— Loads the Mimikatz PE withPE.Load()
and executes a chosen Mimikatz command.LogonPasswords()
— Loads the Mimikatz PE withPE.Load()
and executes the Mimikatz command to retrieve plaintext passwords from LSASS. Equates toCommands("privilege::debug sekurlsa::logonPasswords”)
. (Requires Admin)SamDump()
— Loads the Mimikatz PE withPE.Load()
and executes the Mimikatz command to retrieve password hashes from the SAM database. Equates toCommand("privilege::debug lsadump::sam")
. (Requires Admin)LsaSecrets()
— Loads the Mimikatz PE withPE.Load()
and executes the Mimikatz command to retrieve LSA secrets stored in registry. Equates toCommand("privilege::debug lsadump::secrets")
. (Requires Admin)LsaCache()
— Loads the Mimikatz PE withPE.Load()
and executes the Mimikatz command to retrieve Domain Cached Credentials hashes from registry. Equates toCommands("privilege::debug lsadump::cache")
. (Requires Admin)Wdigest()
— Loads the Mimikatz PE withPE.Load()
and executes the Mimikatz command to retrieve Wdigest credentials from registry. Equates toCommands("sekurlsa::wdigest")
.All()
— Loads the Mimikatz PE withPE.Load()
and executes each of the above builtin, local credential dumping commands. (Requires Admin)DCSync()
— Loads the Mimikatz PE withPE.Load()
and executes the “dcsync” module to retrieve the NTLM hash of a specified (or all) Domain user. (Requires Domain Admin (or equivalent rights))PassTheHash()
— Loads the Mimikatz PE withPE.Load()
and executes the “pth” module to start a new process as a user using an NTLM password hash for authentication. (Requires Admin)
SharpSploit.Credentials.Tokens
The SharpSploit.Credentials.Tokens
class implements token manipulation functions, as well as more complex actions that rely on token manipulation. While many token manipulation projects have already been created, SharpSploit.Credentials.Tokens
often borrows from @0xbadjuju's awesome Tokenvator project.
The following are the primary functions implemented in the SharpSploit.Credentials.Tokens
class:
WhoAmI()
— Gets the username of the currently used/impersonated token.ImpersonateUser()
— Impersonate the token of a process owned by the specified user. Used to execute subsequent commands as the specified user. (Requires Admin)ImpersonateProcess()
— Impersonate the token of the specified process. Used to execute subsequent commands as the user associated with the token of the specified process. (Requires Admin)GetSystem()
— Impersonate the SYSTEM user. Equates toImpersonateUser("NT AUTHORITY\SYSTEM")
. (Requires Admin)BypassUAC()
— Bypasses UAC through token duplication and spawns a specified process with high integrity. (Requires Admin)RunAs()
— Makes a new token to run a specified function as a specified user with a specified password. Automatically callsRevertToSelf()
after executing the function.MakeToken()
— Makes a new token with a specified username and password, and impersonates it to conduct future actions as the specified user.RevertToSelf()
— Ends the impersonation of any token, reverting back to the initial token associated with the current process. Useful in conjuction with functions that impersonate a token and do not automatically RevertToSelf, such as:ImpersonateUser()
,ImpersonateProcess()
,GetSystem()
, andMakeToken()
.EnableTokenPrivilege()
— Enables a specified security privilege for a specified token. (Requires Admin)
SharpSploit.Enumeration
The SharpSploit.Enumeration
namespace includes any classes that do enumeration. Currently, this includes some basic local host-based enumeration, network enumeration (i.e. ping/port scanning), and Domain and Net enumeration. I think there’s room for a lot of addition and improvement to this namespace, but it is a start.
SharpSploit.Enumeration.Host
The SharpSploit.Enumeration.Host
class does basic local host-based enumeration. This class is currently very basic. I plan to eventually make some useful additions, but currently there are many other more robust tools, such as Seatbelt.
The following are the primary functions implemented in the SharpSploit.Enumeration.Host
class:
GetProcessList()
— Gets a list of running processes on the system.CreateProcessDump()
— Creates a minidump of the memory of a running process. Useful for offline Mimikatz if dumping the LSASS process. (Requires Admin)GetHostname()
— Gets the hostname of the system.GetUsername()
— Gets the current Domain and username of the process running.GetCurrentDirectory()
— Gets the current working directory full path.GetDirectoryListing()
— Gets a directory listing of the current working directory.ChangeCurrentDirectory()
— Changes the current directory by appending a specified string to the current working directory.RegistryRead()
— Reads a value stored in registry.RegistryWrite()
— Writes a value into the registry.
SharpSploit.Enumeration.Network
The SharpSploit.Enumeration.Network
class includes a threaded TCP ping/port scanner for network enumeration.
The following are the primary functions implemented in the SharpSploit.Enumeration.Network
class:
PortScan()
— Conducts a port scan of specified computer(s) and port(s) and reports open ports.Ping()
— Pings specified computer(s) to identify live systems.
SharpSploit.Enumeration.Domain
The SharpSploit.Enumeration.Domain
class provides libraries for Active Directory domain enumeration. This is a partial port of @harmj0y's PowerView project.
This class does not intend to be a direct or complete port of PowerView, things will be formatted and implemented differently. I should also mention @0xthirteen's SharpView project, which does aim to be a more direct/complete port of PowerView.
SharpSploit.Enumeration.Domain.DomainSearcher
The SharpSploit.Enumeration.Domain.DomainSearcher
class facilitates searching a particular AD domain with a set of credentials.
This is the first major difference you will notice when using SharpSploit.Enumeration.Domain
PowerView. PowerView functions can all be executed independently as “static” functions, conforming to the nature of PowerShell as a scripting language. SharpSploit
embraces the nature of an object-oriented programming language such as C#. You will need to create a DomainSearcher
object that is setup to search a particular domain with a particular set of credentials, and call specific enumeration functions on that DomainSearcher
object.
The DomainSearcher
class contains the following primary functions:
GetDomainUsers()
— Gets a list of specified (or all) userDomainObject
s in the current Domain.GetDomainGroups()
— Gets a list of specified (or all) groupDomainObject
s in the current Domain.GetDomainComputers()
— Gets a list of specified (or all) computerDomainObject
s in the current Domain.GetDomainSPNTickets()
— GetsSPNTicket
s for specifiedDomainObject
s.GetDomainGroups()
— Gets a list ofSPNTicket
s for specified (or all) users with a SPN set in the current Domain.
SharpSploit.Enumeration.Net
The SharpSploit.Enumeration.Net
class continues the partial PowerView port, and includes functions for enumerating local users, groups, logons, and sessions on remote computers by using Windows API functions (similar to the native, Windows “net.exe” utility). A key note is that the “WinNT” PowerView collection method is not supported.
The SharpSploit.Enumeration.Net
class includes the following primary functions:
GetNetLocalGroups()
— Gets a list ofLocalGroup
s from specified remote computer(s).GetNetLocalGroupMembers()
— Gets a list ofLocalGroupMember
s from specified remote computer(s) for a specified group.GetNetLoggedOnUsers()
— Gets a list ofLoggedOnUser
s from specified remote computer(s).GetNetSessions()
— Gets a list ofSessionInfo
s from specified remote computer(s).
SharpSploit.Execution
The SharpSploit.Execution
namespace includes any classes that deal with executing “stuff”. Currently, this includes PE loading, .NET assemblies, and shell/powershell commands.
SharpSploit.Execution.Assembly
The SharpSploit.Execution.Assembly
class includes methods for loading .NET assemblies and executing functions within the assembly through reflection.
The SharpSploit.Execution.Assembly
class includes the following primary functions:
Load()
— Loads a .NET assembly byte array or base64-encoded byte array.AssemblyExecute()
— Loads a .NET assembly byte array or base64-encoded byte array and executes a specified method within a specified type with specified parameters using reflection.
SharpSploit.Execution.PE
The SharpSploit.Execution.PE
class is planned to be a class for loading arbitrary PEs. Currently, it really only serves as a Mimikatz PE loader. This class is an adapted version of @subtee’s PE loader that has been made to support arbitrary PEs. As a warning, I have not been successful in loading any arbitrary PE with this class, so your mileage may vary. I hope that this will eventually be fully implemented.
The SharpSploit.Execution.PE
class includes the following primary functions:
Load()
— Loads a PE with a specified byte array. (Requires Admin)GetFunctionExport()
— Get a pointer to an exported function in a loaded PE. The pointer can then be used to execute the function in the PE.
SharpSploit.Execution.Shell
The SharpSploit.Execution.Shell
class includes methods for executing “shell” (cmd.exe
) commands and PowerShell commands/scripts. It is possible to execute shell commands as an alternative user (provided that you have a valid password), similar to the native Windows runas.exe
executable.
I’m particularly excited about the PowerShellExecute()
function. The PowerShellExecute()
function by default uses @mattifestation’s AMSI bypass and @tifkin_'s PowerShell logging bypass (which bypasses ScriptBlock logging and Module logging). An issue with executing some of these types of bypasses previously through PowerShell is that the bypasses themselves are logged or AMSI-scanned, creating a bit of a chicken and the egg problem. By utilizing C#, we can execute these bypasses in C# prior to the execution of any PowerShell, resulting in log-free and AMSI-free PowerShell execution!
The SharpSploit.Execution.Shell
class includes the following primary functions:
PowerShellExecute()
— Executes specified PowerShell code using the System.Management.Automation.dll and bypasses AMSI, ScriptBlock Logging, and Module Logging (but not Transcription Logging).ShellExecute()
— Executes a specified Shell command, optionally with an alternative username and password. Equates toShellExecuteWithPath(ShellCommand, “C:\\WINDOWS\\System32")
.ShellExecuteWithPath()
— Executes a specified Shell command from a specified directory, optionally with an alternative username and password.
SharpSploit.Execution.ShellCode
The SharpSploit.Execution.ShellCode
class includes a method for executing shellcode. Shellcode execution is accomplished by copying it to pinned memory, modifying the memory permissions with Win32.Kernel32.VirtualProtect()
, and executing with a .NET delegate
. This class is based on code written by @enigma0x3.
The SharpSploit.Execution.ShellCode
class includes the following primary function:
ShellCodeExecute()
— Executes a specified ShellCode byte array by copying it to pinned memory, modifying the memory permissions withWin32.Kernel32.VirtualProtect()
, and executing with a .NETdelegate
.
SharpSploit.Execution.Win32
SharpSploit.Execution.Win32
is a large library of PInvoke signatures and structures for Win32 API functions used by various SharpSploit
functionality. www.pinvoke.net was an invaluable resource for the implementation of this class. I will not attempt to document all of the signatures here, but feel free to check them out in the Win32.cs
file.
SharpSploit.LateralMovement
The SharpSploit.LateralMovement
namespace includes classes that allow for any type of code execution on remote computers. Currently, this only includes WMI lateral movement, but others will be coming soon!
SharpSploit.LateralMovement.WMI
The SharpSploit.LateralMovement.WMI
class includes basic WMI lateral movement capabilities. Currently, the only capability is to spawn a process on a remote system using the Win32_Process.Create
WMI method with a specified username and password.
The SharpSploit.LateralMovement.WMI
class currently includes only the following primary function:
WMIExecute()
— Execute a process on a remote system with Win32_Process Create with specified credentials.
Testing and Documentation
Complete documentation for SharpSploit
is available at https://sharpsploit.cobbr.io/api/index.html!
The SharpSploit
project also includes SharpSploit.Tests
, hooray for unit testing! By no means have I implemented complete coverage in these unit tests, but I am still working at it. I think it would be great if we as a security community could try to up our software development game a bit and include unit tests for offensive projects. They provide great examples for how to use our toolsets, promote code quality, and are useful for contributors.
More To Do
This is just the beginning for SharpSploit
! You’ll notice I alluded to lots of needed additions to SharpSploit
throughout the description above. I plan to continue working on and maintaining SharpSploit
, so hopefully you will see lots of updates coming soon. The format of SharpSploit
as a library is also very conducive to contribution from others. I am very open to pull requests and hope that others will want to contribute to the project to make it better for everyone.
Defensive .NET
This is really an offensive post and meant as an intro to SharpSploit
, but I will at least touch on the defensive side. After educating myself a bit, I’ll plan on a more thorough defensive blog post as a follow up.
To begin discussing defense for .NET, it’s important to clarify terms. I’ve used “.NET” and “C#” almost interchangably throughout this post, but there is a subtle distinction. The .NET framework is a library and runtime for Windows for “managed code”. “Managed code” is any code that compiles to CIL (Common Intermediate Language, formerly referred to as “MSIL”) and will be managed by the CLR (Common Language Runtime), which is the runtime for the .NET framework and converts CIL to “native” machine code. C# is an example of a language that uses managed code that compiles to CIL. Further, C# was created specifically to target .NET and it’s object model aligns to the .NET object model. Other languages that can compile to CIL (and thus, can be executed under the .NET framework) include VB.NET and JScript.NET.
So while we’ve talked quite a bit about C# from the offensive side, from a defensive perspective what we really care about is .NET. Specifically, we care about the CIL code being interpreted by the CLR. The bad news is that traditionally, defenders do not have much optics into what is happening in .NET code. And even if they did, it may be hard to identify malicious .NET. In fact, that admission in this tweet from @blowdart was what first got me excited about offensive .NET:

The good news is that this appears to be changing. I alluded to this at the beginning of the post, but it does seem as if .NET optics are in the works (discovered by @mattifestation):

However, as far as I know, there is no official guidance from Microsoft on how to utilize these optics quite yet. Though hopefully we will get some guidance and documentation as this appears to still be in the works.
Without these optics, what other options do we have? Well the good news is that there is still very few ways to execute .NET without touching disk at some point. And if you can recover the .NET assembly off of disk, it is very easy to reverse the assembly to source code and begin to identify key indicators (CIL code is still bytecode!).
The easiest and most popular way to execute malicious .NET assemblies is through the System.Reflection.Assembly.Load()
method. So how will operators execute this method to kick off their malicious .NET assembly? Typically, either through powershell.exe
or through scriptlet-based execution techniques such as regsvr32.exe
and wmic.exe
through techniques demonstrated by the great DotNetToJScript project by @tiraniddo. While those scriptlet-based techniques do have the ability to execute remotely hosted scriplets, this still results in the scriptlet hitting disk in a predictable location under %LOCALAPPDATA%\Microsoft\Windows\Temporary Internet Files\
(which I’m not sure all red teamers have yet realized). This leaves powershell.exe
as the last real way to execute .NET assemblies without touching disk, and PowerShell has great logging capabilities to ensure you capture the assembly in ScriptBlock logs! Once you have captured the assembly off disk or in logs, you will be able to easily reverse it to source and analyze using a .NET debugger, such as dnSpy.
I hope this is a decent, if not brief, start to detecting malicious .NET. Again, once I further educate myself, I’ll try to follow up with a more detailed post.
Conclusion
This is just the beginning for SharpSploit
! You’ll notice I alluded to lots of needed additions to SharpSploit
throughout this post. I plan to continue working on and maintaining SharpSploit
, so hopefully you will see lots of updates coming soon.
I hope that others find SharpSploit
helpful, particularly for those trying to diversify their toolset from PowerShell. And as I hinted to at the beginning of this post, SharpSploit
is the first in a series of C# tooling that I have been developing over the last few months, so expect to see much more here soon!
Credits
I owe a ton of credit to a lot of people. Nearly none of SharpSploit
is truly original work. SharpSploit
ports many modules written in PowerShell by others, utilizes techniques discovered by others, and borrows ideas and code from other C# projects as well. With that being said, I’d like to thank the following people for contributing to the project (whether they know they did or not :)):
- Justin Bui (@youslydawg) — For contributing the
SharpSploit.Enumeration.Host.CreateProcessDump()
function. - Matt Graeber (@mattifestation), Will Schroeder (@harmj0y), and Ruben (@FuzzySec) — For their work on PowerSploit.
- Will Schroeder (@harmj0y) — For the PowerView project.
- Alexander Leary (@0xbadjuju) — For the Tokenvator project.
- James Foreshaw (@tiraniddo) — For his discovery of the token duplication UAC bypass technique documented here.
- Matt Nelson (@enigma0x3) — For his Invoke-TokenDuplication implementation of the token duplication UAC bypass, as well his C# shellcode execution method.
- Benjamin Delpy (@gentilkiwi) — For the Mimikatz project.
- Casey Smith (@subtee) — For his work on a C# PE Loader.
- Chris Ross (@xorrior) — For his implementation of a Mimikatz PE Loader found here.
- Matt Graeber (@mattifestation) — For discovery of the AMSI bypass found here.
- Lee Christensen (@tifkin_) — For the discovery of the PowerShell logging bypass found here.
- All the contributors to www.pinvoke.net — For numerous PInvoke signatures.