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
:
run-exe:
loadmodule Seatbelt.exe run-exe Seatbelt.Program Seatbelt all run-exe Seatbelt.Program Seatbelt SysmonConfig
run-dll:
loadmodule Seatbelt.exe run-dll Seatbelt.Program Seatbelt UserChecks
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:
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:
The generated report then looks like this:
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:
All tasks issued from that ImplantHandler
instance will be logged as that user, both in the C2Server log and 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 ImplantHandler.py --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.
Validation has also been added for these, so attempting to set an invalid beacon time will print a suitable error message and do nothing.
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.
C2Viewer
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 C2Server.py | 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 README.md
.
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.
Conclusion
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: https://github.com/nettitude/PoshC2.
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 Implant-Core.py/.cs/.ps1
- Rename 2nd stage cores into Stage2-Core.py/.ps1
- 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 Config.py to avoid name clashes
- Pull some dependency-less functions into Utils.py 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 Testing.md 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 C2Viewer.py and added instructions for same functionality to readme just using system commands