Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Menelaus29/c2-framework/llms.txt

Use this file to discover all available pages before exploring further.

Overview

SessionManager holds all active agent sessions in memory for fast access, synchronized to the database on every mutation. It provides thread-safe operations using asyncio locks. Source: server/session_manager.py

SessionState Dataclass

@dataclass
class SessionState:
    session_id: str
    hostname:   str
    username:   str
    os:         str
    agent_ver:  str
    first_seen: float
    last_seen:  float
    jitter_pct: int
    active:     bool = True
Represents the state of a single agent session.
session_id
str
Unique UUID for the session
hostname
str
Agent’s hostname
username
str
Current username on agent system
os
str
Operating system version (e.g., “Windows 10 22H2”)
agent_ver
str
Agent version string (e.g., “1.0.0”)
first_seen
float
Unix timestamp when agent first checked in
last_seen
float
Unix timestamp of last activity (beacon, heartbeat)
jitter_pct
int
Beacon jitter percentage (0-100)
active
bool
default:"True"
Whether session is active. Deactivated sessions receive TERMINATE message.

SessionManager Class

Constructor

def __init__(self)
Initializes empty session manager with asyncio lock for thread safety. Internal State:
self._sessions: dict[str, SessionState] = {}
self._lock = asyncio.Lock()

Methods

create_session()

async def create_session(self, payload: dict, db: Database) -> str
Create a new in-memory SessionState, persist to DB, return session_id.
payload
dict
required
Agent check-in data containing:
  • hostname: Agent hostname
  • username: Current username
  • os: Operating system version
  • agent_ver: Agent version string
  • jitter_pct: Beacon jitter percentage (optional, defaults to 0)
db
Database
required
Database instance for persistence
return
str
Newly created session_id (UUID)
Example:
from server.session_manager import SessionManager
from server.storage import Database

async with Database() as db:
    mgr = SessionManager()
    
    payload = {
        'hostname': 'VICTIM-PC',
        'username': 'jdoe',
        'os': 'Windows 10 22H2',
        'agent_ver': '1.0.0',
        'jitter_pct': 20
    }
    
    session_id = await mgr.create_session(payload, db)
    print(f"Created session: {session_id}")
    # Output: Created session: 550e8400-e29b-41d4-a716-446655440000
Behavior:
  • Generates new UUID for session_id
  • Sets first_seen and last_seen to current time
  • Defaults jitter_pct to 0 if not provided
  • Stores session in memory with lock
  • Persists to database
  • Logs creation event

get_session()

async def get_session(self, session_id: str) -> SessionState | None
Return the in-memory SessionState for session_id, or None if not found.
session_id
str
required
UUID of the session to retrieve
return
SessionState | None
SessionState object if found, None otherwise
Example:
session = await mgr.get_session('550e8400-e29b-41d4-a716-446655440000')
if session:
    print(f"Hostname: {session.hostname}")
    print(f"Last seen: {session.last_seen}")
    print(f"Active: {session.active}")
else:
    print("Session not found")
Performance: O(1) dictionary lookup with async lock

update_last_seen()

async def update_last_seen(self, session_id: str, db: Database) -> None
Update last_seen in memory and persist to DB.
session_id
str
required
UUID of the session to update
db
Database
required
Database instance for persistence
Example:
# Called on every beacon/heartbeat
await mgr.update_last_seen(session_id, db)
Behavior:
  • Updates session.last_seen to time.time()
  • Only updates if session exists in memory
  • Persists change to database
  • Silent no-op if session not found

list_sessions()

async def list_sessions(self) -> list[SessionState]
Return all in-memory sessions ordered by last_seen descending.
return
list[SessionState]
List of SessionState objects sorted by most recent activity first
Example:
sessions = await mgr.list_sessions()

for session in sessions:
    status = "Active" if session.active else "Inactive"
    print(f"{session.hostname} ({session.username}) - {status}")
    print(f"  Last seen: {session.last_seen}")
    print(f"  Session ID: {session.session_id}")
Output:
VICTIM-PC-2 (bob) - Active
  Last seen: 1710177845.23
  Session ID: 660e8400-e29b-41d4-a716-446655440000
VICTIM-PC (jdoe) - Inactive
  Last seen: 1710177800.12
  Session ID: 550e8400-e29b-41d4-a716-446655440000
Use Cases:
  • CLI sessions command
  • Web UI session listing
  • Monitoring dashboards

deactivate_session()

async def deactivate_session(self, session_id: str, db: Database) -> None
Mark session inactive in memory and persist to DB.
session_id
str
required
UUID of the session to deactivate
db
Database
required
Database instance for persistence
Example:
# Operator kills session via CLI
await mgr.deactivate_session(session_id, db)

# Next beacon from this agent will receive TERMINATE message
Behavior:
  • Sets session.active = False in memory
  • Persists to database (sets active=0)
  • Logs deactivation event
  • Agent receives TERMINATE on next beacon
  • Silent no-op if session not found
Note: Deactivated sessions remain in memory and database for audit/historical purposes.

restore_from_db()

async def restore_from_db(self, db: Database) -> None
Reload active sessions from DB into memory on server restart.
db
Database
required
Database instance to restore from
Example:
# Called during server startup
mgr = SessionManager()
await mgr.restore_from_db(db)
logger.info('sessions restored from DB', extra={'count': len(mgr._sessions)})
Behavior:
  • Fetches all session rows from database
  • Only restores sessions where active=1
  • Reconstructs SessionState objects in memory
  • Logs count of restored sessions
Use Case: Server restart without losing active agent sessions

Usage Patterns

Complete Session Lifecycle

async with Database() as db:
    mgr = SessionManager()
    
    # Server startup: restore existing sessions
    await mgr.restore_from_db(db)
    
    # Agent checks in
    payload = {
        'hostname': 'VICTIM-PC',
        'username': 'jdoe',
        'os': 'Windows 10 22H2',
        'agent_ver': '1.0.0',
        'jitter_pct': 20
    }
    session_id = await mgr.create_session(payload, db)
    
    # Agent beacons periodically
    await mgr.update_last_seen(session_id, db)
    
    # Operator lists sessions
    sessions = await mgr.list_sessions()
    for s in sessions:
        if s.active:
            print(f"Active: {s.hostname}")
    
    # Operator kills session
    await mgr.deactivate_session(session_id, db)
    
    # Next beacon: server checks session status
    session = await mgr.get_session(session_id)
    if not session.active:
        # Send TERMINATE message to agent
        pass

Thread Safety

All methods use async with self._lock for thread-safe access to _sessions dict:
async with self._lock:
    self._sessions[session_id] = state
This ensures safe concurrent access from multiple beacon requests.

Integration Points

Server Main

Used in server_main.py beacon handlers:
from server.session_manager import SessionManager

session_mgr = SessionManager()

# Check-in handler
session_id = await session_mgr.create_session(payload, db)

# Task pull handler
session = await session_mgr.get_session(session_id)
if not session:
    return None  # Invalid session
await session_mgr.update_last_seen(session_id, db)

CLI Commands

Used in cli/session_commands.py:
# List active sessions
sessions = await session_mgr.list_sessions()
for session in sessions:
    if session.active:
        print_session(session)

# Kill session
await session_mgr.deactivate_session(session_id, db)

Testing

Self-Test Suite

Run built-in tests:
python -m server.session_manager
Test Coverage:
  • create_session generates valid UUID
  • get_session returns correct state
  • get_session returns None for unknown ID
  • update_last_seen advances timestamp
  • list_sessions sorts by last_seen descending
  • deactivate_session sets active flag
  • restore_from_db only loads active sessions
Output:
Running session_manager self-test...
  [OK] create_session
  [OK] get_session
  [OK] get_session returns None for unknown session_id
  [OK] update_last_seen
  [OK] list_sessions
  [OK] deactivate_session
  [OK] restore_from_db

All session_manager self-tests passed.

Logging

Structured logging with contextual fields:
logger.info('session created',
            extra={'session_id': session_id, 'hostname': state.hostname})
Events:
  • Session created
  • Session deactivated
  • Sessions restored from DB

Performance Considerations

All active sessions held in memory. For 10,000 agents, SessionState objects consume ~2-3 MB RAM.
Single lock protects _sessions dict. Read operations like get_session are fast. High beacon rates may see contention.
Every mutation (create, update_last_seen, deactivate) writes to DB. Uses async I/O for non-blocking operations.

Server Main

FastAPI beacon endpoint integration

Database

Session persistence layer

CommandQueue

Task management per session

Session Commands

CLI session management