Project: PoshC2 Author: Nettitude (Ben Turner @benpturner, Rob Bone @m0rv4i, and contributors) GitHub: https://github.com/nettitude/PoshC2 SOCKS Component: https://github.com/nettitude/SharpSocks Description: Proxy-aware, Python3-based C2 framework for red teaming, post-exploitation and lateral movement License: BSD 3-Clause Docs: https://poshc2.readthedocs.io/
📋 Wiki Status: Comprehensive reference built against the official PoshC2 ReadTheDocs documentation (poshc2.readthedocs.io) and the nettitude/PoshC2 GitHub README. Structured to mirror the Adaptix / Merlin / Sliver wikis for consistent cross-referencing. Current as of PoshC2 v9.0 ("Revival").
Overview
PoshC2 is a proxy-aware command-and-control framework built to aid penetration testers and red teamers with post-exploitation and lateral movement. Where Adaptix and AdaptixC2-style frameworks lean on compiled Go/C++ agents and a GUI client, PoshC2 takes a Python3, terminal-driven, client/server approach with heavy PowerShell/C# heritage.
Key capabilities:
- Primarily Python3 — server and Implant-Handler are Python3; follows a modular format so users can add their own modules and tools
- Four Implant Types — PowerShell, Sharp (C#), Python3, and PBind (named-pipe variants of PS/Sharp)
- No PowerShell.exe dependency — the PowerShell implant talks to
System.Management.Automation.dlldirectly rather than spawningpowershell.exe - Highly configurable payloads — beacon time, jitter, kill dates, user agents and more, set per-project
- Large out-of-the-box payload set — PowerShell v2/v4, C++ and C# source, EXEs, DLLs, raw shellcode, Python3 payloads — regularly updated
- Auto-generated Apache rewrite rules — every project generates a
rewrite-rules.txtfor a C2 proxy, protecting the real C2 server - Modular, extensible format — add or edit C#, PowerShell or Python3 modules that run in-memory in the Implants
- Notifications — new Implant check-ins can trigger Pushover or Slack alerts
- Fully encrypted comms — traffic is encrypted at the application layer even over plain HTTP
- Client/Server, multi-operator — several Implant-Handler processes can connect to one C2 Server
- Extensive logging — every task/response is timestamped and stored in a database with user, host, implant ID etc.
- Free, open-source SOCKS proxy — via SharpSocks integration
- Daisy-chaining — implants can relay C2 traffic for hosts without direct internet access
- Domain Fronting support — built into the payload comms configuration
PoshC2 shines for engagements needing a lightweight, terminal-first, highly scriptable C2 with strong Windows AD tradecraft roots (it grew out of PowerShell red-team tooling) — a good contrast to GUI-heavy frameworks like Adaptix or Sliver's Go-based single-binary model.
Architecture
PoshC2 follows a database-mediated server / handler split, which is architecturally distinct from Merlin's gRPC model, Sliver's mTLS multiplayer model, and Adaptix's plugin-extender model:
┌────────────────────────────┐ ┌────────────────────────────┐
│ Implant-Handler (posh) │ │ Implant-Handler (posh) │
│ Operator 1 terminal (CLI) │ │ Operator 2 terminal (CLI) │
└──────────────┬─────────────┘ └──────────────┬─────────────┘
│ writes Tasks │ writes Tasks
▼ ▼
┌───────────────────────────────────────────────────┐
│ Project Database │
│ (SQLite by default; stores Tasks, Implants, │
│ Creds, logs, timestamps, issuing user, etc.) │
└───────────────────────┬───────────────────────────┘
│ C2 Server polls DB for
│ new tasks, updates on responses
▼
┌─────────────────────────────┐
│ C2 Server │
│ (posh-server, Python3) │
│ - Listens for Implants │
│ - Issues tasks on check-in │
│ - Logs all traffic │
└───────────┬─────────────────┘
│ HTTP(S) / SMB named pipe
│ (optionally via Apache C2 proxy)
▼
┌───────────────────────────────────────┐
│ Implants │
│ PowerShell │ Sharp (C#) │ Python3 │
│ PBind (named-pipe PS/Sharp variants) │
└───────────────────────────────────────┘
Components:
- C2 Server (
posh-server) — listens for Implant connections, issues queued tasks on check-in, and logs everything. Started once per project. - Implant-Handler (
posh) — the operator's interactive console used to issue commands and interact with Implants. Tasks are written to the database, not sent directly to the C2 Server process — this decoupling is what lets multiple operators run separate Implant-Handler processes safely against one C2 Server. - Project Database — the mediator between Handler and Server. Every task issued from an Implant-Handler is stored with a timestamp, the issuing user, and Implant details; the C2 Server picks tasks up from here and dispatches them when the Implant checks in, ensuring nothing is ever lost even if the operator's terminal disconnects.
- Implants — the four payload types (PowerShell, Sharp, Python3, PBind); see Implant Types.
- C2 Proxy (optional but recommended) — a VPS running Apache with
mod_rewritesits in front of the real C2 Server, silently redirecting only recognised C2 URLs back to the server while serving a benign site for everything else. This hides the real C2 infrastructure and lets operators swap proxies without touching the backend.
Target ──▶ Apache C2 Proxy (VPS, mod_rewrite) ──▶ PoshC2 Server ──▶ Project Database ◀── Implant-Handler(s)
│
└── non-matching traffic served a benign decoy site
Domain Fronting
PoshC2 has native support for Domain Fronting — routing C2 traffic through a legitimate, trusted CDN domain so that the domain reputation checked by the target/network is that of the fronted site, not the real C2 endpoint. It cannot be feasibly detected unless SSL Inspection is in place, since it works by manipulating the HTTP Host header independently of the TLS SNI.
Configuration:
PayloadCommsHost = "https://frontable.com"
DomainFrontHeader = "endpoint.cdn.com"
PayloadCommsHost— the domain-frontable site initially connected to.DomainFrontHeader— the CDN endpoint's actual value placed in the Host header (domain only).
Requirement: the host needs .NET v4.0.30319 available to the Implant process; if it's missing, the implant silently falls back to a direct connection to the CDN endpoint.
Installation
PoshC2 is written in Python3 and developed/tested primarily on Kali Linux (Debian-based). Docker images are also provided for running on other host OSes.
Recommendation: if using host AV/EDR, add an exclusion for the PoshC2 install directory and the project directory.
Direct Install (Kali/Debian)
Elevated privileges are required — the install script performs apt updates/installs.
curl -sSL https://raw.githubusercontent.com/nettitude/PoshC2/master/Install.sh | sudo bash
Or clone and run manually:
git clone https://github.com/nettitude/PoshC2.git
cd PoshC2
sudo ./Install.sh
Custom install directory (-p, default is /opt/PoshC2):
curl -sSL https://raw.githubusercontent.com/nettitude/PoshC2/master/Install.sh | sudo bash -s -- -p /root/PoshC2
Script usage summary:
*** PoshC2 Install script ***
Usage:
./Install.sh -b <git branch> -p <Directory to clone PoshC2 to>
Defaults are master branch to /opt/PoshC2
Docker Install
Docker install does not clone the full PoshC2 repo — it pulls the pre-built PoshC2 images from Docker Hub, performing only a minimal local setup of dependencies/scripts.
First, install Docker on the host, then share the PoshC2 projects directory with Docker if your OS requires it. Default project directory: /var/poshc2 (*nix), /private/var/poshc2 (macOS).
Kali/Linux hosts:
curl -sSL https://raw.githubusercontent.com/nettitude/PoshC2/master/Install-for-Docker.sh | sudo bash
Script usage summary:
*** PoshC2 Install script for Docker ***
Usage:
./Install-for-Docker.sh -b <git branch>
Default is the master branch
Using the Docker image tag at runtime:
posh-server -t latest
posh -u crashoverride -t latest
As the C2 Server container isn't interactive under Docker, use posh-stop-server to stop it.
Windows Install
Import the PowerShell module and use the -PoshC2Dir / -LocalPoshC2ProjectDir parameters for every command:
Import-Module -DisableNameChecking C:\PoshC2\resources\scripts\PoshC2.psm1
posh-project -PoshC2Dir "C:\PoshC2" -LocalPoshC2ProjectDir "C:\PoshC2_Project" -Arg1 "-n" -Arg2 "newproject"
posh-config -PoshC2Dir "C:\PoshC2" -LocalPoshC2ProjectDir "C:\PoshC2_Project"
posh-server -PoshC2Dir "C:\PoshC2" -LocalPoshC2ProjectDir "C:\PoshC2_Project"
posh -PoshC2Dir "C:\PoshC2" -LocalPoshC2ProjectDir "C:\PoshC2_Project" username
Cutting Edge / Dev Branch
master is kept stable; new features land first on dev or feature branches off dev. dev is tested before merge and is usually fairly stable, but trades stability for freshness — use at your own discretion for live engagements.
curl -sSL https://raw.githubusercontent.com/nettitude/PoshC2/dev/Install.sh | sudo bash -s -- -b dev
Note the branch name appears both in the raw URL path and as the -b argument.
Using an older release: list tags and hard-reset a local clone to a specific version (this overwrites local changes, including config edits):
git tag --list
git reset --hard v4.8
Apache Rewrite Rules (C2 Proxy)
Using a C2 Proxy in front of the real server is the recommended OPSEC pattern: a VPS running Apache with mod_rewrite silently redirects only recognised C2 URLs back to the PoshC2 Server, while serving a normal, benign website for everything else (bots, scanners, or the blue team poking around). This protects the real C2 infrastructure and lets operators pivot to a new proxy without changing the backend server.
Nettitude has a walkthrough covering a full 5-VPS OpenVPN setup: https://labs.nettitude.com/blog/making-poshc2-more-accessible-with-a-5-vps/
Setup
Install Apache and enable the required modules:
apt-get install apache2
apt install libapache2-mod-security2
a2enmod security2
a2enmod ssl
a2enmod rewrite
a2enmod proxy
a2enmod proxy_http
a2enmod headers
On first launch for a new project, the C2 Server auto-generates a rewrite-rules.txt file containing the exact rules needed for that project. These are dropped into the Apache vhost config:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
RewriteEngine On
SSLProxyEngine On
SSLProxyCheckPeerCN Off
SSLProxyVerify none
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
Proxy100Continue Off
# Optional: 404 cookie info for OPSEC (see blog link below)
SecAuditEngine RelevantOnly
SecAuditLog /var/log/apache2/sec.log
SecAuditLogParts BSecAudit
LogRelevantStatus ^(404)
Define PoshC2 10.0.0.1
Define SharpSocks 10.0.0.1
RewriteRule ^/wpaas/(.*) https://${PoshC2}/wpaas/$1 [NC,L,P]
# ... one RewriteRule per generated C2/SOCKS path in rewrite-rules.txt ...
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>
</IfModule>
Any URL matching a rule is proxied to the C2 Server; anything else is served normally from DocumentRoot (/var/www/html above) — this is what gives PoshC2 its "benign front door."
Apache Whitelisting
On an engagement, lock the C2 proxy down so only the client's known IP ranges can reach the real infrastructure. Build a whitelist file (Apache-specific format, note the trailing dash):
10.0.0.1 –
10.0.0.2 –
Convert a CIDR range into that format:
#!/usr/bin/python
from netaddr import IPNetwork
import sys
for ip in IPNetwork("10.10.0.0/21"):
print('%s -' % ip)
Apply the whitelist with RewriteMap and a RewriteCond, returning 404 to anyone not listed:
RewriteMap IP txt:/etc/apache2/whitelist
Define PoshC2 10.0.0.1
RewriteCond ${IP:%{REMOTE_ADDR}|NOT-FOUND} !NOT-FOUND
RewriteRule ^/images/static/content/(.*) https://${PoshC2}/images/static/content/$1 [NC,P,L]
This condition/rule pair must be repeated for each rewrite path you want protected.
Domain Fronting + Whitelisting
When Domain Fronting is in play, the REMOTE_ADDR Apache sees is the CDN's IP, not the real client. The true originating IP instead lands in the X-Forwarded-For header — whitelist on that too:
RewriteMap ips txt:/etc/apache2/whitelist.txt
RewriteCond ${ips:%{REMOTE_ADDR}|NOT-FOUND} !NOT-FOUND [OR]
RewriteCond ${ips:%{HTTP:X-Forwarded-For}|NOT-FOUND} !NOT-FOUND
RewriteRule ^/images/static/content/(.*) https://${PoshC2}/images/static/content/$1 [NC,P,L]
Bonus OPSEC capability: PoshC2 ships a CookieDecrypter.py script. Combined with extra 404 logging on the proxy (SecAuditEngine config above), if a connection arrives from outside your whitelisted range and gets dropped, you can still decrypt the dropper's cookie payload to reveal environment variables about where it ran — giving situational awareness even on rejected connections. Background: https://labs.nettitude.com/blog/operational-security-with-poshc2-framework/
Project Management & Configuration
Creating a Project
Project data lives under /var/poshc2/. Projects are created, listed, and switched via posh-project:
[*] Usage: posh-project -n <new-project-name>
[*] Usage: posh-project -s <project-to-switch-to>
[*] Usage: posh-project -l (lists projects)
[*] Usage: posh-project -d <project-to-delete>
[*] Usage: posh-project -c (shows current project)
posh-project -n my-project-name
All configuration and generated data for that engagement lives at /var/poshc2/my-project-name.
Edit the active project's configuration:
posh-config
Opens the config file in $EDITOR (defaults to vim); pass --nano if you prefer nano.
Key Configuration Options
| Option | Description |
|---|---|
| PayloadCommsHost | URL list payloads beacon out to for all C2 comms |
| DomainFrontHeader | HTTP Host header value when Domain Fronting is enabled |
| KillDate | Date after which all persistence/implants stop beaconing |
| Jitter | Beacon-interval jitter as a decimal (e.g. 0.2 = ±20%, so a 5s beacon becomes 4–6s) |
| DefaultSleep | Default beacon interval for new implants |
| EnableNotifications | Notify on new implant check-in (requires ClockworkSMS or Pushover configured) |
| NotificationsProjectName | Prefix used in notifications to identify this server |
| DefaultMigrationProcess | Process spawned/migrated into for payloads that migrate on execution |
| UserAgent | HTTP User-Agent string used for HTTP(S) comms |
By default the implant uses HTTPS; switching PayloadCommsHost to http:// runs it over plain HTTP — note that application-layer encryption is applied regardless, so even HTTP traffic content stays encrypted. HTTPS additionally hides HTTP headers (enabling Host-header-based Domain Fronting) unless the target is doing SSL Inspection.
Starting & Running PoshC2
C2 Server
posh-server
On first run per project this builds the full project folder structure and database, generates all out-of-the-box payloads for the current config, and prints quickstart commands (e.g. a ready-to-use PowerShell one-liner) to the server log.
Once the log shows the webserver running, the server is ready to receive implants.
Second operator wanting to tail the log should not re-run posh-server (it will try to rebind the port) — instead:
posh-log
Implant Handler
posh
Prompts for a username (used purely for logging/attribution). Or set it inline:
posh -u crashoverride
Multiple Implant-Handler processes — from different operators or the same operator in different terminals — can run concurrently against one C2 Server, coordinated through the shared project database.
Running as a Service
For persistence across reboots/session exits, install the C2 Server as a system service:
posh-service
Installs, starts, and immediately tails the log — but unlike the foreground mode, Ctrl-C here only stops your view of the log; the service keeps running. Re-running posh-service restarts it. Resume viewing the log at any time:
posh-log
Stop the service:
posh-stop-service
Docker Usage
posh-server -t latest
posh -u crashoverride -t latest
posh-stop-server # stop the non-interactive server container
Project Folder
Every project gets a unique folder holding all engagement-specific data and downloads, including unique encryption keys. Shellcode generated for one project will not connect to another project's server — a built-in safety measure that prevents payload/server mismatches.
Communications Failover & Rotation
Failover: configure an array of PayloadCommsHost values with matching DomainFrontHeader values (equal-length arrays; empty header values are allowed for non-fronted URLs):
PayloadCommsHost: "https://domainfrontable.com,https://direct.com"
DomainFrontHeader: "dfheader.com,direct.com"
The implant tries the first URL/header pair, falls back to the next on failure, and stops at the first successful connection to load the second stage. Un-connectable URLs get retried several times with increasing timeouts, giving operators time to fix whitelisting issues.
Rotation: once inside the environment, implants can randomly rotate across a defined pool of C2 URLs. This blends traffic, lets operators route around a newly-blocked URL, and helps avoid telemetry-based detection. If one URL in rotation gets blocked, the rotation list can be updated and will apply on the next successful callback.
Check which URLs are reachable before enabling rotation:
invoke-urlcheck -urls https://ur1.com,https://url2.com -domainfront header1.com,header2.com -uri /en-gb/surface/accessories/
Sample output:
27/03/2020 09:30:55: The URL: https://url1.com successfully connected
27/03/2020 09:30:56: The URL: https://url2.com successfully connected
Enable/inspect rotation:
enable-rotation # prompts for the URL/header values to rotate through
get-rotation # shows the URLs currently being cycled through
Getting an Implant
Payloads are generated into the project's payloads directory (type, architecture, and — for PowerShell — version, sensibly named), including raw and Base64-encoded shellcode for custom injection or payload work.
When a payload runs successfully, the implant checks in — a message appears in the C2 Server log along with useful detail: Implant ID, type, and the environment it's running in.
Refresh the Implant-Handler's view of new implants by pressing Enter at the top-level prompt.
Implant Type shorthand (shown left of the prompt):
| Code | Meaning |
|---|---|
| PS | PowerShell Implant |
| C# | Sharp Implant (no System.Management.Automation.dll) |
| PY | Python3 Implant |
| PB | PBind Implant |
Sub-type suffixes:
| Suffix | Meaning |
|---|---|
| ;D | Daisy Implant — relaying C2 comms through another implant |
| ;P | Proxy Implant — hard-coded proxy credentials |
If the checked-in username has a trailing *, the implant is running in an elevated context.
Implant Types
PoshC2 ships four implant flavors:
- PowerShell — does not shell out to
powershell.exe; interacts withSystem.Management.Automation.dlldirectly. Lets the operator load any PowerShell module and run any cmdlet from the implant prompt. Both PowerShell v2 and v4 payload variants exist. - Sharp — a C#.NET implant with zero PowerShell dependency. Ideal for running C# tooling in memory — e.g. BloodHound's collector or the GhostPack suite from SpecterOps.
- Python — a Python3 implant requiring Python3 on the target; runs arbitrary Python3 code and loads Python3 scripts.
- PBind — a subset of PowerShell and Sharp implants that communicate over named pipes instead of the normal network transport (useful for peer-linked / air-gapped-adjacent scenarios).
Using an Implant
The Implant-Handler window does not auto-refresh check-in times. Press Enter on an empty prompt to refresh the implant list and last-seen timestamps.
Select an implant by ID:
1
PS 1>
Select multiple (comma-separated):
1,2,3,4
1,2,3>
Select all:
ALL
ALL>
Commands issued at the implant prompt are dispatched as Tasks, each with its own identifier — visible in the C2 Server log alongside the issuing user. The implant's response echoes the same identifier, so input/output/user are always correlated. Autoloaded tasks (e.g. modules loaded automatically on first use) are attributed to the autoruns user.
Truncated PowerShell output: pipe to Format-List / its alias fl, exactly as you would in a native PowerShell session, to get the full result.
Loading & Running Modules
PowerShell Modules
loadmodule MyPowerShellScript.ps1
Invoke-MyCmdlet
Python Modules
loadmodule MyPythonScript.py
my_python_function("arg")
C# (Sharp) Modules
C# modules are .exe/.dll files invoked via .NET reflection — they never touch disk. PoshC2 needs three pieces of information to run one:
- Namespace of the class containing
Main - Class name of the class containing
Main - Assembly name
loadmodule MySharpStuff.exe
run-exe Namespace.Class Assembly <args>
Given this source:
using System;
namespace ConsoleApp1
{
class MySuperProgram
{
static void Main(string[] args)
{
if (args.Length >= 1)
{
Console.WriteLine(args[0]);
}
}
}
}
Namespace = ConsoleApp1, Class = MySuperProgram. The Assembly Name is found in Visual Studio (right-click the project, not the solution → Properties) or in the .csproj file — here, PwnStuff. To run and print "PoshC2Rocks":
run-exe ConsoleApp1.MySuperProgram PwnStuff PoshC2Rocks
DLLs use run-dll, identical to run-exe but with one extra argument for the entry point (class/namespace must match that entry point):
run-dll ConsoleApp1.MySuperProgram PwnStuff MyEntryMethod PoshC2Rocks
Background execution: run-exe-background behaves exactly like run-exe but runs the command in the background on the implant rather than the foreground.
SOCKS Proxying (SharpSocks)
The SOCKS proxy is one of the most important tools for a red teamer — it tunnels network traffic through a compromised host so traffic appears to originate from inside the target environment, enabling access to otherwise-unreachable subnets, RDP to internal hosts, or browsing the internal intranet.
PoshC2 integrates Nettitude's own SharpSocks (by Rob Maslen). Background: https://labs.nettitude.com/blog/poshc2-v3-with-socks-proxy-sharpsocks/
Most modern browsers and cURL support SOCKS natively. For tools like rdesktop or nmap, use proxychains on Linux; on Windows, use something like ProxyCap.
Start SharpSocks from a PowerShell implant:
PS 1> sharpsocks
This prints the command to run on the C2 server to start the SharpSocks server, e.g.:
/opt/PoshC2/SharpSocks/SharpSocksServerCore -c=CteIopoCcIfFegXFXsdzyscpt -k=O4QsWRPiEXYbHJobUSs9GfPloh9O8U7VlyV0dxYnWZ8= --verbose -l=http://127.0.0.1:49031
Are you ready to start the SharpSocks in the implant? (Y/n)
Once the SharpSocks server is running, press Enter — the implant connects and the SOCKS proxy comes up, confirmed in the SharpSocks server output.
Help, Auto-completion & Auto-suggestions
help at any implant prompt gives a context-sensitive command list — e.g. only PowerShell-relevant commands are shown at a PowerShell implant.
Search help text:
PS 1> searchhelp mimikatz
invoke-mimikatz -command '"sekurlsa::logonpasswords"'
invoke-mimikatz -command '"lsadump::sam"'
invoke-mimikatz -command '"lsadump::lsa"'
invoke-mimikatz -command '"lsadump::cache"'
invoke-mimikatz -command '"lsadump::secrets"'
invoke-mimikatz -command '"ts::multirdp"'
invoke-mimikatz -command '"privilege::debug"'
invoke-mimikatz -command '"crypto::capi"'
invoke-mimikatz -command '"crypto::certificates /export"'
invoke-mimikatz -command '"sekurlsa::pth /user:<user> /domain:<dom> /ntlm:<hash> /run:c:\temp\run.bat"'
invoke-mimikatz -command '"lsadump::dcsync /domain:domain.local /user:administrator"'
Auto-completion: fuzzy-matched against the first word only — isc matches invoke-shellcode, wmi surfaces every command containing "wmi".
Auto-suggestions: each prompt keeps its own contextual command history (e.g. C# implant history is separate from PowerShell), and shows fish-shell-style gray suggestion text from that history — accept with → or Ctrl-E.
Module-level help:
LoadModule Invoke-Mimikatz.ps1
Get-Help Invoke-Mimikatz
For C#/Python modules with built-in help:
loadmodule MyHelpfulModule.exe
run-exe MyHelpfulModuleNamespace.MyHelpfulModuleClassName MyHelpfulModuleAssemblyName --help
PoshC2's own source is the deepest reference — fully documented Python3 at https://github.com/nettitude/PoshC2.git; since the framework evolves quickly, reading the source is the recommended way to fully understand and tune behavior for your environment.
Extending PoshC2
Any serious red-teamer will want to customise their tooling. Beyond config-file tweaks (UserAgent, HTTP responses, etc.), PoshC2's internals are meant to be edited directly. Useful changes are welcomed back as pull requests.
Adding Modules
Drop new modules into resources/modules in the install directory — see Loading & Running Modules above for the load/run syntax per implant type.
Adding Aliases
Defined in poshc2/client/Alias.py, per implant type. Two kinds of list:
*_alias— exact-match replacement of a short command with a full one.*_replace— prefix-match replacement, so the alias can still take arguments.
# C# Implant
cs_alias = [
["s", "get-screenshot"],
]
# Parts of commands to replace if command starts with the key
cs_replace = [
["safetydump", "run-exe SafetyDump.Program SafetyDump"],
["sharpup", "run-exe SharpUp.Program SharpUp"],
["seatbelt", "run-exe Seatbelt.Program Seatbelt"],
["rubeus", "run-exe Rubeus.Program Rubeus"],
["sharpview", "run-exe SharpView.Program SharpView"],
["sharphound", "run-exe Sharphound2.Sharphound Sharphound"],
["sharpweb", "run-exe SharpWeb.Program SharbWeb"],
["watson", "run-exe Watson.Program Watson"],
["pwnstuff", "run-exe ConsoleApp1.MySuperProgram PwnStuff"]
]
Typing s → get-screenshot (exact match only; sort is untouched). Typing rubeus kerberoast → run-exe Rubeus.Program Rubeus kerberoast (prefix match, arguments preserved); echo "rubeus" is left alone since it doesn't start with the alias.
So pwnstuff PoshC2Rocks becomes shorthand for run-exe ConsoleApp1.MySuperProgram PwnStuff PoshC2Rocks.
Adding Autoloads
Autoloads let a module load automatically the first time its command is run — convenient, but risky if an operator fat-fingers a command and loads something into memory unintentionally.
Edit poshc2/server/Autoloads.py:
def run_autoloads_sharp(command, randomuri, user):
command = command.lower().strip()
if command.startswith("run-exe seatbelt"): check_module_loaded("Seatbelt.exe", randomuri, user)
elif command.startswith("run-exe sharpup"): check_module_loaded("SharpUp.exe", randomuri, user)
elif command.startswith("run-exe safetydump"): check_module_loaded("SafetyDump.exe", randomuri, user)
elif command.startswith("run-exe rubeus"): check_module_loaded("Rubeus.exe", randomuri, user)
elif command.startswith("run-exe sharpview"): check_module_loaded("SharpView.exe", randomuri, user)
elif command.startswith("run-exe watson"): check_module_loaded("Watson.exe", randomuri, user)
elif command.startswith("run-exe sharphound"): check_module_loaded("SharpHound.exe", randomuri, user)
elif command.startswith("run-exe internalmonologue"): check_module_loaded("InternalMonologue.exe", randomuri, user)
elif command.startswith("run-exe sharpsocks"): check_module_loaded("SharpSocks.exe", randomuri, user)
elif command.startswith("run-exe sharpweb"): check_module_loaded("SharpWeb.exe", randomuri, user)
elif command.startswith("run-exe wmiexec.program"): check_module_loaded("WExec.exe", randomuri, user)
elif command.startswith("run-exe smbexec.program"): check_module_loaded("SExec.exe", randomuri, user)
elif command.startswith("run-exe invoke_dcom.program"): check_module_loaded("DCOM.exe", randomuri, user)
elif command.startswith("sharpsocks"): check_module_loaded("SharpSocks.exe", randomuri, user)
elif command.startswith("safetykatz"): check_module_loaded("SafetyKatz.exe", randomuri, user)
elif command.startswith("pwnstuff"): check_module_loaded("PwnStuff.exe", randomuri, user)
Adding Auto-completion Entries
Add commands (including your own aliases) to the relevant prompt-context list in poshc2/client/Help.py for auto-completion and the help command, then restart the Implant-Handler:
SHARPCOMMANDS = ["get-userinfo", "stop-keystrokes", "get-keystrokes", "delete", "move", "label-implant", "upload-file", "quit",
"download-file", "get-content", "ls-recurse", "turtle", "cred-popper", "resolveip", "resolvednsname", "testadcredential",
"testlocalcredential", "get-screenshot", "modulesloaded", "get-serviceperms", "unhide-implant", "arpscan", "ls", "pwd", "dir",
"inject-shellcode", "start-process", "run-exe", "run-dll", "hide-implant", "help", "searchhelp", "listmodules", "loadmodule",
"loadmoduleforce", "back", "ps", "beacon", "setbeacon", "kill-implant", "get-screenshotmulti", "safetydump", "seatbelt", "sharpup",
"sharphound", "rubeus", "sharpview", "watson", "get-hash", "migrate", "sharpsocks", "safetykatz", "get-computerinfo",
"get-dodgyprocesses", "sharpweb", "pwnstuff"]
Adding Payloads
Payloads generate on first C2 Server start for a project, or on-demand via createnewpayload / createdaisypayload from the Implant-Handler.
- Tweak existing templates: resources/payload-templates/ and/or poshc2/server/Payloads.py.
- Add an entirely new payload type: drop a new generator file into poshc2/server/payloads/ — any Python file there gets auto-imported, and its
create_payloadsfunction is called with the Payload details/name. See poshc2/server/payloads/Macro-Payloads.py as a worked example.
OPSEC Prompts
Anything listed in poshc2/client/Opsec.py requires the operator to accept an extra confirmation prompt before it runs — a lightweight safety rail against firing off a risky command by accident.
API Usage
PoshC2 ships a REST API service, started via the posh-api-server script (or via posh-service, which starts both the C2 Server and the API together).
posh-api-server
Starts the API locally on TCP port 5000 with default credentials:
Username: poshc2
Password: change_on_install
Change these immediately for any real engagement.
To reach the API remotely, tunnel the port over SSH rather than exposing it directly:
ssh poshc2server -L 5000:127.0.0.1:5000
API Configuration
Adjustable in config.py:
| Key | Description | Default |
|---|---|---|
| HOST | Bind address for the API service | 0.0.0.0 |
| PORT | Listening port | 5000 |
| DEBUG | Run in debug mode | True (change for production) |
Key Endpoints
All GET unless noted. Full path is relative to the API host/port.
| Endpoint | Purpose |
|---|---|
/implants |
List all implants |
/liveimplants / /liveimplantview |
List/render only currently-live implants |
/implants/kill/<implant_id> |
Kill a specific implant |
/implantview[/<number_of_rows>] |
Rendered implant view, optionally row-limited |
/tasks[/<number_of_rows>] |
List tasks, optionally row-limited |
/tasks/implant/<implant_id> |
Tasks for a specific implant |
/taskview[/<number_of_rows>] |
Rendered task view |
/taskview/implant/<implant_id> |
Rendered task view for one implant |
/newtasks / /newtasksview[/<number_of_rows>] |
Pending/queued tasks |
/newtasksview/del/<task_id> |
Delete a queued task |
/creds |
Harvested credentials |
/files / /files/<int:number_of_files> |
Downloaded files |
/file/<path:filename> |
Fetch a specific downloaded file |
/hostedfiles |
Files currently hosted by the server |
/payloads / /payload/<path:filename> |
List / fetch generated payloads |
/autoruns / /autorunsview (GET, POST) |
View/manage autorun entries |
/autorunsview/del/<autorun_id> |
Delete an autorun entry |
/autocompletecmd/<implant_id> |
Autocomplete suggestions for an implant |
/autocompletecmd/<implant_id>/<commandRemote> |
Autocomplete for a specific partial command |
/c2server |
C2 server status/info |
/c2messages / /c2messagesview |
Raw C2 server log messages |
/c2view[/<number_of_rows>] |
Rendered C2 log view |
/commands |
Available commands |
/opsecentrys |
OPSEC-flagged command entries |
/mitrettps |
MITRE ATT&CK TTP mappings used |
/powerstatus |
Server power/running status |
/urls |
Configured C2 URLs |
Reporting
PoshC2 logs every command and its full output from every implant to the project database, along with metadata (issuing user, hostname, etc.). Time-zone note: PoshC2 timestamps use the local server clock — if operating across time zones, set the server's time-zone deliberately so report timestamps line up with the engagement's reference time.
Generate reports:
generate-reports
Run from the Implant-Handler's top-level prompt. Produces HTML report pages in the project's reports directory, plus CSV dumps of the same data.
CSV only:
generate-csvs
Find PoshC2 Command (fpc): search command input/output text and filter by various criteria — handy for locating a specific action across a long engagement history:
fpc <search term>
OPSEC Considerations
- Prefer HTTPS over HTTP for
PayloadCommsHost— while all traffic is encrypted at the application layer regardless, HTTPS additionally hides headers (needed for Domain Fronting) unless the target performs SSL Inspection. - Always run behind an Apache C2 proxy with
mod_rewrite— never expose the real C2 Server directly to the target or the internet. - Whitelist the C2 proxy to the client's known IP ranges (and the
X-Forwarded-Forheader if Domain Fronting). - Set a sensible Jitter — a fixed beacon interval is a network IOC; PoshC2's
Jitterconfig randomizes aroundDefaultSleep. - Set a KillDate for every engagement so persistence/implants self-expire.
- Rotate credentials — change the default API credentials (
poshc2/change_on_install) before startingposh-api-server. - Tunnel the API over SSH rather than exposing port 5000 externally.
- Use unique projects per engagement — project-specific encryption keys mean payloads from one engagement cannot accidentally connect to another project's server.
- Don't update mid-engagement —
posh-updatecan introduce incompatibilities with an already-running project. - OPSEC-flag risky commands via
poshc2/client/Opsec.pyso operators get a confirmation prompt before firing them. - Consider the 404-cookie-decrypter workflow for extra situational awareness on rejected/whitelisted-out connections.
Cheatsheet
Project Setup
posh-project -n my-engagement
posh-config
posh-server
Starting Everything (Separate Terminals)
# Terminal 1 - C2 Server
posh-server
# Terminal 2 - Operator 1
posh -u operator1
# Terminal 3 - Operator 2 (multi-op support)
posh -u operator2
# View server log without a second server instance
posh-log
Running as a Service
posh-service # install + start + tail log
posh-log # resume viewing
posh-stop-service # stop
Docker
posh-server -t latest
posh -u crashoverride -t latest
posh-stop-server
Implant Selection
1 # select implant 1 -> PS 1>
1,2,3,4 # select multiple -> 1,2,3>
ALL # select all implants -> ALL>
back # return to top-level prompt
Core Implant Commands
help # context-sensitive command list
searchhelp <term> # search help text (e.g. searchhelp mimikatz)
ls / pwd / dir # navigation
get-content <file> # read a file
upload-file / download-file
get-screenshot # single screenshot
get-screenshotmulti # multi-monitor screenshot
ps # list processes
migrate # migrate to another process
beacon / setbeacon # view / set beacon interval
kill-implant # terminate the implant
hide-implant / unhide-implant
Loading & Running Modules
# PowerShell
loadmodule Invoke-Mimikatz.ps1
Invoke-Mimikatz -Command '"sekurlsa::logonpasswords"'
# Python
loadmodule MyPythonScript.py
my_python_function("arg")
# C# / Sharp
loadmodule Rubeus.exe
run-exe Rubeus.Program Rubeus kerberoast
run-dll Namespace.Class Assembly EntryMethod <args>
run-exe-background Namespace.Class Assembly <args> # non-blocking
Common Aliased Sharp Tools (out of the box)
seatbelt -> run-exe Seatbelt.Program Seatbelt
sharpup -> run-exe SharpUp.Program SharpUp
safetydump -> run-exe SafetyDump.Program SafetyDump
rubeus -> run-exe Rubeus.Program Rubeus
sharpview -> run-exe SharpView.Program SharpView
sharphound -> run-exe Sharphound2.Sharphound Sharphound
sharpweb -> run-exe SharpWeb.Program SharbWeb
watson -> run-exe Watson.Program Watson
SOCKS Proxy (SharpSocks)
# In PowerShell implant
PS 1> sharpsocks
# Copy/run the printed SharpSocksServerCore command on the C2 server, then press Enter to connect
# On attack box
proxychains nmap -sT -Pn 192.168.1.0/24
proxychains rdesktop 192.168.1.10
Communications Rotation
invoke-urlcheck -urls https://url1.com,https://url2.com -domainfront header1.com,header2.com -uri /some/path/
enable-rotation
get-rotation
Reporting
generate-reports # HTML + CSV to project reports dir
generate-csvs # CSV only
fpc <search term> # find PoshC2 command across history
API
posh-api-server # start API on :5000 (poshc2 / change_on_install)
ssh poshc2server -L 5000:127.0.0.1:5000 # tunnel it instead of exposing directly
curl http://127.0.0.1:5000/implants
curl http://127.0.0.1:5000/tasks/implant/1
curl http://127.0.0.1:5000/creds
Updating / Version Pinning
posh-update -b master # or -b dev / a feature branch; NOT recommended mid-engagement
git tag --list
git reset --hard v4.8 # pin to an older release (overwrites local changes)
Troubleshooting
posh-server fails to bind / second operator can't start it
Symptom: running posh-server a second time errors trying to bind the port.
Fix: only one C2 Server process per project. Additional operators should use posh -u <name> (Implant-Handler) and posh-log to tail the log, not another posh-server.
Implant never checks in
Possible causes:
- Wrong
PayloadCommsHostin the active project config — regenerate payloads after fixing. - C2 proxy misconfigured — verify the Apache
rewrite-rules.txtrules are actually loaded in the vhost and the vhost is active. - Whitelist blocking the target IP — check
X-Forwarded-Forwhitelisting if Domain Fronting is enabled; verify the target's egress IP is included. - KillDate has passed — implants configured with an expired KillDate won't beacon.
- .NET missing for Domain Fronting — if
.NET v4.0.30319isn't present, the implant silently falls back to a direct CDN connection instead of using the front.
Domain Fronting isn't hiding the real Host header
Cause: SSL Inspection is active on the target network — this is one of the few things that can defeat Domain Fronting, since it lets the inspecting proxy see the true Host header inside the encrypted tunnel.
C# module fails with a reflection / entry-point error
Fix:
- Double check Namespace, Class, and Assembly Name match exactly (Assembly Name is not always the same as the project name — check
.csprojor VS project Properties). - For DLLs, confirm the entry-point method in
run-dllactually lives in the specified class/namespace. - Re-
loadmodulethe file if the implant was migrated or the process was recycled.
Autoload fired unexpectedly / loaded a module you didn't intend
Cause: an autoload entry in poshc2/server/Autoloads.py matched a command prefix you didn't expect (e.g. typo'd command name overlapping an autoload trigger).
Fix: review/tighten the startswith() conditions in the relevant run_autoloads_* function, or remove the autoload if it's too broad for your workflow.
API returns 401 / can't authenticate
Fix: confirm you're using the credentials set in your project (defaults are poshc2 / change_on_install unless already rotated) and that you're hitting the right host/port — remember the API is meant to be reached via SSH tunnel (ssh poshc2server -L 5000:127.0.0.1:5000), not exposed directly.
Update broke an in-progress project
Cause: posh-update was run mid-engagement.
Fix: this is explicitly discouraged by the docs. If it happens, check git stash list (updates auto-stash local changes) and consider git reset --hard <last-known-good-tag> to roll back, then re-run the install script for that version if needed.
References
- GitHub Repository: https://github.com/nettitude/PoshC2
- Official Documentation: https://poshc2.readthedocs.io/
- SharpSocks (SOCKS proxy component): https://github.com/nettitude/SharpSocks
- 5-VPS OpenVPN Proxy Walkthrough: https://labs.nettitude.com/blog/making-poshc2-more-accessible-with-a-5-vps/
- SharpSocks / PoshC2 v3 Blog: https://labs.nettitude.com/blog/poshc2-v3-with-socks-proxy-sharpsocks/
- OPSEC / Cookie Decrypter Blog: https://labs.nettitude.com/blog/operational-security-with-poshc2-framework/
- Contact:
- Twitter/X: @Nettitude_Labs
- Slack: poshc2.slack.com (request invite via labs@nettitude.com)
- Email: labs@nettitude.com
Key Contributors:
- Ben Turner — @benpturner
- Rob Bone — @m0rv4i
- Rob Maslen — @rbmaslen
- Doug McLeod — @b4ggio_su
- Rich Hicks — @scriptmonkey_
- Phil Lynch — @plynch98
- Ross Bingham — @pwndexter
Appendix: Common Operational Mistakes
- Not running behind an Apache C2 proxy — exposing the real C2 Server directly defeats the entire rewrite-rule OPSEC model PoshC2 is built around.
- Leaving default API credentials —
poshc2/change_on_installships as the literal default password; change it before startingposh-api-server. - Exposing the API port directly — tunnel over SSH (
ssh poshc2server -L 5000:127.0.0.1:5000) instead of binding0.0.0.0:5000to the internet. - Fixed beacon sleep with no Jitter — an exact, unjittered interval is a textbook network IOC; always set a meaningful
Jittervalue. - No KillDate set — implants and persistence mechanisms will happily keep beaconing long after an engagement ends without one.
- Forgetting to whitelist
X-Forwarded-Forwhen Domain Fronting — whitelisting onlyREMOTE_ADDRwill whitelist the CDN, not the real client, breaking the intended lockdown. - Updating PoshC2 mid-engagement —
posh-updatecan introduce breaking changes against an already-running project; wait until the engagement wraps. - Reusing a project across engagements — mixing clients in one project directory risks cross-contamination of logs, creds, and reports; use
posh-project -nfor a fresh one each time. - Assuming HTTP is unencrypted — operators sometimes forget PoshC2 encrypts at the application layer regardless of HTTP vs HTTPS; the real reason to prefer HTTPS is header confidentiality (Domain Fronting) and blending in, not payload encryption itself.
- Broad autoload prefixes — an overly generic
startswith()match inAutoloads.pycan silently load a module into memory when the operator only meant to run a similarly-named command. - Not exporting reports/CSVs before teardown —
generate-reports/generate-csvspull from the project database; back these up (and the database itself) before decommissioning project infrastructure.
Last Updated: July 2026 Wiki Revision: 1.0 (PoshC2 v9.0 "Revival") Contributed by: Red Team Documentation Project