Tutorial 02: Operator Ticketing¶
Wintermute provides a backend-agnostic ticketing system. This allows operators to track vulnerabilities and incidents through a unified Ticket class, while the actual data is persisted to a backend like Bugzilla, Jira, or a simple JSON file.
1. The Ticket Protocol¶
The Ticket system relies on backends that implement the TicketBackend protocol. This means you can swap backends (e.g., moving from Bugzilla to a local JSON storage) without changing any of the code that creates or updates tickets.
from wintermute.backends.bugzilla import BugzillaBackend
from wintermute.tickets import Status, Ticket
# Initialize the Bugzilla backend pointing to our research lab server
bz_backend = BugzillaBackend(
base_url="http://192.168.0.145/bugzilla",
api_key="YOUR_API_KEY_HERE",
default_product="FirmwareSecurity",
default_component="Hardware-Abid",
)
# Register it as the default backend
Ticket.register_backend("bugzilla", bz_backend, make_default=True)
print("Bugzilla hardware security backend registered.")
2. Creating a Ticket¶
When creating a ticket, you can provide core fields like title and description, as well as custom_fields which are passed directly to the backend. In hardware auditing, we use these fields to track specific hardware revisions and firmware versions.
tid = Ticket.create(
title="Unauthenticated U-Boot Console Access via UART",
description="The target device exposes a root shell on UART0 when the 's' key is held during boot, bypassing all authentication mechanisms.",
assignee="researcher@wintermute.dev",
requester="lead-auditor@wintermute.dev",
custom_fields={
"product": "Automotive-ECU",
"component": "Bootloader",
"cf_hardware_id": "HW-REV-B.2",
"cf_firmware_ver": "U-Boot-2023.10-rc4",
"op_sys": "Linux",
"version": "unspecified",
},
)
print(f"Critical hardware vulnerability ticket created: {tid}")
3. Reading and Updating Tickets¶
Once a ticket is created, you can retrieve it, add comments, and update its status using the same unified interface.
# Read the ticket back
t = Ticket.read(tid)
print(f"Current Status: {t.data.status}")
# Add research findings as a comment
Ticket.comment(
tid,
text="Verified injection of 'init=/bin/sh' into bootargs via environment modification.",
author="researcher@wintermute.dev",
)
# Update status to in-progress
Ticket.update(tid, status=Status.IN_PROGRESS)
print(f"Ticket {tid} updated with exploitation details.")
4. Swapping Backends¶
If you need to switch backends later (e.g., for air-gapped operations where no Bugzilla server is reachable), the call sites for Ticket.create and Ticket.read remain exactly the same. Use the built-in InMemoryBackend for offline work.
from wintermute.tickets import InMemoryBackend
# Switch to in-memory storage for air-gapped auditing
Ticket.register_backend("local", InMemoryBackend())
Ticket.use_backend("local")
print("Switched to in-memory backend. Backend-agnostic interface maintained.")