A modular, extensible security testing framework integrated with Burp Suite via the Montoya API.
Philosophy: Every technique you learn becomes a module. Every module is a unit-tested, repeatable experiment. Knowledge is stored as code.
src/main/kotlin/dev/recon/framework/
├── core/
│ ├── model/ # HttpRequest, HttpResponse, Finding — stable forever
│ ├── module/ # TestingModule interface — what every module implements
│ ├── engine/ # Engine — orchestrates module execution
│ └── context/ # FrameworkContext — shared services (transport, logger, analyzers)
├── transport/
│ ├── HttpTransport # Interface — decouples modules from Burp
│ ├── BurpTransport # Montoya API implementation
│ └── MockTransport # For unit tests — no Burp required
├── analysis/
│ └── analyzers/
│ ├── StatusCodeAnalyzer # 403→200 WAF bypass detection
│ ├── BodyLengthAnalyzer # Response length differences
│ ├── ContentAnalyzer # Keyword/pattern changes (WAF blocks, errors)
│ └── TimingAnalyzer # Blind injection via response delay
├── mutation/
│ ├── MutationStrategy # Interface — one atomic mutation operation
│ └── strategies/
│ ├── DuplicateHeaderStrategy # Parser discrepancy exploitation
│ ├── EncodingStrategy # URL encode, double encode, path variants
│ └── ConflictingHeaderStrategy # X-Forwarded-For, X-Original-URL, etc.
├── modules/
│ └── waf/
│ └── WafBypassModule # First module — combines all three strategies
├── logging/
│ └── FrameworkLogger # Structured logs (debug.log) + findings (findings.jsonl)
└── burp/
└── FrameworkExtension # Thin Burp adapter — entry point, context menu
# Compile and run tests
./gradlew test
# Build the Burp extension JAR
./gradlew shadowJar
# Output: build/libs/recon-framework-0.1.0.jarRequires: Java 21. No system Gradle needed — the wrapper handles it.
- Open Burp Suite → Extensions tab → Installed → Add
- Extension type: Java
- Select:
build/libs/recon-framework-0.1.0.jar - Check Extensions → Output tab — you should see:
✅ Recon Framework initialized with 1 module(s)
- Intercept or find any request in Proxy History, Repeater, or Target site map
- Right-click → Send to Recon Framework
- Watch the Extensions → Output tab for results
- Structured logs are saved to:
~/.recon-framework/logs/
| File | Contents |
|---|---|
debug_<session>.log |
Execution trace — module steps, timing, errors |
findings_<session>.jsonl |
Structured findings — one JSON object per line |
requests_<session>/ |
Every request/response pair sent by the framework |
Query findings with jq:
# All HIGH severity findings
jq 'select(.severity == "HIGH")' ~/.recon-framework/logs/findings_*.jsonl
# All WAF bypass findings
jq 'select(.module == "WAF Bypass")' ~/.recon-framework/logs/findings_*.jsonl
# Top confidence findings
jq 'select(.confidence > 0.8) | {title, confidence, technique}' ~/.recon-framework/logs/findings_*.jsonlThis is the core workflow. Every new technique = a new file.
// src/main/kotlin/dev/recon/framework/modules/sqli/BlindSqliModule.kt
package dev.recon.framework.modules.sqli
import dev.recon.framework.core.context.FrameworkContext
import dev.recon.framework.core.model.*
import dev.recon.framework.core.module.TestingModule
class BlindSqliModule : TestingModule {
override val id = "blind-sqli"
override val name = "Blind SQL Injection"
override val description = "Detects blind SQLi via timing (SLEEP/WAITFOR) and boolean-based responses"
override val category = "injection"
override fun isApplicable(request: HttpRequest): Boolean =
request.method == "POST" || request.path.contains("?")
override fun run(request: HttpRequest, context: FrameworkContext): List<Finding> {
// 1. Get baseline
val baseline = context.transport.send(request)
// 2. Generate mutations (your payloads here)
// 3. Send and analyze
// 4. Return findings
return emptyList() // Implement your logic
}
}val modules = listOf(
WafBypassModule(),
BlindSqliModule() // ← add here
)// src/test/kotlin/.../BlindSqliModuleTest.kt
class BlindSqliModuleTest {
@Test
fun `detects timing-based injection`() {
val transport = MockTransport { req ->
if (req.body?.contains("SLEEP") == true)
HttpResponse(statusCode = 200, responseTimeMs = 5000L)
else
HttpResponse(statusCode = 200, responseTimeMs = 100L)
}
// ... assert findings
}
}// src/main/kotlin/dev/recon/framework/mutation/strategies/CasingStrategy.kt
class CasingStrategy : MutationStrategy {
override val name = "Casing"
override val description = "Alternates case in path to bypass case-sensitive WAF rules"
override fun mutate(request: HttpRequest): List<MutatedRequest> {
val upper = request.copy(path = request.path.uppercase())
return listOf(
MutatedRequest(upper, name, "Uppercased path: ${request.path} → ${upper.path}")
)
}
}Then add it to any module's strategy list — zero other changes needed.
| Decision | Reason |
|---|---|
| Kotlin over Java | data class + copy() = immutable mutations without boilerplate |
| Montoya API over legacy | Legacy is unmaintained; Montoya is the present and future |
| Immutable request model | Original request never changes — safe for parallel mutation branches |
MockTransport in main source |
Tests must be runnable without Burp — this enables that |
FrameworkContext container |
New shared services don't require module signature changes |
| JSON Lines for findings | Parseable by jq, Python, and any future tooling |
| Confidence scoring (0–1) | Security testing is probabilistic — binary yes/no is rarely accurate |
-
TimingStrategy— add SLEEP/WAITFOR payloads to request body parameters -
ParserConfusionModule— HTTP request smuggling detection -
AccessControlModule— IDOR and privilege escalation testing - Rate limiter in Engine — configurable delay between mutations
- Burp UI panel — findings table displayed inside Burp
- Multi-baseline calibration — send baseline N times to average out jitter
- Module tags/categories — selectively run subsets of modules