Detecting PoshC2 – Indicators of Compromise

As a counterpart to the release of PoshC2 version 6.0 we are providing a list of some of its Indicators of Compromise (IoCs), particularly as used out-of-the-box, as well as some other effective methods for detecting it in your environment.

We also introduce the new PoshC2 Detections GitHub repository at that will be continually updated as development continues, in order to assist blue teams with detecting PoshC2, particularly when used by less sophisticated attackers that do not alter or configure it to change the default IoCs. We encourage the community to contribute to and help update and improve this repository.

It is worth noting that PoshC2 is open source, so while these are IoCs for PoshC2 if used it its default state, ultimately it can be altered either through configuration or by changing the source code itself. The default configuration is subject to change, however where possible the location of that value is pointed out to the reader in order to allow these values to be monitored and updated, in addition to providing the GitHub repository.


One way to detect PoshC2 is to focus on the communications. Compromised hosts have to communicate with the C2 server somehow in order to pick up tasks and return task output. This is unavoidable and PoshC2 can currently only do this through the use of HTTP(S) requests.

That isn’t to say this this makes it easy to detect; a huge amount of HTTP traffic is present in most environments and the flexibility of the protocol allows for traffic to be hidden and routed through legitimate websites using techniques such as Domain Fronting and reverse proxies.

An example of HTTP comms when domain fronting.

An example of HTTP comms when domain fronting.

Something very helpful for catching C2 communications is SSL Inspection. By being able to inspect SSL traffic leaving the perimeter, the contents of that traffic can be checked and statistics acquired for detecting C2 communications. Without this, network defenders are largely blind, particularly against domain fronted communications which travel via legitimate third-party websites.

If SSL Inspection is implemented then the HTTP traffic can be viewed, and while PoshC2 encrypts the contents of the HTTP bodies, the HTTP URLs and headers can still be viewed.


PoshC2 has two different ways of generating URLs to use for communications. Operators can either use a static list of URLs or provide a wordlist from which random URLs will be generated, and these files are stored at resources/urls.txt and resources/wordlist.txt respectively, with the static URL list being the default option. These URLs are then loaded into the database when the project is first created, and a random URL is chosen by each implant each time it beacons. The default URL list is below:

While these URLs were originally copied from legitimate requests, if you see several of them being repeated to a site, particularly if they do not seem relevant to that site and if the response does not make sense, then it could be PoshC2 beacon traffic.

HTTP Responses

PoshC2 also has static HTML responses that it responds with. The default is six HTTP 200 responses and one 404 response. These are stored in files at resources/responses/ and also loaded into the database when the server is first created. The server responds with a random 200 response to POST requests that do not error or require a specific response, and with the single 404 response to all unexpected URLs or when the C2 server errors. Other responses return context relevant data, such as tasks, implant code and so on.

This static HTML file then at resources/responses/404_response.html is an IoC and if returned from a webserver that you are investigating is suggestive of PoshC2. Similarly, the 200_response* files in the same directory are IoCs if returned by POST requests.

Note however, that it is recommended that operators change these files before creating a PoshC2 project, as is the use of a C2 proxy, so as with the other indicators the absence of this particular response is not unexpected for a PoshC2 installation.

SSL Certificate

PoshC2 by default creates a self-signed certificate for its HTTP server, the values for which are stored in poshc2/server/ file. These values are not in the ‘normal’ configuration file config.yml and are less documented and are therefore harder to change.

An experienced operator will not expose their C2 server to the internet, but will instead use a proxy server with a valid certificate and filter firewall traffic to the C2 server that is not from that proxy, however if these steps are not taken and a certificate with the below values is presented then it is another strong indicator that PoshC2 is in use, likely by less sophisticated adversaries.

The issuer data can be viewed using Chrome and the default values suggest this is a PoshC2 server.

The issuer data can be viewed using Chrome and the default values suggest this is a PoshC2 server.

JA3 Signatures

Another method for signaturing C2 traffic without SSL Inspection is to fingerprint the Client Hello packet that initialises the TCP connection. The specific bytes that make up the packet are dependent upon the type of connection being formed and the underlying packages and methods used to do so.

As these packets are sent before encryption has been established they are cleartext and can be intercepted and read. It turns out that the packet contents are quite unique and can be fingerprinted. Salesforce have an excellent blog post on JA3 fingerprinting, but essentially the details are extracted from the packets that make up this TCP handshake and hashed to create a quick and easy signature, that can be used to identify not only that PoshC2 is in use, but also the specific implant type.

The current Windows 10 PoshC2 signatures are below:

PowerShell: c12f54a3f91dc7bafd92cb59fe009a35

Sharp: fc54e0d16d9764783542f0146a98b300

JA3 fingerprinting has been incorporated into security products such as Splunk and Darktrace to provide quick and easy identification of C2 traffic, and can be utilised with these fingerprints. These fingerprints are available and will be maintained in the PoshC2 Detections GitHub repository.


Another mechanism for detecting C2 traffic, with or without SSL Inspection, is through the use of telemetry.

PoshC2’s default values for beacon time and jitter in the config.yml file are 5 seconds and a 20% jitter, as can be seen below, which is both a fast beacon rate and a small jitter, making it relatively easy to detect using this method.

With these values the implants are going to beacon every 4-6 seconds by default, with an average of 5 seconds.

After capturing some traffic, filtering by the C2 server host and only checking TLS Client Hello packets, we can see that the TLS connection is created roughly every five seconds, confirming what we expect.

We can see the difference between each session initialisation is between 4 and 6 seconds.

We can see the difference between each session initialisation is between 4 and 6 seconds.

By exploring this small sample of data, we can determine that the average time between the requests is 5.05 seconds, with a standard deviation of 0.81 or 16%, close to the 5 second beacon with the 20% jitter we expect from PoshC2’s defaults.

Security products that can provide telemetry data, e.g. Splunk can be used to detect C2 traffic in this way by checking for repetitive beacons at a relatively fixed frequency, whether it’s at the above defaults for PoshC2 or at any frequency, given that it is configurable.

PowerShell Implant

PoshC2 has three implant types; PowerShell, C#, and Python, with the latter being a lightweight implant type mostly intended for compromising *nix hosts.

The PowerShell Implant runs by loading System.Management.Automation.dll, the engine behind PowerShell.exe, into the desired process and executing PowerShell commands directly using this DLL.

The PowerShell implant supports full PowerShell execution from any process (here netsh.exe) by loading System.Management.Automation.dll.

The PowerShell implant supports full PowerShell execution from any process (here netsh.exe) by loading System.Management.Automation.dll.

While this does not utilise PowerShell.exe, bypassing many restrictions and controls, it is still subject to PowerShell specific constraints such as Constrained Language mode and ScriptBlock logging. The latter, in particular, can be used to detect PoshC2 and any other malicious PowerShell commands, whether via PowerShell.exe or otherwise.

Note, however, that ScriptBlock logging was only enabled in PowerShell v5.0 and above, so if PowerShell v2.0 is available on the target then a downgrade attack can be performed and PowerShell 2.0 used, bypassing Constrained Language mode, ScriptBlock logging, AMSI and other controls added in v5.0.

PS Logging

Enabling ScriptBlock logging and Transcript Logging for PowerShell allows logging of processes running PowerShell commands.

There are multiple IoCs here, not least that the Host Application is not PowerShell.exe, as should be expected, but instead netsh.exe.

PowerShell transcripts provide valuable information. Alerting on Host Applications that are not PowerShell.exe is a good way to find PowerShell implants from any C2 framework.

PowerShell transcripts provide valuable information. Alerting on Host Applications that are not PowerShell.exe is a good way to find PowerShell implants from any C2 framework.

Similarly in the Event Viewer for ScriptBlock logging:

PowerShell ScriptBlock logging can reveal a wealth of information, here including the beacon URLs for PoshC2 and again netsh.exe as a host process.

PowerShell ScriptBlock logging can reveal a wealth of information, here including the beacon URLs for PoshC2 and again netsh.exe as a host process.

This also includes the ‘command line’ of the ScriptBlock being executed, which includes the beacon URLs from which a random element is being chosen, therefore listing all the C2 URLs that are in use by this implant.

Aside from the wealth of information ScriptBlock logging provides for any threat hunter, an event command line specific to PoshC2 that can be used to identify the PowerShell implant is below:

The $ServerClean variable is specific to PoshC2 and a clear IoC.

The $ServerClean variable is specific to PoshC2 and a clear IoC.

CommandLine= $ServerClean = Get-Random $ServerURLS

This ScriptBlock is part of the beaconing process and will be repeated frequently in the Event Viewer making it easy to identify.


As mentioned earlier, the PowerShell implant does not function by invoking PowerShell.exe, but instead by loading System.Management.Automation.dll into the implant process.

This means that this is an IoC, and if we find a process that should not have this DLL loaded, particularly if it is an unmanaged code binary (so not .NET) then it is highly likely that this is a process that has been injected into by a PowerShell implant, PoshC2 or otherwise.

System.Management.Automation.dll, the engine behind PowerShell, is a .NET library.

System.Management.Automation.dll, the engine behind PowerShell, is a .NET library.

netsh.exe is a C++ binary and should not be loading .NET libraries.

netsh.exe is a C++ binary and should not be loading .NET libraries.

We can see this DLL loaded into an implant process using Process Hacker:

System.Management.Automation.dll loaded into netsh.exe.

System.Management.Automation.dll loaded into netsh.exe.

Similarly we can view the .NET Assemblies in the process:

We can view the .NET Assemblies in the netsh.exe process and see PowerShell is loaded.

We can view the .NET Assemblies in the netsh.exe process and see PowerShell is loaded.

This tab shouldn’t even be present for a genuine netsh.exe process as it is unmanaged code!

The module is not loaded in a genuine netsh process and the .NET tabs are not available.

The module is not loaded in a genuine netsh process and the .NET tabs are not available.

An operator can be smarter about their migration by injecting into .NET processes, however it is still unlikely that a legitimate process (that isn’t PowerShell.exe) would load System.Management.Automation.dll, so this is a great IoC to look out for in your environment.

This can be implemented at scale by searching for loaded modules in an EDR product, for example in CarbonBlack with a query of:

modload:System.Management.Automation* AND -process_name:powershell.exe AND process_name:powershell_ise.exe

This searches for process with System.Management.Automation.dll or (the Native Image version) loaded into a process when that process is not PowerShell.exe or PowerShell_ISE.exe. Other legitimate exclusions may need to be added for your particular environment.

Here we can see various processes that have loaded System.Management.Automation.dll into memory that are likely implants.

Here we can see various processes that have loaded System.Management.Automation.dll into memory that are likely implants.

C# Implant

The C# or Sharp implant is PoshC2’s ‘next gen’ implant, written (unsurprisingly) in C#. The key difference from a detection perspective is that this implant does not require loading System.Management.Automation.dll in order to function. Most of the functionality of the C# implant is custom-written and while it can load System.Management.Automation.dll in order to execute PowerShell, this is an operator decision and is by no means necessary.

A similar process to the above can be applied, but is a little harder to implement. .NET processes load the mscoree.dll library which is the core library behind the .NET CLR (Command Language Runtime), so again any unmanaged code processes that are loading this library could have been migrated into. The issue here is finding these processes, as it’s not as simple as searching for just ‘module loaded and process name is not powershell.exe’.

A blacklist can be created of common target processes that are unmanaged, always available and should not be loading the .NET runtime, and these can be monitored, as well as noting this during manual triage.

Implant loads mscoree.dll if it is not already present in the process – another IoC if the process is supposed to be unmanaged.

A C# implant loads mscoree.dll if it is not already present in the process – another IoC if the process is supposed to be unmanaged.

Note that the PowerShell implant will also load this library as it is also a .NET process, however the presence of System.Management.Automation.dll marks it as an implant that can run PowerShell.

The C# implant has the ability to load compiled binaries into memory over C2 and run them, which is extremely powerful. There are indicators of this however, as a new virtual runspace is created for each module and their namespace is listed in the AppDomain to which they are loaded.

Viewing the .NET Assemblies in Process Explorer or Process Hacker for example then reveals what modules have been run.

In the C# implant loaded modules have their namespace visible in the AppDomain, making it clear what has been loaded into the implant.

In the C# implant loaded modules have their namespace visible in the AppDomain, making it clear what has been loaded into the implant.

Above we can see the Core and dropper_cs modules that make up the core functionality of the C# implant, as well as some native modules from Microsoft that are required to run. These modules will always be present in the PoshC2 C# implant and are a clear IoC. We also see Seatbelt and SharpUp, two common C# offensive modules from SpectreOps, and we can surmise that they have been on the target.

In General


We have used netsh.exe as the example implant process in this post and that is for a good reason. The default migration process for PoshC2 in the C# and PowerShell implants is C:\Windows\System32\netsh.exe, so when the migrate or inject-shellcode commands are used and a specific process ID or name is not set, then a new netsh.exe process is spawned and migrated into.

This itself is a common IoC for PoshC2, as netsh.exe is not typically frequently run in environments, and certainly not on most end user’s hosts. Therefore, if there is a sudden uptick in the number of these processes being run in the environment or if several are running on a host then it could be worth investigating.


PoshC2 has three quick persistence commands available to the PowerShell implant. Each of these installs a PowerShell.exe one liner payload to the registry in the key at HKCU\Software\Microsoft\Windows\CurrentVersion\themes with a name Wallpaper777, Wallpaper555 or Wallpaper666, depending on the command being run.

This payload is then triggered by either:

  • A registry key at HKCU\Software\Microsoft\Windows\CurrentVersion\run with the name IEUpdate
  • A Scheduled Task, also with the name IEUpdate
  • A shortcut file placed at %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\IEUpdate.lnk

All of these can be alerted upon and used to determine that the adversary using PoshC2, in addition to alerting on the invocation of PowerShell.exe with encoded parameters, and so on.

Binary payloads

Some of the more common payloads that are dropped on targets are the PoshC2 executables and DLLs that can be run using rundll32.exe.

For the DLLs, there are different versions for PowerShell and Sharp implants across versions 2 and 4 of PowerShell and x86 and x64 bit architectures, however all the DLLs have a single entry-point common to all: VoidFunc.

All the DLL payloads have the same single entry point of VoidFunc.

All the DLL payloads have the same single entry point of VoidFunc.

This entry-point is hard-coded in PoshC2 and cannot be changed without hacking the compiled binary itself.

For the common executable and DLL payloads we’ve also added Yara rules for detecting them. These are based on signaturable parts of the binaries that will not change across different installs of PoshC2, for example with different comms options. These are also available in the new PoshC2 Detections GitHub repository.

In Summary

We’ve looked at a few different detections for catching PoshC2 when used out-of-the-box. Using these in your environment will help protect against the less sophisticated users of PoshC2 in addition to further understanding how the tool works.

Any new detections or amendments can be added to the repository and we encourage the community to add their own detections or rules and configurations for other security tools to help build a centralised data store for everyone.

Introducing PoshC2 v4.8 – includes C# dropper, task management and more! – Part One

We recently released version 4.8 of PoshC2, which includes a number of fixes and improvements that help facilitate simulated attacks. This is the first post in a series of posts that will include some of the details around the fixes and updates, alongside a number of other posts which will show some of the other cool features we have been working on in the background.

C Sharp (#)

As of PoshC2 version 4.6, a C# implant has been available. The main driver behind this implementation was to stay clear of System.Management.Automation.dll when an environment is heavily monitored and the EDR product can detect loaded modules inside a running process. Granted, not all EDR products are currently doing this, as it can create a hit on performance at the endpoint level, but its important to understand the OPSEC implications of running different C2 droppers.

This has been a work in progress since the release and is continually improving, and we believe this will be the way forward in months to come against advanced blue teams with a good detection and response capability across the organisation. Currently the implant is fully functional and allows an operator to load any C# assembly and execute this in the running process. This allows the user to extend the functionality massively because they’re able to load all the great modules out there in the wild, created by other infosec authors. The way this is loaded uses the System.Reflection namespace. The code can then be called using .NET reflection, which searches inside the current AppDomain for the assembly name and attempts to either run the entry point given or the main method of the executable. An example usage is as follows, for both run-exe and run-dll:



Task Management

One of the issues we’ve overcome in this release was around tracking tasks; there was no way to determine what output related to which issued command. This was largely due to the implant not using task ID’s that were tracked throughout the entire command process flow.

Typically, this was fine because you know what command you’re running, but when multiple people are working on the same instance, or if multiple similar commands are run, then it could be difficult to figure out what output came from which command. This also made tracking failed commands fairly difficult if not impossible to find. The following screenshots shows the output inside the C2Server and the CompletedTasks HTML file:

Figure 1: How commands were issued and returned against an implant

Figure 2: The old format of the tasks report

Furthermore, tasks were only logged in the database when the implant responded with some output. Now, tasks are inserted as soon as they are picked up by the implant with a start time, and updated with a completed time and the desired output when they return. This allows us to track tasks even if they kill the implant or error and never return, and to see how long they took. It also allows us to reference tasks by ID, allowing us to match them in the C2Server log and to only refer to the task by its ID in the response, decreasing message length and improving operational security. An example of the output is shown below:

Figure 3: The new task logging

The generated report then looks like this:

Figure 4: The new report format

User Logging

The astute amongst you will have noticed the new User column in the report above. Another improvement that has been made in relation to tracking tasks is user logging. Now when you start the ImplantHandler you are prompted for a username; it is possible to leave this blank if required, but when PoshC2 is being used as a centralised C2Server with multiple users it’s important to track which user ran which task as shown in the examples below:

Figure 5: You are now prompted for a username when you start the ImplantHandler

All tasks issued from that ImplantHandler instance will be logged as that user, both in the C2Server log and in the report.

Figure 6: If a username is set it is logged in the task output

Figure 7: The username is also logged for the task in the report

For scripting and/or ease of use, the ImplantHandler can also be started with the -u or --user option, which sets the username, avoiding the prompt:

python --user "bobby b"

Beacon Timing

The way beacon sleep times were handled was inconsistent amongst implants, so now we’ve standardised it. All beacon times must now be in the format of value and unit, such as 5m, 10s or 2h. This is then displayed as such for all implant types in the ImplantHandler. As seen below, the fourth column states the current beacon time in seconds, whereas now we show only the output in the newer format.

Figure 8: The old beacon time format

Figure 9: The new beacon time format

Validation has also been added for these, so attempting to set an invalid beacon time will print a suitable error message and do nothing.

Figure 10: The validation message if an invalid format is set

We’ve also changed the implant colour coding so that they are only flagged as timing out if they haven’t checked in for a multiple of their beacon time, as opposed to a hard coded value.

Previously the implants would be coloured as yellow if they hadn’t checked in for 10 minutes or more, and red for 60 minutes or more. Now they are coloured yellow if they have not checked in for 3x beacon time, and red for 10x beacon time, granting far more accurate and timely feedback to the operator.

Figure 11: Implant colour coding has been improved so that the colour is dependent on the beacon time


The C2Viewer was a legacy script used to just print the C2Server log, useful when multiple people want to be able to view and manipulate the output independently.

There were a few issues with the implementation however, and there was a possibility that it would miss output as it polled the database. Additionally, as this was an additional script, it added maintenance headaches for updates to task output.

This file has now been removed, and instead if you want to view the output in the same way, we recommend that you run the C2Server and pipe it to a log file. You can print the log to stdout and a log file using tee:

python -u | tee -a /var/log/poshc2_server.log

This output can then be viewed and manipulated by anyone, such as by using tail:

tail -f -n 50 /var/log/poshc2_server.log

This method has the added benefit of storing all server output. While all relevant data is stored in the database, having a backup of the output actually seen in the log during usage can be extremely useful.

Further details can be found in the

Internal Refactoring

We’re also making strides to improve the internals for PoshC2, refactoring files for clarity, and cutting cyclic dependencies. We aim to modularise the entire code base in order to make it more accessible and easier to maintain, including making changes, but as this is a sizeable change we’ll be doing it incrementally to limit the impact.


There have been quite a few changes made, and we’re aiming to not only improve the technical capabilities of PoshC2, but also the usability and maintainability.

Naturally, any changes come with a risk of breaking things no matter how thorough the testing, so please report any issues found on the GitHub page at:

The full list of changes is below, but as always keep an eye out on the changelog as we update this with any changes for each version to make tracking easier. This is the first blog of a series of blogs on some additional features and capability within PoshC2. Stay tuned for more information.

  • Insert tasks when first picked up by the implant with start time
  • Update task when response returned with output and completed time
  • Log task ID in task sent/received
  • Add ability to set username and associate username to tasks issued
  • Print user in task information when the username is not empty
  • Improved error handling and logging
  • Rename CompletedTasks table to Tasks table
  • Method name refactoring around above changes
  • Pull out implant cores into
  • Rename 2nd stage cores into
  • Stage2-Core.ps1 (previously Implant-Core.ps1 ) is no longer flagged by AMSI
  • Use prepared statements in the DB
  • Refactoring work to start to break up dependency cycle
  • Rename DB to Database in to avoid name clashes
  • Pull some dependency-less functions into to aid dependency management
  • Fix download-file so that if the same file is downloaded multiple times it gets downloaded to name-1.ext name-2.ext etc
  • Adjust user/host printing to always be domain\username @ hostname in implants & logs
  • Fix CreateRawBase payload creation, used in gzip powershell stager and commands like get-system
  • Added ImplantID to Tasks table as a foreign key, so it’s logged in the Tasks report
  • Added for testing checklist/methodology
  • Fix Get-ScreenshotAllWindows to return correct file extension
  • Fix searchhelp for commands with caps
  • Implant timeout highlighting is now based on beacon time – yellow if it’s not checked in for 3x beacon time and red if not checked in for 10x beacon time
  • Setting and viewing beacon time is now consistent across config and implant types – always 50s/10m/1h format
  • Added validation for beacon time that it matches the correct format
  • Fix StartAnotherImplant command for python implant
  • Rename RandomURI column in html output to Context, and print it as domain\username @ hostname
  • Move service instructions to readme so that poshc2.service can just be copied to /lib/systemd/system
  • Removed and added instructions for same functionality to readme just using system commands

Python Server for PoshC2

We are delighted to announce the release of our PoshC2 Python Server, allowing cross-platform support.

Over the past six months we have been working on a Python server for PoshC2, which allows it to be run on almost any Unix or Windows based system that is capable of running Python. We have thoroughly tested the server on Kali, and Debian based Linux distributions without any issues. The server-side repository has been named ‘PoshC2_Python’ so as not to confuse it with the Windows PowerShell server version, ‘PoshC2’, which is still widely used in a client environment or enclave when needed. Update (12 Nov 2019): this is no longer the case. The repository names have been updated so that PoshC2 is where this version now lives.  PoshC2_old is for the legacy version. Here are a few of the main advantages we’ve identified of running PoshC2 via Python:

  • Removes the need for Windows
  • Team Collaboration
  • AutoComplete on the Implant Handler
  • GraphViz Visualisation
  • Lower CPU Utilization

Team Collaboration

The Python code enhances the flexibility of PoshC2. One of the main features is cross-platform support, therefore the server will run on both Windows and Unix based systems. The main feature of running the server on Linux is to enhance the team collaboration piece, which has always been limited via a Windows system. The Python server can be installed as a service; see the ‘Systemctl Service’ section below for more details on how to achieve this. It also allows multiple users to connect remotely and run their own implant handler to interact with each implant, while viewing the output from journalctl. Alternatively, the server session can be run from a screen terminal and multiple users can view the same screen session. The best results would be to run this as a systemctl service so it survives a reboot and will automatically re-start.

Almost all features have been ported over to Python from the server side, apart from the compiled executables that were ordinarily generated using .NET’s command line compiler csc.exe. However, instead of generating .NET binaries, we have introduced new template files that are written in C++ that can be modified and used as a template. See the ‘Templates’ section below for more information. This gives the user the ability to modify the template files, customize the binary, change the process the shellcode is injected into, the injection method, hardcode a domain name or any other additional functionality you can think of to enrich the dropper.

This is an example of the C2 server up and running. Similarly to PoshC2, the server window is usually viewed side by side to the Implant Handler to maintain consistency. The server will populate all the payloads only the fly, including the one liners, shellcode, HTA and macro files.

Systemctl Service

The PoshC2 server can now automatically be started as a service using systemctl within Linux. You can choose to either have this functionality enabled or not during the install; by default the service is not created, but it can be enabled by following the steps below. The advantages of having the C2 server installed as a service are endless, but most importantly if you are on a live engagement and the server for some reason fails or reboots, your C2 server will be resilient and bounce back once the system is back up and running.

To add the service, create the following file with the code in the block below:

#> vim /lib/systemd/system/poshc2.service

Once the file is in the correct location, the systemctl command will know how to enable the service, should you require this. To enable the service, run the enable command, followed by start, as shown below.

#> systemctl enable poshc2.service

#> systemctl start poshc2.service

You can also stop or restart the service if you need to change the config or re-run the server for any reason.

#> systemctl stop poshc2.service

#> systemctl restart poshc2.service

The best feature of running PoshC2’s python server as a service is the fact you can view the server output using the journalctl log. If you are running the server on an engagement with multiple users, it is very easy to share the output by running this command when you login via SSH.

#> journalctl -n 20000 -u poshc2.service -f –output cat


While this hasn’t changed significantly since the first iteration of PoshC2, it’s one of the most important elements of the tool.  It will fundamentally assist in the SOC detection and response phase of your engagements, i.e. when assessing the response of the Blue Team.  It is, of course, also a professional requirement to have full logs of offensive activity.

One addition to the reporting section is the introduction of the opsec command which is in the pre-implant help. This will provide a list of all files uploaded to the system, including a unique list of hosts touched for reporting purposes.

The output-to-html now uses a GraphViz implementation which is still a work in progress, but aims to visually represent the compromise in action, including all hosts that are connected to the C2 server and how they are communicating back to the infrastructure. For example, if you have a host daisy chaining via an implant this will be displayed on the GraphViz output below. These files are generated when the output-to-html function is run within the pre-implant handler window. All icons are customizable via the output file that is generated or you can switch these out editing the files folder in PoshC2.

It should be noted that proxy aware implants and daisy payloads are still all functioning within the Python server and work the same way as in PoshC2. For more information on Daisy Chaining please refer to the documentation or the following blog:


The HTML output has also been improved and now has some additional JavaScript that allows the commands to be searched and all output data truncated for easier viewing. The output can also be searched using the same method. If there are any additional reporting requirements you would like to see in here, please hit us up on Slack or Twitter.

Videos will be released shortly on how to get started with PoshC2, including customizing the file and editing the source files for better optimization.

Quick Install

To install PoshC2 on Ubuntu from a terminal, run the following:

curl -sSL | bash

To get started, follow the instructions on the readthedocs webpage, which walk you through how to customize your install and have a better chance of not being detected within your engagement.

If you have any issues regarding crypto, this could be due to a dependency installation failure. The best way to get around this has been to create a virtualenv in Python and then install the requirements file manually for that virtualenv. For more information, read how to create a virtualenv in Python online.

Wine SharpSocks

But what about SharpSocks? Never fear, SharpSocks works with Wine! If you need to find out how to get Wine installed for SharpSocks, there is a file called Install_SOCKS that has step-by-step instructions on how to achieve this. For those who don’t know what SharpSocks is, the following blog post discusses our release of a SOCKS server for PoshC2in detail.


As previously mentioned, PoshC2 now has template files that can be optimized prior to starting the service. An example of why you might want to do this is environment detections such as domain name or user. The template files are created using C++; by default there is an executable that creates a file which migrates automatically, and one that stays in the same process.


To reduce the likelihood of the binary files being detected, you could also sign the executables that are generated from PoshC2 by using a code signing certificate. This will add some legitimacy to the binary when calling out to the internet.

#> signtool.exe sign /f code_signing_cert.pfx /p password Posh64.exe


As part of the initial payload, the PowerShell script will attempt to execute. If the C2 Proxy or implant cannot reach the server, the process will wait for 300 seconds (5 minutes) and retry. If required, it will then wait for 600 seconds (10 minutes) and try one final time. This has been implemented as a backup in case your C2 infrastructure is locked down too securely and for some reason doesn’t accept the implant first time. This could be due to a number of environmental detections, including the external IP address. The implant could have passed the domain check but come from a non-whitelisted IP address. This will give you the opportunity to evaluate the IP address and add to the whitelist if this should have been part of the organisations external IP address range given to you. This can all be modified if the timings need to be increased for any reason; all of this code is in the file.


Autoruns have always been a great feature for us. There is nothing worse than having to tell the C2 tool to load the following PowerShell module before running the command when this can all be coded into the tool. PoshC2 has implemented a lot of these by default, but if you want to customize them you can amend the autoloads by editing the file and adding extra lines.


The philosophy of PoshC2 has always been to use a selection of amazing PowerShell cmdlets that have been written by others in the industry. The PoshC2 folder has a Modules folder where all the scripts are loaded from. You can simply add any PowerShell script that conforms to the PowerShell standard into this folder and load using the Implant Handler:

loadmodule Invoke-Mimikatz.ps1

Python Dropper

As you will notice, we have made a small start on the Python dropper to allow for Unix support. This has not been exhaustively tested, however, it is a start at making it possible to support execution on the likes of a macOS based system or similar. There is currently a requirement to have pycrypto on the box that is executing the payload but we are working on solutions to implement the crypto piece without requiring this dependency. Something along the lines of in memory module imports, or similar. If anyone would like to contribute to this side of the PoshC2, please don’t hesitate to get in touch.

A Python implant has limited features right now. Currently you can set the beacon timer and execute commands on the host.

PoshC2 Execution Tips

A pro tip for executing PoshC2 on a client device that is highly monitored and has PoshC2 v5 with script block logging, module logging and transcript Logging is combining the work Dave Hardy did with the PoSHBypass (including the authors of the bypass techniques) and the transcript logging bypass we put together using this gist code snippet:

The script block logging, Module Logging & AMSI bypass was put together here by Dave Hardy:

The Transcript Evasion technique was from here:

From an OpSec perspective, similarly to most C2 frameworks and adversaries, PoshC2 has some default IoCs that it is highly recommended you optimize to avoid detection. These items include:

  • Comms URLS
  • UserAgent
  • Use Domain Fronting
  • Change Default Migration Process (netsh.exe)
  • Change Default Persistence Methods
  • Template Files

When you start PoshC2 ,you have to optimize the file which will provide you a list of default URLs that can be used. These are publicly available and will most likely get signatured in time. To ensure you are providing the best chance of remaining undetected, you should optimize these URLs. This also goes for the UserAgent string. Prior to sending in any payloads, it is necessary to do reconnaissance against a target. This will often include techniques like web bugs or similar. This will give you a change to identify the default UserAgent for the target estate. This could range from IE11, Chrome, Firefox or even Edge. You should re-configure the UserAgent configuration to be in line with the corporate estate to merge into normal business traffic on the proxy.

As most of us know, domain fronting is the best form of hiding censorship when performing Red Teaming. Unless the organization is performing SSL inspection, there is no way this type of communication can be detected. If the organization is using SSL inspection, its best to use a site that falls into one of these two categories to have the best chance of going under the radar:

  • Financial Services
  • Health Care

Frameworks such as Cobalt Strike and Metasploit also have common indicators of comprise (IoC), such as ‘notepad.exe’ for Metasploit and ‘rundll32.exe’ for Cobalt Strike. PoshC2 has a similar default process that is used for migration; this is netsh.exe. When performing the default migration within PoshC2, e.g. running the ‘migrate’ command, it will always start the process netsh.exe unless directed by the user on the command line. It is highly recommended that you customize this option; the new PoshC2 ‘Inject-Shellcode’ or ‘migrate’ function also has the ability to spoof the parentpid. Note, this works on Windows 7 but has had some failures on Windows 10. Also, the default method for process migration was using the win32 API call ‘CreateRemoteThread’. PoshC2 now has the ability to use ‘RtlCreateUserThread’ which is not quite as widely used across C2 frameworks. This was largely due to the help from @uint_ptr, who is our in house Windows wizard!

You can use the migrate command with all of the above in mind as follows:

  • migrate -procpath c:\windows\system32\searchprotocolhost.exe -suspended -RtlCreateUserThread
  • migrate -procpath c:\windows\system32\svchost.exe -suspended -RtlCreateUserThread -ParentID 4502

From a logging and monitoring perspective, it is always good practice to migrate to a process that is expected to go out to the internet, e.g. Internet Explorer, Outlook, Lync or similar. If the client has an endpoint product that tracks process migration and history, going from netsh.exe out to the internet should be suspicious. Keep this is mind when selecting the process to migrate into.

Here is an example of parent process spoofing in Windows 7. The following command was ran to migrate the process searchprotocolhost.exe and set the parentid of explorer (pid: 432). Note, this was all done using a standard user account.

  • migrate -procpath c:\windows\system32\ searchprotocolhost.exe -suspended -RtlCreateUserThread -ParentID 432

Persistence is also another function that should be completely optimized. PoshC2 has some default persistence methods, however, these are likely to be highly signatured and should be changed accordingly. There are many methods of persistence you can use, depending on your privileges. COM Hijacking is highly recommended or WMI if elevated. DLL Hijacking is also fairly difficult to detect; if you want to create a custom DLL that can be used for DLL hijacking, here is a simple code snippet that can be used to launch another process when the DLL is attached, which is perfect for DLL hijacking.

AMSI Checker

A new feature we’ve added is an Anti-Malware Scan Interface (AMSI) checker. We have baked this into the core-implant module. This does a quick process check using PowerShell and determines if the amsi.dll is loaded into the core implant. If this module is loaded, it will notify you through the C2Server response and provide a way of unhooking this DLL.

The way we unhook this module is using the work Adam Chester (@_xpn_) put together on exploring PowerShell AMSI and logging evasion. The way this function works is by using C# to pinvoke various Win32 API calls to identify where a certain DLL is loaded and its export functions reside in memory. As we own the memory for our process, we can simply overwrite the code in memory, returning the same response as if the malware check was benign.

For visibility, it is possible to identify if PoshC2 is running in another process by using Process Explorer which comes as part of the sysinternals suite ( This allows you to see if any .NET assemblies have been loaded into the running process. If the process is in fact PoshC2, there is a known Indicator of Compromise when running the CLR v4.0.30319 because you will see a DLL loaded called ‘posh’. This does not appear in .NET version two, as shown in the examples below.

Python 3

In the not too distant future, this will be ported over to Python v3. Currently it’s only designed to work in version 2.7.


In conclusion, both the traditional Windows PoshC2 and the Python Server PoshC2 repositories will be actively maintained by various contributors.  We encourage you to use it, provide feedback and generally contribute to the project.  You can always grab the latest version from GitHub.

github GitHub: