Inception: The Web Scanner That Deliberately Slows Down to Evade WAF Detection
Hook
Most web scanners get faster by hammering individual targets. Inception gets sneakier by deliberately spreading its tests thin across hundreds of domains before circling back.
Context
Bug bounty hunters and security researchers face a perpetual cat-and-mouse game with Web Application Firewalls. Traditional vulnerability scanners like OWASP ZAP or Burp Suite are designed for deep, focused analysis of single applications—they send dozens or hundreds of requests to one target in rapid succession. This pattern is trivial for WAFs to detect and block. The result? Your scanner gets banned after the first few tests, and you've learned nothing about 99% of your target list.
Inception takes a radically different approach born from the realities of scanning at scale. When you're checking 500 domains for exposed Git repositories or misconfigured CORS headers, you don't need the sophistication of a full vulnerability scanner. You need speed, stealth, and the ability to define new checks without recompiling code. Written in Go and designed around WAF evasion, Inception sequences its test cases horizontally across all targets rather than vertically through one target at a time. This creates a request pattern that looks like organic traffic distribution rather than an automated attack, keeping your scanner alive long enough to actually find vulnerabilities.
Technical Insight
Inception's architecture revolves around two core components: a JSON-based signature system and a Go-based concurrent execution engine that deliberately sequences tests to avoid detection patterns. The signature system is where Inception truly shines for practitioners. Rather than hardcoding vulnerability checks, the tool reads test definitions from a provider.json file that specifies HTTP methods, paths, headers, body content, and matching conditions.
Here's what a typical signature looks like:
{
"name": "Exposed .env file",
"method": "GET",
"path": "/.env",
"headers": {},
"body": "",
"match": {
"type": "string",
"value": "DB_PASSWORD"
}
}
The scanner reads this signature, constructs an HTTP request to https://target.com/.env, and checks if the response contains DB_PASSWORD. Simple, but the power comes from the placeholder system. You can use $hostname, $domain, and $fqdn variables that Inception dynamically substitutes based on each target URL. This becomes critical for testing CORS misconfigurations:
{
"name": "CORS reflection vulnerability",
"method": "GET",
"path": "/api/user",
"headers": {
"Origin": "https://evil.$domain"
},
"body": "",
"match": {
"type": "string",
"value": "Access-Control-Allow-Origin: https://evil.$domain"
}
}
When Inception scans example.com, it automatically tests with Origin: https://evil.example.com, making the check target-specific without manual configuration. This dynamic payload generation is surprisingly rare in scanner frameworks and eliminates an entire class of false negatives.
The execution model is where WAF evasion happens. Inception loads all signatures and all target domains into memory, then uses goroutines to create a worker pool (default 200 concurrent workers). But here's the critical design decision: it iterates through signatures in the outer loop and domains in the inner loop. When you run Inception against 100 domains with 20 signatures, it sends signature #1 to all 100 domains before moving to signature #2. From a WAF's perspective, it sees one request for /.env, then several seconds or minutes later, one request for /.git/config. There's no rapid-fire pattern to trigger rate limiting.
The concurrency implementation is straightforward Go:
var wg sync.WaitGroup
jobs := make(chan Job, len(domains)*len(signatures))
for i := 0; i < threads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
makeRequest(job)
}
}()
}
for _, signature := range signatures {
for _, domain := range domains {
jobs <- Job{Domain: domain, Sig: signature}
}
}
close(jobs)
wg.Wait()
This pattern maintains high throughput across the entire scan (200 concurrent requests) while keeping the per-domain request rate low. It's the web scanning equivalent of spreading your bets across the table rather than putting everything on one number.
Inception ships with signatures derived from Snallygaster, the Python-based scanner from J.M. Becker that gained attention for finding exposed credentials at major organizations. The included checks cover common wins: exposed .git directories, .env files, phpinfo() pages, Apache server-status endpoints, and various configuration files that developers forget about. The web-based provider generator (providerCreate.html) lets you build new signatures through a GUI without touching JSON, though experienced users will likely edit the JSON directly for speed.
Matching logic supports three modes: string matching (substring present in response body), status code matching (exact HTTP status), and content-length matching (exact byte count). You can combine these with basic AND logic. What's missing is regex support beyond simple substring matching, conditional logic (if X then check Y), and response time analysis. This keeps the codebase simple but limits detection of behavioral vulnerabilities.
Gotcha
The repository's GitHub classification as an HTML project reveals the first limitation—despite being written in Go, the repository structure or file composition triggers GitHub's language detection incorrectly. This suggests potential issues with how the project is packaged or organized. In practice, after installing Inception via go install, you need to manually specify the provider.json path rather than the tool finding it automatically. This incomplete installation experience means you're on your own for setting up working directories and managing signature files.
The matching system's simplicity becomes a hard wall when you need anything beyond basic detection. Testing for SQL injection requires response time analysis or error message parsing with conditional logic—Inception can't do either. You're limited to "does this string exist?" and "what's the status code?" checks. While this covers a surprising amount of misconfiguration hunting, it means Inception is fundamentally a reconnaissance tool, not a comprehensive vulnerability scanner. If a vulnerability requires multi-step exploitation, request chaining, or analysis of JavaScript execution, you're looking at the wrong tool. The WAF-evasion design also means slower overall scan times compared to aggressive scanners—spreading 1000 requests across 100 domains takes longer than hammering one target with 1000 requests, even with high concurrency.
Verdict
Use if: You're scanning large lists of domains for specific known misconfigurations, conducting bug bounty reconnaissance where WAF evasion matters more than speed, or you need security researchers without Go expertise to create custom vulnerability signatures through a GUI. Inception excels at answering "which of these 500 subdomains has an exposed .git directory?" without getting blocked. Skip if: You need comprehensive vulnerability scanning with exploitation capabilities (use Burp Suite or Nuclei instead), require complex conditional logic in your detection rules, want sophisticated response analysis beyond string matching, or expect a polished installation experience with automatic configuration. Also skip if you're scanning a single target in-depth—the WAF-evasion design actually slows you down when stealth isn't a requirement.