Module Structure
Supported Formats and Techniques
7 document formats, each with format-specific hiding techniques:| Format | Techniques |
|---|---|
| Phase 1: white_ink, off_canvas, metadata. Phase 2: tiny_text, white_rect, form_field, annotation, javascript, embedded_file, incremental | |
| Image | visible_text, subtle_text, exif_metadata |
| Markdown | html_comment, link_reference, zero_width, hidden_block |
| HTML | script_comment, css_offscreen, data_attribute, meta_tag |
| DOCX | docx_hidden_text, docx_tiny_text, docx_white_text, docx_comment, docx_metadata, docx_header_footer |
| ICS | ics_description, ics_location, ics_valarm, ics_x_property |
| EML | eml_x_header, eml_html_hidden, eml_attachment |
Payload System
PayloadStyle controls the social engineering framing:obvious, citation, reviewer, helpful, academic, compliance, datasource.
PayloadType controls the attack objective: callback (default, benign proof of execution), exfil_summary, exfil_context, ssrf_internal, instruction_override, tool_abuse, persistence. Non-callback types require the --dangerous flag.
Generation Pipeline
generate_documents() is the core function:
- For each technique in the request, call the format-specific generator
- Each generator embeds the payload instruction using the hiding technique
- A unique UUID and authentication token are generated per document
- The callback URL is embedded in the payload (
{callback_url}/c/{uuid}/{token}) - Campaign records are created in the IPI database
GenerateResultreturned with list of campaigns and any errors
Callback Listener
qai ipi listen starts a FastAPI server that receives HTTP callbacks when AI systems execute the hidden payload.
Hit model fields: uuid (campaign match), source_ip, user_agent, timestamp, token_valid (boolean), confidence (HIGH/MEDIUM/LOW).
Confidence scoring:
- HIGH — Valid campaign token present in the callback URL
- MEDIUM — No token but User-Agent matches programmatic HTTP clients (python-requests, httpx, curl)
- LOW — No token and browser/scanner User-Agent
WebSocket Bridge
The IPI listener runs as a separate process. When a hit arrives, it notifies the main qai web server via an internal HTTP POST to/api/ipi/internal/notify. Authentication uses a shared bridge token from ~/.qai/bridge.token.
The --notify-url flag on qai ipi listen controls the target (defaults to http://127.0.0.1:8899). The web server broadcasts the hit via WebSocket so the IPI tab in the run results view updates in real time.
Guidance Builder
build_ipi_guidance() generates a RunGuidance with four blocks:
- INVENTORY — Lists generated files with technique, callback URL, and token
- TRIGGER_PROMPTS — Format-aware, profile-specific prompts (AnythingLLM, Open WebUI, Generic) designed to cause the target to ingest the document
- DEPLOYMENT_STEPS — Ordered instructions for uploading and triggering
- MONITORING — Confidence level explanations for interpreting hits
Adapter
IPIAdapter wraps generation for orchestrator workflows. It creates a child run, calls generate_documents() via asyncio.to_thread(), persists results, builds guidance, and transitions to WAITING_FOR_USER status (since deployment is a manual step).
Database
IPI maintains its own tables (ipi_campaigns, ipi_hits) in the shared ~/.qai/qai.db SQLite database, accessed through ipi/db.py. Campaign data is also bridged to the core runs/findings tables via mapper.py.