Manual proxy interception and point-and-click scanning are fine for ad-hoc bug hunting, but they collapse the moment you need reproducible security testing across multi-branch staging environments or high-velocity microservices.
Modern offensive engineering demands headless automation: spinning up Burp Suite without a graphical user interface (GUI), injecting target traffic through pre-configured proxies, executing custom active/passive scanning rules, and parsing structured findings into CI/CD pipelines.
Here is the blueprint for engineering an automated, headless Burp Suite extension pipeline using Python, headless CLI flags, and automated test runners.
1. Anatomy of Headless Burp Suite Execution
Burp Suite can run completely unattended by leveraging CLI switches and JSON configuration blocks. This bypasses the startup wizard, automatically creates or updates project files, loads predefined extension bundles, and binds the proxy listener to a dedicated loopback port.
Essential CLI Flags
| Flag | Purpose | Production Use Case |
|---|---|---|
--unattended |
Suppresses interactive popups, update dialogues, and confirmation prompts. | Required for headless execution in containerized environments. |
--project-file=<path> |
Opens or creates a specific .burp project database on disk. |
Retains raw request/response logs and scanner issues for post-test analysis. |
--config-file=<path> |
Ingests a JSON configuration for project-level settings (scope, proxy listeners, scanner options). | Defines target scope, timeouts, and live passive scanning rules. |
--user-config-file=<path> |
Ingests user-level configuration (e.g., Montoya/Java/Python extension paths). | Automatically loads custom extensions upon boot. |
2. Declarative Configuration: Defining the Headless Profile
Before invoking the Java runtime, generate a declarative configuration JSON that disables telemetry, sets proxy listeners, and enforces strict target scoping.
burp_project_config.json
{
"project_options": {
"connections": {
"upstream_proxy": {
"use_proxy": false
},
"timeouts": {
"connect": 10,
"normal": 30
}
},
"target": {
"scope": {
"advanced_mode": true,
"include": [
{
"enabled": true,
"protocol": "https",
"host": "^staging-app\\.internal\\.lab$",
"port": "^443$",
"file": "^/.*"
}
]
}
},
"proxy": {
"request_listeners": [
{
"listen_mode": "loopback_only",
"listener_port": 8080,
"running": true,
"support_invisible_proxying": false
}
]
}
}
}
3. The Python Orchestrator: burp_headless_runner.py
The Python orchestrator manages the Burp daemon lifecycle: booting the process, polling port readiness, routing automated integration tests through the upstream proxy, and capturing findings.
#!/usr/bin/env python3
"""
Burp Suite Headless Pipeline Orchestrator
Executes unattended Burp Suite instances, routes test traffic, and extracts findings.
"""
import os
import sys
import time
import socket
import subprocess
import requests
BURP_JAR = os.getenv("BURP_JAR_PATH", "/opt/burpsuite/burpsuite_pro.jar")
PROJECT_FILE = "build/reports/burp_session.burp"
CONFIG_FILE = "burp_project_config.json"
PROXY_HOST = "127.0.0.1"
PROXY_PORT = 8080
def is_port_open(host: str, port: int) -> bool:
"""Checks if the proxy port is accepting connections."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1.0)
return s.connect_ex((host, port)) == 0
def launch_burp_daemon() -> subprocess.Popen:
"""Spawns Burp Suite in headless unattended mode."""
os.makedirs(os.path.dirname(PROJECT_FILE), exist_ok=True)
cmd = [
"java",
"-Djava.awt.headless=true",
"-Xmx4g",
"-jar", BURP_JAR,
"--unattended",
f"--project-file={PROJECT_FILE}",
f"--config-file={CONFIG_FILE}"
]
print(f"[*] Spawning Burp Suite Headless Daemon: {' '.join(cmd)}")
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
return process
def wait_for_proxy(timeout: int = 45):
"""Blocks until the proxy listener is confirmed live."""
print("[*] Waiting for Burp Proxy to bind...")
start_time = time.time()
while time.time() - start_time < timeout:
if is_port_open(PROXY_HOST, PROXY_PORT):
print(f"[+] Burp Proxy listening on {PROXY_HOST}:{PROXY_PORT}")
return True
time.sleep(1.5)
raise TimeoutError("[-] Burp Suite failed to initialize proxy within timeout window.")
def run_integration_suite():
"""Simulates or triggers automated end-to-end traffic through Burp."""
print("[*] Dispatching synthetic test requests through Burp proxy...")
proxies = {
"http": f"http://{PROXY_HOST}:{PROXY_PORT}",
"https": f"http://{PROXY_HOST}:{PROXY_PORT}",
}
# Example endpoint verification against staging lab target
target_endpoints = [
"https://staging-app.internal.lab/api/v1/user/profile",
"https://staging-app.internal.lab/api/v1/auth/session",
"https://staging-app.internal.lab/dashboard"
]
for url in target_endpoints:
try:
resp = requests.get(url, proxies=proxies, verify=False, timeout=5)
print(f" [->] GET {url} | Status: {resp.status_code}")
except Exception as e:
print(f" [!] Failed request to {url}: {e}")
def terminate_daemon(process: subprocess.Popen):
"""Gracefully shuts down the Burp process."""
print("[*] Terminating Burp Suite Daemon...")
process.terminate()
try:
process.wait(timeout=15)
print("[+] Process exited cleanly.")
except subprocess.TimeoutExpired:
print("[!] Forcing SIGKILL on Burp Suite...")
process.kill()
if __name__ == "__main__":
burp_proc = launch_burp_daemon()
try:
wait_for_proxy()
run_integration_suite()
# Allow passive scanning & Montoya listeners to process queue
time.sleep(10)
finally:
terminate_daemon(burp_proc)
4. Loading Custom Extensions via Headless CLI
To execute custom Montoya API or legacy Python/Jython extensions during a headless run, inject their configurations through a user_config.json file.
user_config.json
{
"user_options": {
"extender": {
"python": {
"location_of_jython_standalone_jar_file": "/opt/jython/jython-standalone-2.7.3.jar"
},
"extensions": [
{
"errors": "ui",
"extension_file": "/opt/burp_extensions/custom_auth_logger.py",
"extension_type": "python",
"loaded": true,
"output": "ui"
}
]
}
}
}
Pass the file during startup:
java -Djava.awt.headless=true -Xmx4g -jar burpsuite_pro.jar \
--unattended \
--project-file=session.burp \
--config-file=burp_project_config.json \
--user-config-file=user_config.json
5. Offensive Pipeline Methodology Checklist
Follow this systematic checklist when building continuous lab scanning and staging test rigs:
[+] Phase 1: Environment & Config Pre-Flight
├── Ingest dedicated staging scope patterns (prevent off-scope routing).
├── Verify Java Heap Allocation (-Xmx4g minimum for heavy extension pools).
└── Pre-seed TLS CA certificates into downstream HTTP clients / Playwright instances.
[+] Phase 2: Daemon Initialization & Listener Validation
├── Launch runtime in headless mode (-Djava.awt.headless=true --unattended).
├── Poll socket availability on 127.0.0.1:8080 prior to test dispatch.
└── Confirm Jython runtime and Montoya extensions are initialized.
[+] Phase 3: Traffic Generation & Passive Auditing
├── Route automated unit/integration test suites through the proxy.
├── Inject targeted authentication cookies & headers for role-based testing.
└── Monitor passive scanner queues for security header misses, reflection points, and IDOR patterns.
[+] Phase 4: Artifact Ingestion & CI Gatekeeping
├── Export project databases or extract structured XML/JSON issue reports.
├── Assert zero High/Critical severity findings before allowing staging deployment.
└── Cleanly signal process termination to persist SQLite/Burp project integrity.
Key Takeaways
- Deterministic Execution: Using version-controlled JSON configuration files guarantees that staging scans run with identical scope, headers, and extensions across all pipeline runners.
- Resource Optimization: Running headless without GUI overhead saves memory and CPU cycles, allowing test runs to complete significantly faster in containerized CI environments.
- Continuous Verification: Integrating custom Montoya or Python extensions ensures company-specific security invariants and authorization checks are verified on every commit.
