This project demonstrates a full attack-and-defense cycle around keystroke interception
over a network channel. It runs entirely on a single machine using localhost
(127.0.0.1) sockets — no VMs, no second machine needed.
┌─────────────────────┐ TCP/9999 (localhost) ┌──────────────────────┐
│ agent.py │ ────────────────────────▶ │ c2_listener.py │
│ │ │ │
│ • Hooks keyboard │ Payload structure: │ • Receives packets │
│ • Buffers keys │ [4-byte len][JSON body] │ • Decrypts AES-CBC │
│ • AES-256 encrypt │ │ • Displays + logs │
│ • Sends every 2s │ │ • Multi-session │
│ • Auto-reconnects │ │ │
└─────────────────────┘ └──────────────────────┘
│
detector.py
(polls port 9999 every 3s,
reports WATCHING / SAFE)
| File | Module | Description |
|---|---|---|
agent.py |
1 & 3 | Keystroke capture + AES-256-CBC encrypted C2 sender |
c2_listener.py |
2 & 3 | C2 server — receive, decrypt, display, and log keystrokes |
detector.py |
4 | Port-based detector — polls C2 port and reports listener status |
requirements.txt |
Python dependencies |
pip install -r requirements.txtNote on Scapy:
scapyis listed inrequirements.txtfor potential future packet-sniffing extensions. The currentdetector.pyuses standard sockets only and does not require administrator/root privileges.
Terminal 1 — Start the C2 Listener:
python c2_listener.pyTerminal 2 — Start the Agent:
# Encrypted mode (default — recommended)
python agent.py
# Plaintext mode — for Wireshark demo to show readable traffic
python agent.py --plainTerminal 3 — Start the Detector:
python detector.pyNow type anything. Keystrokes appear in the C2 listener terminal in real time, and the detector reports the active interception channel every 3 seconds.
- Hooks the keyboard globally via
pynput - Buffers captured keystrokes and flushes every 2 seconds
- Encrypts each payload with AES-256-CBC before sending
- Resilient connection: if the listener is not running, the agent retries every 3 seconds without crashing — and reconnects automatically when available
- Supports two modes:
- Default: AES-256-CBC encrypted (binary, unreadable in transit)
--plain: raw JSON (readable in Wireshark — demonstrates risk)
- Press ESC to stop the agent gracefully
Payload structure per flush:
{
"timestamp": "<ISO-8601>",
"keystrokes": ["h", "e", "l", "l", "o", "[ENTER]"],
"count": 6
}
Special keys (Enter, Shift, Tab, etc.) are recorded as [KEYNAME].
- Binds to
0.0.0.0:9999, accepts multiple simultaneous agent connections - Each connection is handled in its own daemon thread
- Reads a 4-byte length prefix then receives the exact payload bytes
- Decrypts AES-256-CBC payloads using the pre-shared key
- Supports both encrypted and plaintext payloads (plaintext flagged with
⚠️ ) - Logs all received keystrokes to
captured_keystrokes.log(JSON, one entry per line) - Displays per-session running totals and color-coded output in the terminal
- Polls port 9999 on localhost every 3 seconds via a TCP connect probe
- Classifies and reports one of two states:
| State | Meaning |
|---|---|
| 👁️ WATCHING | Port 9999 is open — a C2 listener is actively running |
| ❌ SAFE | Port 9999 is closed — no active interception channel |
- No admin/root required — uses only standard socket connections
- Timestamped output on every check cycle
- Stop with Ctrl+C
SHARED_SECRET ──SHA-256 KDF──▶ 32-byte AES Key
│
Keystroke JSON ────────────┤
Random IV (16 bytes) ──────┤
▼
AES-256-CBC
│
▼
{ "iv": base64(IV), "ct": base64(ciphertext), "encrypted": true }
──────────────── TCP/9999 ────────────────▶
The C2 listener reverses this exactly using the same SHARED_SECRET.
- Start
c2_listener.py, thenagent.py(no flags) - Open Wireshark → capture on Loopback Adapter → filter:
tcp.port == 9999 - Observe: payload bytes are binary and unreadable — AES encrypted
- C2 listener terminal shows decrypted, readable keystrokes in real time
detector.pyreports 👁️ WATCHING every 3 seconds
- Stop the agent, restart with
python agent.py --plain - In Wireshark, right-click a packet → Follow → TCP Stream
- Observe: the full JSON payload is readable — passwords and text are visible in clear
- C2 listener flags each batch with
⚠️ PLAIN in red - This demonstrates exactly why encryption matters
- Start
detector.pyfirst, before the listener — observe ❌ SAFE - Start
c2_listener.py— within 3 seconds detector switches to 👁️ WATCHING - Stop
c2_listener.py— detector reverts to ❌ SAFE on the next poll cycle
| Concept | Where |
|---|---|
| Keylogger / spyware mechanics | agent.py — pynput global keyboard hook |
| C2 (Command & Control) infrastructure | agent.py + c2_listener.py — TCP socket channel |
| Resilient malware (auto-reconnect) | agent.py — retry loop in connect() |
| AES-256-CBC symmetric encryption | agent.py encrypt → c2_listener.py decrypt |
| Pre-shared key (PSK) + SHA-256 KDF | SHARED_SECRET → derive_key() in both files |
| Length-prefixed framing (protocol design) | 4-byte big-endian header in sender and receiver |
| Plaintext vs. encrypted traffic contrast | --plain flag in agent.py; Wireshark inspection |
| Packet / traffic analysis | Wireshark on loopback, tcp.port == 9999 |
| Port-based threat detection | detector.py — TCP connect probe every 3s |
| Attack vs. defense perspective | Agent = attacker, Listener = exfil server, Detector = defender |
| Multi-session C2 server | c2_listener.py — threaded client handling |
All tunable constants are at the top of each file:
| Constant | File(s) | Default | Description |
|---|---|---|---|
C2_HOST |
agent.py, detector.py |
127.0.0.1 |
Target/monitored host |
C2_PORT |
all files | 9999 |
C2 TCP port |
SHARED_SECRET |
agent.py, c2_listener.py |
network_security_project_2024 |
AES key derivation secret |
BUFFER_FLUSH_INTERVAL |
agent.py |
2.0 s |
How often keystrokes are sent |
RETRY_INTERVAL |
agent.py |
3 s |
Reconnect delay if listener is down |
CHECK_INTERVAL |
detector.py |
3 s |
Port poll frequency |
LOG_FILE |
c2_listener.py |
captured_keystrokes.log |
Output log path |
LISTEN_HOST |
c2_listener.py |
0.0.0.0 |
Bind address for the C2 server |
Install with:
pip install -r requirements.txtThis project is built strictly for educational purposes in a controlled,
single-machine environment. All traffic is confined to localhost (127.0.0.1)
and never leaves your machine.
Deploying keyloggers, C2 infrastructure, or any form of unauthorized interception against real users or systems without explicit written consent is illegal under:
- IT Act 2000 (India) — Sections 43, 66, 66B
- Computer Fraud and Abuse Act (USA)
- Computer Misuse Act 1990 (UK)
- Equivalent laws in all other jurisdictions
Use this code only in sandboxed, consent-based lab environments.