SALA7 Bypass

Burp Suite Extension for 403/401/429 Bypass Testing

Featured image

Sala7-Bypass

📑 Table of Contents - Sala7-Bypass Extension Blog


1. Introduction & Motivation


2. Architecture & Design Philosophy


3. Core Features Deep Dive


4. Advanced Engine Components


5. User Interface & Experience


6. Filtering & Scope Management


7. Notification & Alert System


8. Data Export & Persistence


9. Configuration & Customization


10. Testing & Validation


11. Technical Implementation Details


12. Security Considerations


13. Future Enhancements & Roadmap


14. Conclusion


Appendices

1. Introduction & Motivation


1.1 The Problem: Why 403/401/429 Bypass Matters

In modern web application security testing, access control mechanisms represent one of the most critical—and frequently misimplemented—layers of defense. The HTTP status codes 403 (Forbidden), 401 (Unauthorized), and 429 (Too Many Requests) serve as the primary gatekeepers separating authenticated, authorized, and rate-limited users from protected resources. Yet these very mechanisms, when improperly configured or architecturally flawed, become the weakest links in an otherwise robust security posture.

The significance of bypassing these controls extends far beyond academic curiosity. In penetration testing and red team operations, the ability to circumvent 403/401/429 responses directly translates to expanded attack surface, privilege escalation pathways, and ultimately, the difference between a “secure” assessment and one that reveals critical vulnerabilities. Consider a typical enterprise scenario: an API endpoint /api/v1/admin/users returns 403 for non-administrative accounts. A tester armed with effective bypass techniques discovers that appending X-Forwarded-For: 127.0.0.1 to the request—exploiting the application’s trust in proxy-forwarded headers—grants full administrative access. The impact is immediate and severe: unauthorized user enumeration, privilege modification, or complete system compromise.

The prevalence of these vulnerabilities stems from fundamental architectural misconceptions. Developers frequently conflate authentication (proving identity) with authorization (granting permissions), implementing the former while neglecting the latter. Rate limiting, intended to prevent brute-force attacks and resource exhaustion, often relies on client-controlled identifiers such as IP addresses—identifiers trivially spoofed through header manipulation. The OWASP Top 10 consistently ranks Broken Access Control (A01:2021) as the most critical web application security risk, with 94% of tested applications exhibiting some form of access control flaw. Within this category, bypass techniques targeting 403/401/429 responses constitute a disproportionately high percentage of exploitable findings.

The technical complexity of modern web architectures exacerbates this problem. Microservices architectures, API gateways, reverse proxies, and Content Delivery Networks (CDNs) introduce multiple layers of header processing, each capable of modifying, stripping, or misinterpreting HTTP headers. A request passing through Cloudflare → Nginx → Kubernetes Ingress → Application Server may have its X-Forwarded-For header rewritten three times, with each layer applying different trust policies. The application developer, unaware of upstream transformations, implements IP-based restrictions using request.getRemoteAddr()—which in containerized environments returns the internal cluster IP rather than the actual client address. The result: a 403 response that crumbles under header injection.

Furthermore, the rise of API-first architectures has normalized stateless authentication via Bearer tokens, API keys, and JWTs. While these mechanisms offer scalability advantages, they frequently lack the contextual awareness of traditional session-based authentication. A 401 response from an API endpoint may indicate a missing token, but the same endpoint might process requests lacking both Authorization and Cookie headers through a legacy fallback mechanism—a behavior pattern that systematic auth removal testing can reveal.

The 429 status code introduces additional complexity. Rate limiting implementations vary dramatically: some track per-IP request counts, others per-user or per-API-key, and many employ distributed Redis-backed counters with sliding window algorithms. Each implementation presents distinct bypass opportunities. Per-IP rate limiting falls to IP spoofing; per-user limiting may yield to credential stuffing combined with header rotation; distributed counters occasionally exhibit race conditions exploitable through precisely timed concurrent requests. The security implications extend beyond access: a successful 429 bypass enables unrestricted brute-force attacks against authentication endpoints, effectively neutralizing a primary defense mechanism.

From a defensive perspective, understanding these bypass techniques is equally critical. Security engineers implementing access controls must anticipate header injection, method tampering, and authentication edge cases. The Sala7-Bypass extension bridges this gap by providing both offensive testing capabilities and transparent visibility into exactly which bypass vectors succeed against a given target—information directly translatable into hardened configurations.


1.2 Real-World Impact of Access Control Bypass

The theoretical implications of access control bypass materialize regularly in disclosed vulnerabilities, breach reports, and real-world penetration testing engagements. Examining documented cases reveals consistent patterns that Sala7-Bypass addresses systematically.

Case Study 1: The Capital One Breach (2019)

While primarily attributed to Server-Side Request Forgery (SSRF), the Capital One breach exemplifies how access control assumptions crumble in cloud environments. The attacker exploited AWS metadata service access, but the initial foothold involved bypassing WAF rules through carefully crafted requests. The incident exposed 100 million customer records and resulted in $190 million in settlement costs. Post-incident analysis revealed that rate limiting rules, had they been properly enforced and bypass-resistant, could have significantly impeded the data exfiltration phase.

Case Study 2: Uber API Authentication Bypass (2022)

Uber’s bug bounty program disclosed a critical vulnerability where API endpoints intended for internal use were accessible externally through X-Uber-Internal header manipulation. The endpoint, returning 401 without the header, granted full administrative access when the header was present with arbitrary values. This represents a textbook authentication bypass: the application checked for header presence rather than header validity—a flaw detectable through systematic header injection and auth removal testing. The vulnerability exposed sensitive driver and rider data across millions of records.

Case Study 3: GitHub Enterprise Path Bypass (2021)

GitHub Enterprise Server contained a vulnerability where access control decisions were based on the request path rather than the routed path. By sending requests to /api/v3 with X-Original-URL: /admin headers, attackers bypassed path-based restrictions and accessed administrative functionality. This path normalization discrepancy—where the proxy routed based on one path while the application authorized based on another—is precisely the scenario Sala7-Bypass’s X-Original-URL and X-Rewrite-URL injection targets.

Case Study 4: Rate Limiting Bypass in Financial APIs

A major European bank’s mobile API implemented rate limiting based solely on X-Forwarded-For headers—headers entirely client-controllable in requests originating from the mobile application. Penetration testers discovered that rotating through the IP range 127.0.0.1 through 127.0.0.255 in successive requests completely circumvented rate limiting, enabling unlimited authentication attempts. The bypass, achievable through Sala7-Bypass’s IP rotation capabilities, exposed customer accounts to brute-force attacks.

Industry Statistics and Trends

Beyond individual cases, industry data underscores the prevalence and impact:

Business Impact Quantification

The business impact of access control bypasses extends across multiple dimensions:

Table

Impact Category Description Quantification
Data Breach Unauthorized data access Average cost: $4.45M (IBM, 2023)
Regulatory Penalty GDPR, CCPA, HIPAA violations Up to 4% global revenue (GDPR)
Reputational Damage Customer trust erosion 65% of consumers lose trust post-breach
Operational Disruption Service degradation Average 23 hours downtime per incident
Competitive Advantage Loss Intellectual property exposure Unquantifiable but frequently severe

The Testing Gap

Despite this prevalence, systematic testing for access control bypasses remains inconsistently implemented. Manual testing—sending individual requests with modified headers in Burp Repeater—is time-consuming, error-prone, and fails to achieve comprehensive coverage. A typical application may have 200+ endpoints, each potentially vulnerable to 40+ header injection vectors, 2 method switching variants, and 3 authentication removal combinations. Manual testing of 200 × 45 × 3 = 27,000 request variants is impractical. Existing automated tools, as examined in the following section, address subsets of this problem space but fail to provide the integrated, configurable, and transparent solution that comprehensive testing demands.


1.3 Existing Tools & Their Limitations

The security testing ecosystem offers several tools addressing access control and bypass testing, yet each exhibits limitations that motivated Sala7-Bypass’s development. Understanding these limitations requires examining both Burp Suite extensions and standalone tools across functional categories.

Category 1: Header Injection Tools

AutoRepeater (Burp Extension)

AutoRepeater automates request modification and replay, supporting header injection through regex-based replacement rules. Its primary limitation lies in configuration complexity: defining rules for 40+ IP spoofing headers requires extensive manual setup. Furthermore, AutoRepeater lacks intelligent response analysis—it replays requests without automatically detecting 403→200 transitions, requiring manual inspection of each response. The extension also lacks built-in IP pool management, rate limiting awareness, and the granular bypass classification (METHOD/HEADER/AUTH/RATE_LIMIT/COMBINED) that Sala7-Bypass provides.

HackBar (Firefox/Chrome Extension)

Hackbar provides manual header injection capabilities through a browser toolbar interface. While useful for quick manual tests, it operates outside the Burp Suite ecosystem, preventing integration with Burp’s proxy history, repeater state, and scanner workflows. It lacks automation entirely, supports only a predefined header set (approximately 12 headers versus Sala7-Bypass’s 40+), and provides no response analysis or success tracking.

Custom Python Scripts

Numerous open-source scripts automate specific bypass vectors—403bypass.py, bypass-403, and similar tools enumerate headers against 403 endpoints. These scripts demonstrate the concept but fail as production testing tools: they lack proxy integration, require command-line execution disrupting testing workflows, provide no real-time feedback, and typically support only single-technique testing (headers only, or method switching only) rather than combined vectors.

Category 2: Method Switching Tools

Burp Suite Built-in “Change Request Method”

Burp Repeater’s right-click “Change Request Method” feature converts GET to POST and vice versa, migrating parameters appropriately. However, this is a manual, per-request operation. Testing an API with 50 endpoints requires 50 manual conversions, with each conversion losing the original request context. There is no automated detection of which endpoints exhibit method-based access control discrepancies, no batch processing, and no integration with response status monitoring.

Postman/Newman

API testing tools support method switching through collection modifications, but operate outside the web application testing workflow. They lack proxy integration, cannot intercept browser-generated requests, and require manual endpoint enumeration—impractical for black-box testing of undocumented APIs.

Category 3: Authentication Testing Tools

AuthMatrix (Burp Extension)

AuthMatrix provides role-based access control testing by comparing responses across different authentication contexts. While powerful for authorization testing, it focuses on horizontal/vertical privilege escalation rather than authentication bypass. It does not test for missing authentication scenarios (removing all auth headers), does not integrate with bypass technique enumeration, and requires explicit role configuration that may not be available in black-box assessments.

Autorize (Burp Extension)

Autorize automatically repeats requests with modified cookies to detect authorization flaws. Its limitation is cookie-centric focus: it tests authorization by substituting cookies between users, but does not test complete authentication removal, header-based bypass, or method switching. It also lacks the real-time statistics, color-coded logging, and bypass classification that Sala7-Bypass provides.

Category 4: Rate Limiting Testing

Wfuzz/FFUF

These fuzzing tools support rate limiting bypass through proxy rotation and delay configuration. However, they are general-purpose fuzzers rather than specialized bypass tools. Configuring them for rate limiting testing requires manual setup of proxy lists, header dictionaries, and response parsing rules. They lack integration with Burp Suite, do not automatically detect 429→200 transitions, and provide no visual feedback on bypass success rates.

Category 5: Comprehensive Solutions

Burp Suite Professional Scanner

Burp’s active scanner includes access control checks, but these are black-box heuristic-based tests rather than systematic bypass enumeration. The scanner may detect some header injection vulnerabilities but lacks the configurable, extensible bypass header pool that Sala7-Bypass provides. It does not support custom header definitions, does not classify bypass types, and provides no real-time bypass statistics during testing.

OWASP ZAP

ZAP’s access control testing features include forced browsing and authorization detection. While comprehensive for authorization testing, ZAP lacks the specialized 403/429 bypass focus of Sala7-Bypass. Its header manipulation capabilities are limited, it lacks method switching automation, and its reporting does not provide per-technique success rates.

Comparative Limitations Summary

Table

Capability AutoRepeater Autorize Custom Scripts Burp Scanner Sala7-Bypass
40+ Header Pool ❌ Manual config ❌ N/A ⚠️ Limited ❌ Fixed set ✅ Built-in
Method Switching ⚠️ Manual ⚠️ Separate tool ✅ Integrated
Auth Removal ⚠️ Cookie only ⚠️ Partial ✅ Full
Rate Limit Bypass ⚠️ Manual ✅ Automated
Combined Vectors ✅ Intelligent
Real-time Stats ⚠️ Basic ⚠️ Post-scan ✅ Live dashboard
Bypass Classification ✅ 5 types
Burp Integration ✅ Native
Color-coded UI ⚠️ Basic ✅ Comprehensive
Sound/Notification ✅ Configurable

The consistent pattern across existing tools is specialization without integration. Each addresses a subset of bypass techniques, but none provides the unified, configurable, and observable platform that comprehensive access control testing demands. Sala7-Bypass fills this gap by combining all major bypass vectors—IP spoofing, method switching, authentication removal, path manipulation, and rate limiting circumvention—within a single Burp Suite extension, augmented by real-time statistics, visual feedback, and intelligent success detection.


1.4 Introducing Sala7-Bypass: A Comprehensive Solution

Sala7-Bypass emerges from the identified gap: a Burp Suite Professional extension designed specifically for systematic, comprehensive, and observable 403/401/429 bypass testing. Built atop PortSwigger’s Montoya API—the modern extension interface replacing the legacy Extender API—Sala7-Bypass integrates natively into Burp Suite’s proxy, repeater, and intruder workflows while providing capabilities that no existing tool offers in unified form.

Design Philosophy

Sala7-Bypass’s architecture rests on three foundational principles:

  1. Comprehensive Coverage: The extension must test all major bypass vectors—IP spoofing (40+ headers), method switching (GET↔POST with parameter migration), authentication removal (Cookie/Authorization), path manipulation (X-Original-URL/X-Rewrite-URL), and rate limiting bypass (IP rotation)—within a single interface. No manual tool switching, no script execution, no workflow disruption.
  2. Intelligent Automation: Beyond mere request replay, the extension must automatically detect successful bypasses by comparing original and modified response statuses, classify bypass types, track per-technique success rates, and adapt behavior based on configuration (auto-stop on success, retry limits, request delays).
  3. Transparent Observability: Every request, every modification, every response, and every bypass decision must be visible, logged, color-coded, and exportable. The tester must understand not just that a bypass succeeded, but how, which technique, which headers, and with what success rate—information directly translatable into vulnerability reports and remediation guidance.

Core Capabilities Overview

Table

Capability Implementation Technical Detail
IP Spoofing 40+ header pool X-Forwarded-For, X-Real-IP, CF-Connecting-IP, etc. with IPv4/IPv6/Hex/Decimal encodings
Method Switching RequestModifier class GET↔POST with automatic parameter migration (URL params→body, body→query string)
Auth Removal Selective header stripping Cookie and/or Authorization removal with independent toggles
Path Manipulation X-Original-URL injection Bypass path-based restrictions through header-based path override
Rate Limit Bypass IP rotation + request pacing Distributed requests with configurable delays and concurrent threads
Combined Vectors Intelligent orchestration Automatic application of multiple techniques based on configuration
Real-time Logging 7-column table model Time, URL, Status, Method, Action, Bypass Type, Response Time
Statistics Dashboard Live calculation Total, Blocked, Bypass Success, Rate %, per-technique breakdown
Visual Feedback Color-coded renderers Status codes (green/orange/red), results (success/fail/blocked), bypass types (badges)
Export CSV/JSON Full request/response metadata with bypass classification
Notifications System tray + sound Configurable WAV file playback, popup dialogs, tray notifications

Architecture Overview

Sala7-Bypass implements a modular architecture separating concerns across six primary components:

Integration Points

Sala7-Bypass integrates with Burp Suite through multiple Montoya API touchpoints:

Table

Integration API Method Purpose
HTTP Handler registerHttpHandler() Intercept all HTTP requests/responses
Proxy Handler registerRequestHandler() Intercept proxy-specific traffic
Suite Tab registerSuiteTab() Embed UI in Burp’s tabbed interface
Repeater repeater().sendToRepeater() Right-click “Send to Repeater”
Intruder intruder().sendToIntruder() Right-click “Send to Intruder”
Logging logging().logToOutput() Extension console output

Target Use Cases

Sala7-Bypass serves multiple testing scenarios:

Development Context

Sala7-Bypass was developed by H3X0S3 (Mahmoud Salah) as a response to repetitive manual bypass testing encountered during professional engagements. The extension name “Sala7” derives from the developer’s handle, with the tool embodying the philosophy that effective security testing requires automation that is simultaneously comprehensive, intelligent, and transparent. The codebase, open-sourced under the H3X0S3/Sala7-Bypass repository, invites community contribution while maintaining professional-grade implementation standards.

The subsequent sections of this blog post dissect each architectural component, implementation detail, and feature capability, providing the technical depth necessary for both users seeking to maximize the extension’s effectiveness and developers aiming to understand or extend its implementation.


2. Architecture & Design Philosophy


2.1 Extension Architecture Overview

Sala7-Bypass adopts a layered architecture that mirrors the request-response lifecycle of HTTP traffic while maintaining strict separation between interception logic, transformation engines, state management, and user interface components. This architectural decision stems from the recognition that Burp Suite extensions operate within a constrained runtime environment—the JVM instance hosting Burp Professional—where memory leaks, thread starvation, and UI responsiveness degradation can compromise both the extension and the host application.

The architecture consists of four primary layers, each with well-defined responsibilities and interfaces:

Layer 1: Interception & Coordination (BurpExtender)

The BurpExtender class serves as the sole entry point for Burp Suite integration, implementing three critical interfaces from the Montoya API:

public class BurpExtender implements BurpExtension, HttpHandler, ProxyRequestHandler

The BurpExtension interface mandates the initialize(MontoyaApi api) method—the extension’s bootstrap sequence. This method performs dependency injection of the Montoya API instance, instantiates all sub-components, and registers handlers with Burp’s internal dispatcher. The sequence is deterministic and failure-critical: any exception during initialization terminates extension loading without recovery.

The HttpHandler interface provides handleHttpRequestToBeSent() and handleHttpResponseReceived()—the twin interception points for all HTTP traffic processed by Burp’s HTTP stack. This includes traffic generated by Burp’s own tools (Scanner, Repeater, Intruder) in addition to proxied browser traffic, ensuring comprehensive coverage regardless of request origin.

The ProxyRequestHandler interface provides handleRequestReceived() and handleRequestToBeSent()—interception points specific to Burp’s proxy listener. While Sala7-Bypass currently implements passthrough behavior for proxy handlers (ProxyRequestReceivedAction.continueWith()), this dual registration ensures future extensibility for proxy-specific manipulations (e.g., SSL stripping detection, upstream proxy bypass).

Layer 2: State & Logic Engine (BypassEngine)

The BypassEngine encapsulates all stateful decision logic, isolating mutable state from the interception layer. This isolation is architecturally critical: BurpExtender handles hundreds of concurrent requests per second, while BypassEngine performs relatively expensive hash computations, database lookups (in-memory maps), and retry bookkeeping. Separating these concerns prevents request processing latency from coupling to state management complexity.

public class BypassEngine {
    private final Set<String> processedRequests;      // Deduplication
    private final Map<String, Integer> retryCount;    // Per-request retry tracking
    private final Map<String, Boolean> successMap;    // Success memoization
}

All collections utilize ConcurrentHashMap and ConcurrentHashMap.newKeySet() implementations, eliminating explicit synchronization while maintaining thread safety under Burp’s multi-threaded request dispatch model.

Layer 3: Transformation Primitives (RequestModifier)

The RequestModifier class implements pure functions—stateless transformations of HttpRequest objects. This functional design enables predictable behavior, unit testability, and composability: complex transformations (method switch + header removal + header addition) are constructed by chaining simple primitives.

public final class RequestModifier {
    private RequestModifier() {} // Utility class - no instantiation
    
    public static HttpRequest switchMethod(HttpRequest request) { ... }
    public static HttpRequest removeHeader(HttpRequest request, String headerName) { ... }
}

The final class declaration and private constructor enforce the utility pattern, preventing accidental stateful subclassing that could introduce side effects into the transformation pipeline.

Layer 4: Presentation & Interaction (BypassExtensionUI)

The UI layer implements the Model-View-Controller pattern with Swing components, maintaining strict separation between data models (LogTableModel), view renderers (StatusCellRenderer, ResultCellRenderer, BypassTypeCellRenderer), and controller actions (button listeners, menu handlers). All UI updates execute on the Event Dispatch Thread (EDT) through SwingUtilities.invokeLater(), preventing deadlock and ensuring responsive interaction during high-volume request processing.

Data Flow Architecture

The inter-layer communication follows a unidirectional data flow pattern:

[Browser/Tool] → [Burp HTTP Stack] → [BurpExtender.handleHttpRequestToBeSent()]
                                                    ↓
                                        [RequestModifier] (transformations)
                                        [BypassEngine] (state checks)
                                                    ↓
                                        [LogEntry] (immutable data capture)
                                                    ↓
                                        [Burp Server] → [Response]
                                                    ↓
                                        [BurpExtender.handleHttpResponseReceived()]
                                                    ↓
                                        [BypassEngine] (success evaluation)
                                        [LogEntry] (status updates)
                                                    ↓
                                        [LogTableModel] (data binding)
                                        [BypassExtensionUI] (rendering)

This unidirectional flow—request transformations proceed downstream, response processing proceeds upstream, and UI updates are asynchronous side effects—prevents circular dependencies and simplifies reasoning about state changes.

Error Handling Strategy

Each layer implements distinct error handling appropriate to its context:


2.2 Integration with Burp Suite Montoya API

The Montoya API represents PortSwigger’s modernized extension interface, replacing the legacy IBurpExtender/IBurpExtenderCallbacks interfaces that dominated Burp extension development for over a decade. Sala7-Bypass’s adoption of Montoya is deliberate and comprehensive, leveraging API capabilities that simplify implementation while enhancing robustness.

API Evolution: Legacy vs. Montoya

Table

Aspect Legacy API (pre-2022) Montoya API (Sala7-Bypass)
Registration IBurpExtender.registerExtenderCallbacks() BurpExtension.initialize(MontoyaApi)
HTTP Handling IHttpRequestResponse mutable objects HttpRequest/HttpResponse immutable objects
Request Modification Manual byte array manipulation Fluent builder methods (withMethod(), withAddedHeader())
UI Registration IBurpExtenderCallbacks.addSuiteTab() MontoyaApi.userInterface().registerSuiteTab()
Tool Integration IBurpExtenderCallbacks.sendToRepeater() MontoyaApi.repeater().sendToRepeater()
Thread Safety Manual synchronization required Immutable objects + concurrent collections

The immutable object pattern is particularly transformative. In the legacy API, modifying a request’s headers required byte-parsing the HTTP message, manipulating the resulting array, and reconstructing the object—a process fraught with encoding errors, header ordering issues, and race conditions. Montoya’s HttpRequest provides fluent methods returning new instances:

// Legacy API (error-prone, mutable)
byte[] requestBytes = request.getRequest();
// ... manual byte manipulation ...
request.setRequest(modifiedBytes);

// Montoya API (type-safe, immutable)
HttpRequest modified = request
    .withMethod("POST")
    .withAddedHeader("X-Forwarded-For", "127.0.0.1")
    .withRemovedHeader("Cookie");

This immutability eliminates an entire class of concurrency bugs: multiple threads (Burp’s HTTP dispatcher, bypass attempt threads, UI update threads) can safely hold references to the same HttpRequest instance without risk of inconsistent state.

Montoya API Utilization in Sala7-Bypass

The extension exercises six major Montoya API domains:

1. Core API (MontoyaApi)

The api instance provides access to all other API domains. Sala7-Bypass stores this reference as a final field, ensuring all sub-components have consistent API access:

private MontoyaApi api; // Set in initialize(), accessed throughout

public MontoyaApi getApi() { return api; } // Exposed for UI callbacks

2. HTTP API (api.http())

The HTTP API provides request interception and manual request execution:

// Registration
api.http().registerHttpHandler(this);

// Interception
@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent request) { ... }

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived response) { ... }

// Manual execution (for bypass attempts)
var httpResponse = api.http().sendRequest(bypassRequest);

The sendRequest() method is critical for bypass attempts: it executes modified requests outside the interception pipeline, preventing recursive loop detection while capturing responses for analysis.

3. Proxy API (api.proxy())

Proxy-specific registration enables future extensibility:

api.proxy().registerRequestHandler(this);

Current implementation provides passthrough behavior, but the registration point exists for proxy-specific features (e.g., intercepting CONNECT tunnels, modifying proxy headers).

4. User Interface API (api.userInterface())

UI registration integrates the extension into Burp’s tabbed interface:

api.userInterface().registerSuiteTab("Sala7-Bypass", ui.getUIComponent());

The tab name “Sala7-Bypass” appears alongside Burp’s native tabs (Target, Proxy, Intruder, etc.), providing first-class UI integration.

5. Repeater/Intruder API (api.repeater(), api.intruder())

Tool integration enables right-click menu actions:

###

// From UI popup menu
extender.getApi().repeater().sendToRepeater(entry.getModifiedRequest());
extender.getApi().intruder().sendToIntruder(entry.getModifiedRequest());

These methods transfer requests—with all modifications intact—into Burp’s native tools, enabling manual verification and further manipulation of discovered bypass vectors.

6. Logging API (api.logging())

Structured logging provides debugging visibility:

api.logging().logToOutput("Sala7-Bypass Extension loaded! v" + CURRENT_VERSION);
api.logging().logToOutput("[BYPASS DETECTED] " + request.url() + " | Type: " + bypassType);

The logToOutput() method writes to Burp’s Extensions output panel, distinct from the proxy history or scanner logs, providing isolated debugging context.

API Version Compatibility

Sala7-Bypass targets Montoya API version 2023.10.2.4 or later, utilizing features stabilized in that release. The extension does not implement dynamic API version detection, as Montoya maintains backward compatibility within major versions. However, the CURRENT_VERSION constant and GitHub update check mechanism (checkForUpdates()) provide future-proofing against API deprecation by notifying users of extension updates.


2.3 Modular Design: Separation of Concerns

Sala7-Bypass’s six-class architecture enforces strict separation of concerns, with each class possessing a single, well-defined responsibility and minimal cross-cutting dependencies. This modularity enables independent testing, maintenance, and extension of components.

Component Responsibility Matrix

Table

Class Responsibility Dependencies Lines of Code (approx.)
BurpExtender Interception coordination, lifecycle management All other classes ~400
BypassEngine Request deduplication, retry tracking, success memoization MontoyaApi (logging only) ~80
RequestModifier Stateless request transformations None (pure utility) ~90
LogEntry Immutable data structure with mutable bypass tracking HttpRequest, HttpResponse ~150
LogTableModel Swing table data binding, statistics computation LogEntry ~120
BypassExtensionUI Swing UI construction, event handling, export BurpExtender, LogTableModel, LogEntry ~800

Dependency Direction

Dependencies flow unidirectionally from high-level coordination to low-level primitives:

plain Copy

BurpExtender  BypassExtensionUI  LogTableModel  LogEntry
                                     
BypassEngine  RequestModifier  (none)

No low-level class depends on high-level classes. LogEntry knows nothing about BurpExtender; RequestModifier has no reference to BypassExtensionUI. This acyclic dependency graph prevents circular coupling and enables independent component testing.

Interface Segregation

The architecture implicitly follows interface segregation principles through class specialization rather than explicit Java interfaces:

Configuration Encapsulation

Extension configuration is distributed across layers according to scope:

This distributed configuration reflects the architectural reality that BurpExtender owns the extension lifecycle, while BypassExtensionUI owns the presentation state.

Extensibility Points

The modular design provides several natural extension points:

  1. New Bypass Techniques: Adding a bypass vector (e.g., HTTP/2 specific headers) requires modifying only RequestModifier (new transformation method) and BurpExtender (integration into the bypass pipeline). No UI changes are required if the technique maps to existing bypass types.
  2. Custom Payload Sources: The BYPASS_HEADERS_POOL is currently a static list. Replacing it with a dynamic source (database, remote API) requires modifying only generateBypassHeaders() in BurpExtender, with BypassExtensionUI already supporting custom payload tables.
  3. Alternative UIs: The BypassExtensionUI implements a complete Swing interface. A headless mode (for CI/CD integration) could be implemented by creating a BypassExtensionHeadless class implementing the same configuration interface, with BurpExtender selecting the appropriate UI based on environment detection.
  4. Reporting Integration: Current CSV/JSON export is file-based. Adding direct Jira, Slack, or Burp Enterprise reporting integration requires modifying only BypassExtensionUI export methods, without touching interception logic.

2.4 Threading Model & Concurrency Handling

Sala7-Bypass operates within Burp Suite’s multi-threaded execution environment, where the HTTP handler, proxy handler, UI event dispatch, and extension-initiated threads execute concurrently. Proper concurrency management is not merely a performance optimization but a correctness requirement: race conditions in request matching, duplicate bypass attempts, or UI update corruption would produce false negatives, false positives, or crashes.

Thread Categories

Table

Thread Type Source Purpose Count
HTTP Dispatcher Burp Suite internal Request/response interception Multiple (Burp-managed)
Proxy Dispatcher Burp Suite internal Proxy-specific interception Multiple (Burp-managed)
Bypass Worker ExecutorService Async bypass attempts Configurable (default 5)
UI Update SwingUtilities.invokeLater() EDT-safe UI updates Single (Swing-managed)
Update Checker new Thread() Background version check 1 (transient)

Request Matching Concurrency

The most critical concurrency challenge is matching responses to their corresponding requests. Burp’s HTTP dispatcher calls handleHttpRequestToBeSent() and handleHttpResponseReceived() on potentially different threads, with no guarantee of sequential execution. Sala7-Bypass solves this through a two-map pending entry system:

// In BurpExtender
private final Map<String, LogEntry> pendingEntries = new ConcurrentHashMap<>();
private final Map<String, Integer> pendingEntryIndex = new ConcurrentHashMap<>();
private final Map<String, LogEntry> originalPendingEntries = new ConcurrentHashMap<>();
private final Map<String, Integer> originalPendingIndex = new ConcurrentHashMap<>();

The generatePendingKey() method creates deterministic keys incorporating URL, method, body hash, and time window:

private String generatePendingKey(HttpRequest request, String prefix) {
    long timeWindow = System.currentTimeMillis() / 1000;
    String bodyHash = request.bodyToString() != null 
        ? String.valueOf(request.bodyToString().hashCode()) 
        : "0";
    return prefix + "|" + request.url() + "|" + request.method() + "|" + bodyHash + "|" + timeWindow;
}

The ConcurrentHashMap implementation ensures atomic put() and remove() operations. When handleHttpResponseReceived() processes a response, it attempts removal with pendingEntries.remove(modKey)—an atomic operation returning the entry if present or null if already processed. This prevents double-processing even if two threads simultaneously handle responses for the same request (a rare but possible scenario with retry logic).

Bypass Worker Thread Pool

Bypass attempts execute asynchronously through a configurable ExecutorService:

private ExecutorService executorService;

// Initialization
this.executorService = Executors.newFixedThreadPool(threadCount);

// Dynamic reconfiguration
public void setThreadCount(int threadCount) {
    // Graceful shutdown with timeout
    executorService.shutdown();
    if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
        executorService.shutdownNow();
    }
    executorService = Executors.newFixedThreadPool(threadCount);
}

The thread pool isolates bypass attempts from Burp’s HTTP dispatcher threads, preventing slow bypass responses from blocking legitimate traffic processing. The maxRetries configuration (default 3) limits per-request bypass attempts, while the thread pool size (default 5) limits concurrent bypass operations across all requests.

Thread Safety in Data Structures

Table

Data Structure Thread Safety Mechanism Purpose
logEntries CopyOnWriteArrayList<<LogEntry> Append-only log storage; copy-on-write ensures iteration safety
pendingEntries ConcurrentHashMap<String, LogEntry> Request-response matching; atomic put/remove
bypassEngine.processedRequests ConcurrentHashMap.newKeySet() Deduplication; atomic add/contains
bypassEngine.retryCount ConcurrentHashMap<String, Integer> Retry tracking; atomic merge operations
bypassEngine.successMap ConcurrentHashMap<String, Boolean> Success memoization; atomic put/get

The CopyOnWriteArrayList for logEntries is a deliberate choice: write operations (adding new entries) are relatively infrequent compared to read operations (UI rendering, statistics computation, export). The copy-on-write pattern provides lock-free reads at the cost of O(n) writes—a favorable tradeoff given the read-heavy access pattern.

UI Thread Safety

All UI modifications execute on the Event Dispatch Thread (EDT):

// Pattern used throughout BypassExtensionUI
SwingUtilities.invokeLater(() -> {
    logTableModel.addEntry(entry);
    updateStats();
});

This pattern prevents ConcurrentModificationException during table rendering and ensures consistent visual state. Background threads (bypass workers, update checker) never directly manipulate Swing components.

Race Condition Prevention

Several specific race conditions are architecturally prevented:

  1. Duplicate Bypass Triggering: BypassEngine.shouldBypass() checks hasSuccess() and getRetryCount() atomically through ConcurrentHashMap operations. The markProcessed() call occurs before executorService.submit(), ensuring another thread cannot submit a duplicate bypass for the same request hash.
  2. Statistics Corruption: updateStats() iterates over logEntries (copy-on-write safe) and computes statistics into local variables before the SwingUtilities.invokeLater() lambda captures final copies. This prevents interleaving of statistics computation and UI update.
  3. Log Entry Mutation: LogEntry fields are mostly final, with mutable bypass tracking fields (bypassType, bypassHeadersUsed, bypassDetails) modified only by the HTTP dispatcher thread that processes the response. The CopyOnWriteArrayList ensures that UI threads reading entries see consistent (if potentially stale) state.

2.5 Memory Management & Performance Considerations

Operating as a long-running Burp Suite extension, Sala7-Bypass must manage memory responsibly to prevent heap exhaustion during extended testing sessions involving thousands of requests and responses. The architecture implements several memory-conscious design patterns.

Request/Response Storage

LogEntry stores complete HttpRequest and HttpResponse objects for both original and modified variants:

public class LogEntry {
    private final HttpRequest originalRequest;
    private final HttpRequest modifiedRequest;
    private HttpResponse originalResponse;
    private HttpResponse modifiedResponse;
}

These objects retain the full request/response byte content, which for large responses (file downloads, JSON arrays) can consume significant memory. The CopyOnWriteArrayList<<LogEntry> logEntries in BurpExtender thus represents the primary memory growth vector.

Memory Mitigation Strategies

Table

Strategy Implementation Tradeoff
No automatic pruning logEntries grows indefinitely Complete history for analysis; memory risk
Manual clear “Clear Logs” button calls logTableModel.clear() User-controlled; requires manual action
Export and clear CSV/JSON export preserves data externally Workflow interruption
Pending map cleanup remove() on response processing Automatic; prevents unbounded map growth

The current implementation prioritizes analysis completeness over automatic pruning, reflecting the penetration testing use case where complete request history is often necessary for report generation. Future enhancements could implement configurable automatic pruning (e.g., retain only last 1000 entries) or response body truncation for large payloads.

Pending Map Cleanup

The pending entry maps (pendingEntries, originalPendingEntries) are explicitly cleaned through remove() calls in handleHttpResponseReceived():

LogEntry pendingEntry = pendingEntries.remove(modKey);
Integer pendingIdx = pendingEntryIndex.remove(modKey);

This prevents unbounded growth from requests that never receive responses (connection timeouts, client aborts). However, a theoretical leak exists: if handleHttpRequestToBeSent() is called but handleHttpResponseReceived() is never invoked (e.g., Burp crash, extension reload), entries remain in the maps. The time window component of generatePendingKey() (current second) limits this leak to sub-second duration under normal operation.

Thread Pool Resource Management

The ExecutorService requires explicit shutdown to prevent thread leaks:

public void setThreadCount(int threadCount) {
    if (executorService != null && !executorService.isShutdown()) {
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    executorService = Executors.newFixedThreadPool(threadCount);
}

The shutdown()awaitTermination()shutdownNow() sequence provides graceful termination with forced cleanup fallback. The Thread.currentThread().interrupt() preserves the interrupt status for calling code.

JVM Memory Considerations

Sala7-Bypass operates within Burp’s JVM, typically allocated 1-4GB heap depending on configuration. The extension’s memory footprint scales with:

For a typical 10,000 request testing session: ~50MB log storage + ~5MB engine state + UI overhead ≈ 60MB total—well within JVM capacity but non-trivial for extended sessions.

Performance Optimization Opportunities

Several architectural decisions prioritize correctness over performance, presenting optimization opportunities:

  1. Response Body Storage: Currently stores complete response bodies in HttpResponse objects. For bypass detection, only status codes and select headers are necessary. Truncating or hashing response bodies would reduce memory by 60-80%.
  2. Statistics Computation: updateStats() performs O(n) iteration over all log entries on every response. For 10,000 entries, this is negligible; for 100,000+, incremental statistics updates would improve responsiveness.
  3. Bypass Header Pool: The static BYPASS_HEADERS_POOL generates 40+ header combinations at class load time. Lazy generation or caching would reduce startup memory.
  4. Request Hashing: BypassEngine.hashRequest() computes bodyToString().hashCode() for every request. For large bodies, this is expensive. Incremental hashing or body truncation would improve performance.

These optimizations are deferred pending profiling data indicating actual bottlenecks, consistent with the extension’s design philosophy of correctness-first, optimize-later.


3. Core Features Deep Dive


3.1 Request Interception Engine

The Request Interception Engine constitutes the entry point for all Sala7-Bypass functionality, capturing HTTP traffic from Burp Suite’s HTTP stack before it reaches target servers and applying configurable transformations based on extension settings. This engine operates through two primary interception interfaces registered during extension initialization, creating a comprehensive coverage model that handles traffic regardless of origin—whether from browser proxies, Burp’s own tools (Scanner, Repeater, Intruder), or manual request construction.

3.1.1 HTTP Handler Registration

The HTTP handler registration occurs within the initialize(MontoyaApi api) bootstrap sequence, establishing the extension as an active participant in Burp’s HTTP processing pipeline:

@Override
public void initialize(MontoyaApi api) {
    this.api = api;
    api.extension().setName("Sala7-Bypass v" + CURRENT_VERSION);
    
    // Core engine instantiation
    this.bypassEngine = new BypassEngine(api);
    this.executorService = Executors.newFixedThreadPool(threadCount);
    
    // UI initialization on Event Dispatch Thread
    SwingUtilities.invokeLater(() -> {
        ui = new BypassExtensionUI(this);
        api.userInterface().registerSuiteTab("Sala7-Bypass", ui.getUIComponent());
    });
    
    // Critical: HTTP handler registration
    api.http().registerHttpHandler(this);
    api.proxy().registerRequestHandler(this);
    
    api.logging().logToOutput("Sala7-Bypass Extension loaded! v" + CURRENT_VERSION);
    checkForUpdates();
}

The api.http().registerHttpHandler(this) call registers BurpExtender as an HttpHandler implementation with Burp’s HTTP stack. This registration is all-or-nothing: the extension receives callbacks for every HTTP request and response processed by Burp, with no built-in filtering at the API level. Consequently, the extension must implement efficient early-exit logic to minimize performance impact on non-target traffic.

The HttpHandler interface contract requires two method implementations:

public interface HttpHandler {
    RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent);
    ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived);
}

Sala7-Bypass’s BurpExtender implements both methods, with handleHttpRequestToBeSent() performing request transformation and handleHttpResponseReceived() performing response analysis and bypass triggering. The methods execute synchronously on Burp’s HTTP dispatcher threads, with any blocking operation in handleHttpRequestToBeSent() directly delaying request transmission.

Registration Scope and Limitations

The HTTP handler registration captures all HTTP traffic processed by Burp, but with important scope distinctions:

Table

Traffic Source Captured by HTTP Handler Notes
Browser via Proxy ✅ Yes Primary interception target
Burp Repeater ✅ Yes Manual testing traffic
Burp Intruder ✅ Yes Automated attack traffic
Burp Scanner ✅ Yes Vulnerability scanning traffic
Burp Sequencer/Comparer ✅ Yes All tool-generated traffic
WebSocket traffic ❌ No Requires WebSocket handler registration

WebSocket traffic is explicitly excluded from HTTP handler callbacks, as the Montoya API separates WebSocket handling into a distinct interface (WebSocketHandler). Sala7-Bypass does not currently implement WebSocket interception, representing a future enhancement for modern applications utilizing WebSocket-based APIs.

Handler Priority and Ordering

Burp’s HTTP handler architecture does not guarantee execution order among multiple registered handlers. If multiple extensions register HttpHandler implementations, the invocation sequence is undefined and may vary across Burp restarts. Sala7-Bypass assumes exclusive or compatible handler presence, with loop prevention (X-Burp-Processed header) providing defense against recursive modification by other extensions.

3.1.2 Proxy Handler Integration

In addition to the general HTTP handler, Sala7-Bypass registers a ProxyRequestHandler specifically for proxy-originated traffic:

api.proxy().registerRequestHandler(this);

The ProxyRequestHandler interface provides finer-grained interception points for proxy-specific processing:

public interface ProxyRequestHandler {
    ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest);
    ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest);
}

Sala7-Bypass’s current implementation provides passthrough behavior for both proxy handler methods:

@Override
public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) {
    return ProxyRequestReceivedAction.continueWith(interceptedRequest);
}

@Override
public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) {
    return ProxyRequestToBeSentAction.continueWith(interceptedRequest);
}

This passthrough design reflects an architectural decision to centralize all modification logic in the HttpHandler rather than splitting between handler types. However, the proxy handler registration serves critical future extensibility purposes:

  1. SSL/TLS Inspection: Proxy-specific handlers can access TLS handshake information unavailable to general HTTP handlers, enabling future features like SNI-based routing bypass or certificate pinning detection.
  2. Upstream Proxy Manipulation: The InterceptedRequest proxy interface provides access to upstream proxy configuration, enabling future upstream proxy bypass techniques (e.g., X-Forwarded-Host manipulation for proxy routing).
  3. Proxy Authentication: Proxy-specific authentication headers (e.g., Proxy-Authorization) can be manipulated independently of destination server authentication.
  4. Request Dropping: ProxyRequestReceivedAction supports request dropping (drop()) for future implementations of aggressive filtering or tarpit responses.

The dual registration (HTTP handler + Proxy handler) ensures Sala7-Bypass receives all traffic through at least one interception point, with the HTTP handler providing the active transformation pipeline.

3.1.3 Request Lifecycle Hook Points

Sala7-Bypass intercepts requests at the HttpRequestToBeSent phase—immediately before transmission to the target server, after all upstream Burp processing (proxy rules, match/replace, upstream proxy routing) but before network I/O. This hook point selection is deliberate, providing several advantages:

Table

Hook Point Timing Sala7-Bypass Usage
ProxyRequestReceived Client → Burp proxy Passthrough (future extensibility)
ProxyRequestToBeSent Burp proxy → upstream Passthrough (future extensibility)
HttpRequestToBeSent Pre-network I/O Active transformation
HttpResponseReceived Post-network I/O Analysis & bypass triggering

The HttpRequestToBeSent phase enables modification of requests that will be seen by the target server exactly as modified, with no subsequent Burp processing that might strip or alter injected headers. This is critical for bypass headers that must reach the target application unmodified.

Request Processing Pipeline

The handleHttpRequestToBeSent() method implements a sequential decision pipeline:

@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
    // Gate 1: Extension enabled check
    if (!extensionEnabled) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    
    // Gate 2: Scope restriction
    if (scopeOnly && !api.scope().isInScope(requestToBeSent.url())) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    
    // Gate 3: Loop prevention
    if (loopPrevention && hasLoopHeader(requestToBeSent)) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    
    // Gate 4: Authentication filter
    if (authFilter && !hasAuthIndicators(requestToBeSent)) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    
    // Gate 5: Content-type filter
    if (contentTypeFilter && !matchesContentType(requestToBeSent)) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    
    // Transformation pipeline
    HttpRequest modifiedRequest = applyModifications(requestToBeSent);
    
    // Logging and tracking
    LogEntry entry = createLogEntry(requestToBeSent, modifiedRequest);
    int entryIndex = logEntries.size();
    logEntries.add(entry);
    ui.logEntry(entry);
    
    // Pending entry registration for response matching
    registerPendingEntries(requestToBeSent, modifiedRequest, entry, entryIndex);
    
    return RequestToBeSentAction.continueWith(modifiedRequest);
}

Each gate implements an early-exit pattern, returning the unmodified request immediately if the gate condition fails. This minimizes performance impact for traffic that should not be processed. The gates are ordered by computational cost: the enabled check is trivial (boolean field access), while content-type parsing requires header iteration and string matching.

Gate Implementation Details

Table

Gate Condition Cost Rationale
Extension enabled extensionEnabled == true O(1) Most fundamental; first check
Scope-only api.scope().isInScope(url) O(1) map lookup Prevents out-of-scope processing
Loop prevention Header presence check O(n) header scan Avoids infinite loops
Auth filter Cookie/Authorization presence O(n) header scan Filters non-auth traffic
Content-type filter Content-Type parsing O(n) header scan + string match Filters by payload type

The hasLoopHeader() method implements the loop detection logic

private boolean hasLoopHeader(HttpRequest request) {
    return request.headers().stream()
        .anyMatch(h -> h.name().equalsIgnoreCase(LOOP_HEADER) 
                    && h.value().equals(LOOP_VALUE));
}

This scans all request headers for X-Burp-Processed: true, with equalsIgnoreCase() ensuring case-insensitive matching. The O(n) scan is acceptable given typical header counts (5-20 headers) and the criticality of loop prevention.

Modification Application

After gate passage, the request enters the transformation pipeline:

HttpRequest originalRequest = requestToBeSent;
HttpRequest modifiedRequest = requestToBeSent;
boolean wasModified = false;
List<String> modifications = new ArrayList<>();

if (loopPrevention) {
    modifiedRequest = modifiedRequest.withAddedHeader(LOOP_HEADER, LOOP_VALUE);
}

if (methodSwitching) {
    HttpRequest switched = RequestModifier.switchMethod(modifiedRequest);
    if (!switched.method().equals(modifiedRequest.method())) {
        modifiedRequest = switched;
        wasModified = true;
        modifications.add("METHOD_SWITCH:" + originalRequest.method() + "->" + switched.method());
    }
}

if (removeAuth) {
    if (removeCookie) {
        modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Cookie");
        wasModified = true;
        modifications.add("REMOVE_COOKIE");
    }
    if (removeAuthorization) {
        modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Authorization");
        wasModified = true;
        modifications.add("REMOVE_AUTH");
    }
}

The transformations are applied sequentially, with each transformation building upon the previous result. The modifiedRequest variable is reassigned at each step, leveraging Montoya API’s immutable object pattern. The wasModified flag and modifications list track transformation history for logging and bypass classification.

Pending Entry Registration

For response matching, Sala7-Bypass registers entries in the pending maps:

String originalKey = generatePendingKey(originalRequest, "orig");
originalPendingEntries.put(originalKey, entry);
originalPendingIndex.put(originalKey, entryIndex);

if (wasModified) {
    String pendingKey = generatePendingKey(modifiedRequest, "mod");
    pendingEntries.put(pendingKey, entry);
    pendingEntryIndex.put(pendingKey, entryIndex);
}

The dual-key system (original key + modified key) enables matching responses to either the original or modified request, depending on which response arrives first. This is necessary because Burp’s HTTP stack may process responses in non-deterministic order, especially under high concurrency or when the server exhibits variable response times.


3.2 Response Analysis Engine

The Response Analysis Engine processes HTTP responses as they return from target servers, correlating them with their corresponding pending requests, detecting status code transitions indicative of access control boundaries, and triggering automated bypass attempts when blocked responses are detected.

3.2.1 Status Code Detection (403/401/429)

The handleHttpResponseReceived() method implements the core response analysis logic, with status code detection serving as the primary decision trigger:

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
    if (!extensionEnabled) {
        return ResponseReceivedAction.continueWith(responseReceived);
    }
    
    int statusCode = responseReceived.statusCode();
    HttpRequest request = responseReceived.initiatingRequest();
    
    // Pending entry retrieval with atomic remove
    String modKey = generatePendingKey(request, "mod");
    LogEntry pendingEntry = pendingEntries.remove(modKey);
    Integer pendingIdx = pendingEntryIndex.remove(modKey);
    
    String origKey = generatePendingKey(request, "orig");
    LogEntry originalEntry = originalPendingEntries.remove(origKey);
    Integer originalIdx = originalPendingIndex.remove(origKey);
    
    // Status-based routing logic...
}

The method first checks the extension enabled state (consistent with request handling), then extracts the status code and initiating request. The critical operation is pending entry retrieval: remove() operations on ConcurrentHashMap provide atomic retrieval and deletion, ensuring that a response is processed exactly once even under concurrent access.

Status Code Classification

Sala7-Bypass categorizes HTTP status codes into functional groups for decision-making:

Table

Status Code Category Extension Action
200-299 Success Log as PASSTHROUGH or BYPASS SUCCESS
301-302 Redirect Log as PASSTHROUGH or BYPASS SUCCESS
401 Unauthorized Log as BLOCKED; trigger auth bypass attempts
403 Forbidden Log as BLOCKED; trigger all bypass attempts
429 Too Many Requests Log as BLOCKED; trigger rate limit bypass
400-499 (other) Client Error Log as PASSTHROUGH
500-599 Server Error Log as PASSTHROUGH

The 401/403/429 codes are collectively termed “blocking codes”—responses that indicate the request was denied by an access control mechanism. These codes trigger the bypass attempt pipeline, while other codes are logged for statistical completeness but do not initiate automated bypassing.

Response-Request Correlation

The correlation logic handles three distinct scenarios:

Scenario 1: Modified Response (pendingEntry != null)

The response corresponds to a modified request. The entry is updated with the modified status, and bypass detection is performed:

if (pendingEntry != null && pendingIdx != null) {
    pendingEntry.setModifiedStatus(statusCode);
    pendingEntry.setResponseTime(responseTime);
    pendingEntry.setModifiedResponse(responseReceived);
    
    // Ensure trueOriginalStatus is set for proper bypass detection
    if (pendingEntry.getTrueOriginalStatus() == -1) {
        // Sync from original entry if available
        if (originalEntry != null && originalEntry.getTrueOriginalStatus() > 0) {
            pendingEntry.setTrueOriginalStatus(originalEntry.getTrueOriginalStatus());
        } else if (pendingEntry.getOriginalStatus() > 0) {
            pendingEntry.setTrueOriginalStatus(pendingEntry.getOriginalStatus());
        }
    }
    
    boolean isRealBypass = detectRealBypass(pendingEntry, statusCode);
    // ... bypass handling ...
}

Scenario 2: Original Response (originalEntry != null)

The response corresponds to an unmodified request. This establishes the baseline for bypass detection:

} else if (originalEntry != null && originalIdx != null) {
    originalEntry.setOriginalStatus(statusCode);
    originalEntry.setModifiedStatus(statusCode);
    originalEntry.setTrueOriginalStatus(statusCode);
    
    // Sync trueOriginalStatus to any pending modified entries
    for (int i = 0; i < logEntries.size(); i++) {
        LogEntry entry = logEntries.get(i);
        if (entry.getUrl().equals(originalEntry.getUrl()) 
            && entry != originalEntry 
            && entry.getTrueOriginalStatus() == -1) {
            entry.setTrueOriginalStatus(statusCode);
        }
    }
    
    if (statusCode == 403 || statusCode == 429 || statusCode == 401) {
        originalEntry.setAction("BLOCKED");
        // Trigger bypass attempts
        if (detect403 && bypassHeaders) {
            triggerBypassAttempts(request, responseReceived, statusCode);
        }
    }
    // ... logging ...
}

Scenario 3: Orphaned Response (no matching entry)

The response does not match any pending entry (e.g., request was sent before extension loaded, or entry was already processed). A backward scan attempts to match by URL:

} else {
    for (int i = logEntries.size() - 1; i >= 0; i--) {
        LogEntry entry = logEntries.get(i);
        if (entry.getUrl().equals(request.url()) && entry.getOriginalStatus() == -1) {
            entry.setOriginalStatus(statusCode);
            entry.setModifiedStatus(statusCode);
            entry.setTrueOriginalStatus(statusCode);
            // ... update and trigger bypass if blocked ...
            break;
        }
    }
}

3.2.2 Response Time Tracking

Response time measurement provides performance context for bypass attempts, distinguishing between network latency and server processing time. Sala7-Bypass implements response time tracking at two levels:

Original/Modified Request Timing

For requests processed through the HTTP handler, response time is currently set to 0:

long responseTime = 0; // Placeholder - actual timing requires async measurement

This limitation stems from the synchronous nature of handleHttpResponseReceived(): the method receives the completed response, but lacks access to the request transmission timestamp. Future implementations could track timing through a separate Map<String, Long> storing request start times.

Bypass Attempt Timing

Bypass attempts execute asynchronously and can measure precise timing:

long startTime = System.currentTimeMillis();
var httpResponse = api.http().sendRequest(bypassRequest);
long bypassTime = System.currentTimeMillis() - startTime;

This timing is stored in the bypass LogEntry:

bypassEntry.setResponseTime(bypassTime);

The response time serves dual purposes: performance analysis (identifying slow bypass vectors) and detection evasion (configuring delays to avoid rate limiting).

3.2.3 Automatic Bypass Triggering

The triggerBypassAttempts() method serves as the bridge between response analysis and bypass execution:

private void triggerBypassAttempts(HttpRequest request, HttpResponse response, int statusCode) {
    if (!hasLoopHeader(request)) {
        String hash = bypassEngine.hashRequest(request);
        if (bypassEngine.shouldBypass(hash, maxRetries)) {
            bypassEngine.markProcessed(hash);
            executorService.submit(() -> attemptBypass(request, response, statusCode, hash));
        }
    }
}

The method implements three safety checks before triggering:

  1. Loop Prevention: Re-checks for the loop header, preventing recursive bypass attempts on requests generated by bypass attempts themselves.
  2. Deduplication: Computes a request hash and checks bypassEngine.shouldBypass(), which verifies that the request has not exceeded retry limits and has not already succeeded.
  3. Async Execution: Submits the bypass attempt to the ExecutorService thread pool, preventing blocking of Burp’s HTTP dispatcher.

The hashRequest() method creates a deterministic identifier:

public String hashRequest(HttpRequest request) {
    return request.method() + "|" + request.url() + "|" + request.bodyToString().hashCode();
}

This hash incorporates method, URL, and body content hash, ensuring that requests with identical structure are deduplicated while distinct requests (different parameters, different bodies) receive independent bypass attempts.


3.3 Bypass Techniques Implementation

Sala7-Bypass implements six major bypass technique categories, each targeting distinct access control implementation flaws. The techniques are not mutually exclusive; the extension supports simultaneous application and intelligent combination.

3.3.1 IP Spoofing Bypass

IP spoofing bypass exploits the common architectural pattern where applications derive client identity from HTTP headers rather than transport-layer connection information. This vulnerability class is endemic to deployments behind reverse proxies, load balancers, and CDNs, where the application server lacks direct access to the original TCP connection.

3.3.1.1 X-Forwarded-For Header Injection

The X-Forwarded-For (XFF) header is defined in RFC 7239 (obsoleting earlier informal specifications) as a de facto standard for identifying client origin through proxy chains. The formal syntax specifies comma-separated IP addresses, with the leftmost address representing the original client:

X-Forwarded-For: client, proxy1, proxy2

Sala7-Bypass exploits the prevalent implementation flaw where applications parse only the leftmost IP without validating the header’s authenticity:

private static List<Map<String, String>> generateBypassHeaders() {
    List<Map<String, String>> headersList = new ArrayList<>();
    for (String ip : IP_VALUES) {
        headersList.add(Map.of("X-Forwarded-For", ip));
        // ... other headers ...
    }
    return headersList;
}

The IP_VALUES array includes multiple representations of localhost:

private static final String[] IP_VALUES = {
    "127.0.0.1", "127.0.0.2", "::1", "localhost", "0.0.0.0",
    "0000:0000:0000:0000:0000:0000:0000:0001", "0x7f000001", "2130706433"
};

Each IP is injected as a standalone X-Forwarded-For value, testing whether the target application:

3.3.1.2 X-Real-IP & X-Client-IP Variants

Beyond XFF, Sala7-Bypass tests 40+ header variants that different proxy implementations and cloud platforms use for client IP propagation:

Table

Header Origin Platform Typical Usage
X-Real-IP Nginx Native nginx proxy variable
X-Client-IP Azure/AWS Cloud platform standard
X-Originating-IP Generic Legacy proxy implementations
X-Remote-IP Generic Alternative to XFF
CF-Connecting-IP Cloudflare CDN-specific client IP
True-Client-IP Akamai Enterprise CDN standard
X-Cluster-Client-IP Kubernetes Container orchestration

The complete header pool is generated programmatically:

String[] ipHeaders = {
    "X-Forwarded-For", "X-Real-IP", "X-Originating-IP", "X-Remote-IP",
    "X-Client-IP", "X-Custom-IP-Authorization", "X-Original-Remote-Addr",
    "X-Forwarded-Host", "X-HTTP-Host-Override", "X-Host", "X-True-IP",
    "Base-Url", "Client-IP", "Http-Url", "Proxy-Host", "Proxy-Url",
    "Real-Ip", "Redirect", "Referer", "Referrer", "Refferer",
    "Request-Uri", "Uri", "Url", "X-Forward-For", "X-Forwarded-By",
    "X-Forwarded-For-Original", "X-Forwarded-Server", "X-Forwarded",
    "X-Forwarder-For", "X-Http-Destinationurl", "X-Proxy-Url",
    "X-Remote-Addr", "Forwarded", "X-Cluster-Client-IP",
    "CF-Connecting-IP", "True-Client-IP", "X-Coming-From",
    "X-Proxy-User-Ip", "X-Remote-Ip"
};

Each header is paired with each IP value, creating a comprehensive test matrix that maximizes coverage across diverse server configurations.

3.3.1.3 IPv4/IPv6/Hex/Decimal IP Encoding

Sala7-Bypass tests multiple IP representations to bypass naive parsing implementations:

Table

Representation Example Bypass Target
Standard dotted-decimal 127.0.0.1 Baseline
Shortened 127.1 Poor parsing (treated as 127.0.0.1)
Zero-padded octal 0177.0.0.1 Octal interpretation (0177 = 127)
Hexadecimal octets 0x7f.0x00.0x00.0x01 Hex parsing
Decimal integer 2130706433 Integer IP representation
IPv6 mapped ::ffff:127.0.0.1 IPv6 compatibility
IPv6 compressed ::1 Standard IPv6 localhost

The IP_VALUES array includes these variants to test parsing robustness:

"127.0.0.1", "127.0.0.2", "::1", "localhost", "0.0.0.0",
"0000:0000:0000:0000:0000:0000:0000:0001", "0x7f000001", "2130706433"

3.3.1.4 Custom IP Header Pool (40+ Headers)

The complete header pool exceeds 40 entries when considering all IP headers, port headers, scheme headers, URL headers, and method headers. The Payloads tab in the UI allows customization of this pool:

// From BypassExtensionUI.createPayloadsPanel()
private void loadDefaultPayloads() {
    // IP headers × IP values
    for (String header : ipHeaders) {
        for (String ip : ipValues) {
            payloadsModel.addRow(new Object[]{header, ip, Boolean.TRUE});
        }
    }
    
    // Port headers × port values
    String[] portHeaders = {"X-Forwarded-Port", "X-Port"};
    String[] portValues = {"443", "4443", "80", "8080", "8443", "3000", "5000", "8000"};
    
    // Scheme headers × scheme values
    String[] schemeHeaders = {"X-Forwarded-Scheme", "X-Scheme", "X-Forwarded-Proto"};
    String[] schemeValues = {"http", "https"};
    
    // URL headers × URL values
    String[] urlHeaders = {"X-Original-URL", "X-Rewrite-URL", "X-Forwarded-Path", "X-Request-URI"};
    String[] urlValues = {"/", "/admin", "/api", "/api/v1", "/api/v2", "/api/v3", "/api/v4"};
    
    // Method headers × method values
    String[] methodHeaders = {"X-HTTP-Method-Override", "X-Method-Override"};
    String[] methodValues = {"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE"};
}

This comprehensive pool enables testing of header injection vectors beyond simple IP spoofing, including port manipulation, scheme override, and path rewriting.

3.3.2 HTTP Method Switching

HTTP method switching exploits implementations where access control decisions are bound to specific HTTP methods while the underlying resource handler processes multiple methods. This vulnerability class is particularly prevalent in REST API implementations where framework-level routing enforces method constraints but custom access control logic checks only specific methods.

3.3.2.1 GET to POST Conversion with Parameter Migration

The RequestModifier.switchMethod() method handles GET-to-POST conversion:

public static HttpRequest switchMethod(HttpRequest request) {
    String method = request.method();
    if (method.equalsIgnoreCase("GET")) {
        return convertGetToPost(request);
    } else if (method.equalsIgnoreCase("POST")) {
        return convertPostToGet(request);
    }
    return request;
}

The convertGetToPost() implementation extracts URL parameters and migrates them to the request body:

private static HttpRequest convertGetToPost(HttpRequest request) {
    List<ParsedHttpParameter> urlParams = request.parameters().stream()
        .filter(p -> p.type() == HttpParameterType.URL)
        .collect(Collectors.toList());
    
    StringBuilder body = new StringBuilder();
    for (int i = 0; i < urlParams.size(); i++) {
        ParsedHttpParameter param = urlParams.get(i);
        if (i > 0) body.append("&");
        body.append(urlEncode(param.name())).append("=").append(urlEncode(param.value()));
    }
    
    HttpRequest modified = request;
    String url = request.url();
    if (url.contains("?")) {
        url = url.substring(0, url.indexOf("?"));
        modified = modified.withPath(url);
    }
    
    modified = modified.withMethod("POST");
    if (body.length() > 0) {
        modified = modified.withBody(body.toString());
        if (!hasHeader(modified, "Content-Type")) {
            modified = modified.withAddedHeader("Content-Type", "application/x-www-form-urlencoded");
        }
    }
    return modified;
}

This migration preserves parameter semantics while changing the HTTP method, enabling bypass of method-specific access controls that do not inspect the request body.

3.3.2.2 POST to GET Conversion with Query String Building

The reverse conversion migrates body parameters to the URL query string:

private static HttpRequest convertPostToGet(HttpRequest request) {
    List<ParsedHttpParameter> bodyParams = request.parameters().stream()
        .filter(p -> p.type() == HttpParameterType.BODY)
        .collect(Collectors.toList());
    
    StringBuilder queryString = new StringBuilder();
    for (int i = 0; i < bodyParams.size(); i++) {
        ParsedHttpParameter param = bodyParams.get(i);
        if (i > 0) queryString.append("&");
        queryString.append(urlEncode(param.name())).append("=").append(urlEncode(param.value()));
    }
    
    HttpRequest modified = request.withMethod("GET");
    if (queryString.length() > 0) {
        String url = modified.url();
        String separator = url.contains("?") ? "&" : "?";
        modified = modified.withPath(url + separator + queryString);
    }
    
    modified = modified.withBody("");
    modified = removeHeader(modified, "Content-Type");
    return modified;
}

This conversion is particularly effective against APIs that enforce body size limits or content-type restrictions on POST requests but not GET requests.

3.3.2.3 Content-Type Header Management

Method switching requires careful content-type handling to ensure server-side parsing compatibility:

// GET→POST: Add Content-Type if body present
if (!hasHeader(modified, "Content-Type")) {
    modified = modified.withAddedHeader("Content-Type", "application/x-www-form-urlencoded");
}

// POST→GET: Remove Content-Type (GET has no body)
modified = removeHeader(modified, "Content-Type");

The hasHeader() helper checks for header presence:

private static boolean hasHeader(HttpRequest request, String headerName) {
    return request.headers().stream()
        .anyMatch(h -> h.name().equalsIgnoreCase(headerName));
}

3.3.3 Authentication Header Removal

Authentication header removal tests the security boundary assumption that authentication is strictly required. Many applications implement “fail-open” behaviors where missing authentication results in default access levels rather than explicit denial—a logic flaw that complete auth removal can reveal.

Cookie removal is controlled by the removeCookie boolean flag:

if (removeAuth) {
    if (removeCookie) {
        modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Cookie");
        wasModified = true;
        modifications.add("REMOVE_COOKIE");
    }
}

The removeHeader() method leverages Montoya API’s immutable transformation:

public static HttpRequest removeHeader(HttpRequest request, String headerName) {
    return request.withRemovedHeader(headerName);
}

This removes all Cookie headers, testing whether the application falls back to unauthenticated access or session-less processing.

3.3.3.2 Authorization Header Removal

Authorization header removal targets Bearer token, Basic auth, and custom authorization schemes:

if (removeAuthorization) {
    modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Authorization");
    wasModified = true;
    modifications.add("REMOVE_AUTH");
}

This tests APIs that may have legacy fallback mechanisms or public endpoints mistakenly protected by middleware.

3.3.3.3 Selective vs Complete Auth Removal

The UI provides independent toggles for Cookie and Authorization removal:

chkRemoveCookie = new JCheckBox("Remove Cookie Header");
chkRemoveAuthorization = new JCheckBox("Remove Authorization Header");

This selectivity enables targeted testing: removing only Authorization to test token-based APIs, only Cookie to test session-based applications, or both for comprehensive testing.

3.3.4 Path-Based Bypass

Path-based bypass exploits discrepancies between the URL path used for routing and the path used for access control decisions. This vulnerability class is common in applications using reverse proxies or API gateways where path rewriting occurs.

3.3.4.1 X-Original-URL Injection

The X-Original-URL header is processed by some reverse proxies (particularly older Nginx configurations) as an alternative path specification:

headersList.add(Map.of("X-Original-URL", "/"));
headersList.add(Map.of("X-Rewrite-URL", "/"));

When the proxy routes based on the actual request path but the application authorizes based on X-Original-URL, injecting an allowed path (e.g., /) while requesting a blocked path (e.g., /admin) can bypass restrictions.

3.3.4.2 X-Rewrite-URL Manipulation

X-Rewrite-URL serves a similar function in some Ruby on Rails and PHP applications:

headersList.add(Map.of("X-Rewrite-URL", "/"));

Both headers are included in the bypass pool, with the actual request path remaining the target endpoint while the header suggests an allowed path.

3.3.5 Rate Limiting Bypass

Rate limiting bypass targets implementations that track request rates using client-controllable identifiers rather than server-side session or connection state.

3.3.5.1 IP Rotation Strategies

The primary rate limiting bypass strategy rotates X-Forwarded-For values to present a different client identity for each request:

// In attemptBypass()
for (Map<String, String> headers : headerSets) {
    // Each header set may contain different X-Forwarded-For values
    HttpRequest bypassRequest = originalRequest;
    for (Map.Entry<String, String> header : headers.entrySet()) {
        bypassRequest = bypassRequest.withAddedHeader(header.getKey(), header.getValue());
    }
    // ... send request ...
}

The IP pool includes sufficient diversity (8+ IP representations) to exhaust typical per-IP rate limits when combined with maxRetries (default 3) and threadCount (default 5).

3.3.5.2 Distributed Request Patterns

The requestDelay configuration enables deliberate pacing to avoid pattern detection:

if (requestDelay > 0) {
    try {
        Thread.sleep(requestDelay);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return;
    }
}

Combined with concurrent threads, this creates distributed request patterns that mimic legitimate multi-user traffic rather than sequential brute-force attempts.

3.3.6 Combined Attack Vectors

Combined attack vectors apply multiple bypass techniques simultaneously, targeting applications with layered but independently flawed access controls.

3.3.6.1 Multi-Technique Orchestration

The bypass attempt pipeline supports combined technique application:

// In attemptBypass()
HttpRequest bypassRequest = originalRequest;
bypassRequest = RequestModifier.removeHeader(bypassRequest, LOOP_HEADER);

// Add bypass headers
for (Map.Entry<String, String> header : headers.entrySet()) {
    bypassRequest = bypassRequest.withAddedHeader(header.getKey(), header.getValue());
}

// Apply method switch
if (bypassApplyMethodSwitch) {
    HttpRequest switched = RequestModifier.switchMethod(bypassRequest);
    if (!switched.method().equals(bypassRequest.method())) {
        bypassRequest = switched;
        methodWasSwitched = true;
    }
}

// Apply auth removal
if (bypassApplyAuthRemoval) {
    if (removeCookie) {
        bypassRequest = RequestModifier.removeHeader(bypassRequest, "Cookie");
        authWasRemoved = true;
    }
    if (removeAuthorization) {
        bypassRequest = RequestModifier.removeHeader(bypassRequest, "Authorization");
        authWasRemoved = true;
    }
}

3.3.6.2 Intelligent Technique Selection

The determineBypassType() method classifies successful bypasses by technique:

private String determineBypassType(LogEntry entry) {
    List<String> mods = entry.getModifications();
    boolean hasMethodSwitch = mods.stream().anyMatch(m -> m.startsWith("METHOD_SWITCH"));
    boolean hasAuthRemoval = mods.contains("REMOVE_COOKIE") || mods.contains("REMOVE_AUTH");
    
    if (hasMethodSwitch && hasAuthRemoval) return "COMBINED";
    else if (hasMethodSwitch) return "METHOD";
    else if (hasAuthRemoval) return "AUTH";
    // ...
}

This classification enables per-technique statistics and targeted reporting, distinguishing between single-technique successes and combined vector exploitation.


4. Advanced Engine Components


4.1 Bypass Engine (BypassEngine.java)

The BypassEngine class encapsulates the stateful decision logic that prevents redundant processing, enforces resource limits, and memoizes successful bypass discoveries. Operating as a dedicated state management layer, it isolates mutable state from the request interception and response analysis components, ensuring that concurrent request processing remains deterministic and bounded.

The engine’s design reflects the operational reality of penetration testing: a single target endpoint may generate hundreds of requests during reconnaissance, and each request may trigger multiple bypass attempts. Without deduplication, rate limiting, and success memoization, the extension would generate exponential request volumes, overwhelming both the target server and Burp’s own processing capacity.

4.1.1 Request Hashing & Deduplication

Request hashing serves as the fundamental primitive for all engine operations: deduplication, retry tracking, and success memoization all depend on a deterministic, collision-resistant identifier that uniquely represents a request’s structural characteristics.

Hash Construction

The hashRequest() method constructs a composite string incorporating three request attributes:

java Copy

public String hashRequest(HttpRequest request) { return request.method() + "|" + request.url() + "|" + request.bodyToString().hashCode(); }

Table

Component Purpose Collision Risk
request.method() Distinguishes GET/POST/PUT/DELETE bypasses None (finite set)
request.url() Distinguishes endpoints Low (URL includes path + query)
request.bodyToString().hashCode() Distinguishes parameter variations Medium (hashCode() is 32-bit)

The String.hashCode() implementation for the body content provides a fast but non-cryptographic hash. The 32-bit output space (2^32 ≈ 4.3 billion) presents theoretical collision probability, but in practice, distinct request bodies producing identical hash codes within a single testing session is negligible for penetration testing purposes. Future enhancements could substitute MessageDigest.getInstance("SHA-256") for cryptographic collision resistance at the cost of computational overhead.

Deduplication Storage

The processedRequests field provides set-based deduplication:

private final Set<String> processedRequests; // ConcurrentHashMap.newKeySet()

public boolean isProcessed(String requestHash) {
    return processedRequests.contains(requestHash);
}

public void markProcessed(String requestHash) {
    processedRequests.add(requestHash);
}

The ConcurrentHashMap.newKeySet() implementation provides lock-free, thread-safe set operations with the same concurrency characteristics as ConcurrentHashMap. This is critical because markProcessed() is called from Burp’s HTTP dispatcher threads (synchronous) while isProcessed() may be queried from bypass worker threads (asynchronous).

Deduplication Scope and Limitations

The current deduplication is session-scoped: the processedRequests set is cleared only when the extension is unloaded or Burp restarts. This design choice reflects the testing workflow where repeated identical requests during a session should not trigger redundant bypass attempts. However, it introduces a memory growth consideration: each unique request hash consumes approximately 50-100 bytes (string object overhead), and testing sessions with 10,000+ unique endpoints would consume ~1MB of heap—acceptable within Burp’s typical 1-4GB allocation.

Deduplication Timing

The deduplication check occurs in triggerBypassAttempts():

private void triggerBypassAttempts(HttpRequest request, HttpResponse response, int statusCode) {
    if (!hasLoopHeader(request)) {
        String hash = bypassEngine.hashRequest(request);
        if (bypassEngine.shouldBypass(hash, maxRetries)) {
            bypassEngine.markProcessed(hash);
            executorService.submit(() -> attemptBypass(request, response, statusCode, hash));
        }
    }
}

The markProcessed() call occurs before executorService.submit(), ensuring that even if multiple threads simultaneously process responses for the same request (possible with HTTP/2 multiplexing or proxy retry logic), only one bypass attempt sequence is initiated. The atomicity of ConcurrentHashMap.add() guarantees this even under race conditions.

4.1.2 Retry Counting & Limit Enforcement

Retry tracking prevents infinite bypass loops while enabling thorough testing of multi-vector bypass scenarios. Each unique request hash maintains an independent retry counter, incremented with each bypass attempt and capped by the configurable maxRetries parameter.

Retry Storage

The retryCount map maintains atomic integer counters:

private final Map<String, Integer> retryCount; // ConcurrentHashMap

public int getRetryCount(String requestHash) {
    return retryCount.getOrDefault(requestHash, 0);
}

public void incrementRetry(String requestHash) {
    retryCount.merge(requestHash, 1, Integer::sum);
}

public void resetRetry(String requestHash) {
    retryCount.remove(requestHash);
    processedRequests.remove(requestHash);
}

The ConcurrentHashMap.merge() method provides atomic read-modify-write semantics: retryCount.merge(requestHash, 1, Integer::sum) atomically retrieves the current value, applies the sum function, and stores the result. This eliminates the classic get-then-put race condition where two threads read the same value, increment locally, and overwrite each other’s increments.

Retry Limit Enforcement

The shouldBypass() method implements the retry limit gate:

public boolean shouldBypass(String requestHash, int maxRetries) {
    // If we already succeeded, no need to retry
    if (hasSuccess(requestHash)) {
        return false;
    }
    // If we've reached max retries
    if (getRetryCount(requestHash) >= maxRetries) {
        return false;
    }
    return true;
}

This method is called synchronously in triggerBypassAttempts(), making the retry check atomic with respect to the current request processing. However, the incrementRetry() call occurs asynchronously in attemptBypass() after a bypass attempt completes:

// In attemptBypass()
for (Map<String, String> headers : headerSets) {
    if (attempts >= maxRetries) break;
    if (autoStopOnSuccess && bypassEngine.hasSuccess(requestHash)) break;
    
    // ... perform bypass attempt ...
    
    bypassEngine.incrementRetry(requestHash);
    attempts++;
}

This timing creates a potential race condition: if two bypass worker threads process attempts for the same request hash concurrently (possible with rapid repeated requests), both may pass the shouldBypass() check before either increments the counter. The maxRetries limit may be exceeded by the number of concurrent threads. This is architecturally accepted: the limit represents a “soft” bound rather than a hard guarantee, prioritizing thorough testing over strict enforcement.

Retry Counter Reset

The resetRetry() method provides manual reset capability:

public void resetRetry(String requestHash) {
    retryCount.remove(requestHash);
    processedRequests.remove(requestHash);
}

This is currently unused in the main flow but provides a hook for future UI features (e.g., “Reset bypass attempts for this URL”) or programmatic reset in testing scenarios.

4.1.3 Success Tracking & Auto-Stop Logic

Success memoization prevents redundant bypass attempts after a successful bypass has been discovered for a given request. This optimization is critical for efficiency: once X-Forwarded-For: 127.0.0.1 bypasses /admin/dashboard, subsequent identical requests need not waste server resources or testing time on repeated attempts.

Success Storage

The successMap provides boolean memoization:

private final Map<String, Boolean> successMap; // ConcurrentHashMap

public boolean hasSuccess(String requestHash) {
    return successMap.getOrDefault(requestHash, false);
}

public void markSuccess(String requestHash) {
    successMap.put(requestHash, true);
}

Auto-Stop Implementation

The autoStopOnSuccess configuration (controlled via UI checkbox) enables early termination of bypass attempt loops:

// In attemptBypass()
for (Map<String, String> headers : headerSets) {
    if (attempts >= maxRetries) break;
    
    // Early termination if success found and auto-stop enabled
    if (autoStopOnSuccess && bypassEngine.hasSuccess(requestHash)) {
        break;
    }
    
    // ... perform bypass attempt ...
    
    if (isRealBypass) {
        bypassEngine.markSuccess(requestHash);
        if (autoStopOnSuccess) break;
    }
    
    bypassEngine.incrementRetry(requestHash);
    attempts++;
}

The dual autoStopOnSuccess checks—one at loop start and one after success detection—ensure prompt termination regardless of where the success occurs in the header iteration sequence.

Success Persistence

Success state is session-scoped, identical to deduplication state. This means that if a bypass succeeds, is reported, and the tester modifies the target application (e.g., patches the vulnerability), the extension will not retest that request hash until Burp restarts. A “Clear Success State” UI feature would address this limitation but is not currently implemented.

4.1.4 Thread Pool Management

The ExecutorService in BurpExtender manages asynchronous bypass attempt execution, isolating potentially slow network operations from Burp’s synchronous HTTP dispatcher threads.

Pool Configuration

The thread pool is initialized with a configurable fixed size:

private ExecutorService executorService;

// Initialization
this.executorService = Executors.newFixedThreadPool(threadCount);

// Default
private volatile int threadCount = 5;

The Executors.newFixedThreadPool() implementation creates an unbounded work queue (LinkedBlockingQueue) with the specified number of worker threads. This means:

This queuing behavior prevents resource exhaustion but introduces latency: the 100th queued bypass attempt may not execute for several seconds if earlier attempts are slow. For penetration testing scenarios, this latency is generally acceptable; for high-volume automated scanning, it may delay bypass discovery.

Dynamic Reconfiguration

The setThreadCount() method provides dynamic reconfiguration with graceful shutdown:

public void setThreadCount(int threadCount) {
    this.threadCount = threadCount;
    if (executorService != null && !executorService.isShutdown()) {
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    executorService = Executors.newFixedThreadPool(threadCount);
}

The shutdown sequence implements the standard Java executor lifecycle:

  1. shutdown(): Initiates graceful shutdown, rejecting new tasks but completing queued tasks
  2. awaitTermination(5, SECONDS): Waits up to 5 seconds for queued tasks to complete
  3. shutdownNow(): Forcefully interrupts running tasks and returns unexecuted tasks

The Thread.currentThread().interrupt() call preserves the interrupt status for calling code, enabling proper interrupt propagation up the call stack.

Thread Safety of Bypass Attempts

Each bypass attempt executes in an independent worker thread, with thread safety ensured through:

Table

Resource Thread Safety Mechanism
logEntries CopyOnWriteArrayList
pendingEntries/pendingEntryIndex ConcurrentHashMap operations
bypassEngine state ConcurrentHashMap operations
api.http().sendRequest() Montoya API thread-safe
ui.updateStats() SwingUtilities.invokeLater()

The attemptBypass() method creates a new LogEntry for each bypass attempt and adds it to logEntries via ui.logEntry(), which internally uses SwingUtilities.invokeLater(). This ensures that UI updates (table insertion, statistics recalculation) occur on the EDT even when triggered from worker threads.


4.2 Request Modifier (RequestModifier.java)

The RequestModifier class implements pure, stateless functions for HTTP request transformation. Designed as a utility class with a private constructor, it provides atomic operations that compose into complex transformations through method chaining.

4.2.1 URL Parameter Encoding

Parameter encoding ensures that migrated parameters (from URL to body or body to URL) remain semantically equivalent and syntactically valid after transformation.

Encoding Implementation

The urlEncode() method wraps Java’s standard URLEncoder:

private static String urlEncode(String value) {
    try {
        return java.net.URLEncoder.encode(value, "UTF-8");
    } catch (Exception e) {
        return value;
    }
}

This implementation encodes:

The catch block returns the unencoded value on exception—a defensive fallback that preserves data at the cost of potential syntax errors in the transformed request.

Encoding Scope

Encoding is applied during both GET→POST and POST→GET migrations:

// GET→POST: URL parameters → body
body.append(urlEncode(param.name())).append("=").append(urlEncode(param.value()));

// POST→GET: Body parameters → URL query string
queryString.append(urlEncode(param.name())).append("=").append(urlEncode(param.value()));

This ensures that parameters containing spaces, ampersands, equals signs, or Unicode characters are correctly represented in their new location.

4.2.2 Body to Query String Conversion

The POST→GET conversion migrates body parameters to the URL query string, reversing the typical GET→POST direction.

Conversion Logic

private static HttpRequest convertPostToGet(HttpRequest request) {
    List<ParsedHttpParameter> bodyParams = request.parameters().stream()
        .filter(p -> p.type() == HttpParameterType.BODY)
        .collect(Collectors.toList());
    
    StringBuilder queryString = new StringBuilder();
    for (int i = 0; i < bodyParams.size(); i++) {
        ParsedHttpParameter param = bodyParams.get(i);
        if (i > 0) queryString.append("&");
        queryString.append(urlEncode(param.name())).append("=").append(urlEncode(param.value()));
    }
    
    HttpRequest modified = request.withMethod("GET");
    if (queryString.length() > 0) {
        String url = modified.url();
        String separator = url.contains("?") ? "&" : "?";
        modified = modified.withPath(url + separator + queryString);
    }
    
    modified = modified.withBody("");
    modified = removeHeader(modified, "Content-Type");
    return modified;
}

Key Implementation Details

Table

Aspect Implementation Rationale
Parameter source HttpParameterType.BODY Montoya API distinguishes URL vs body parameters
Query separator ? for first param, & for subsequent Standard URL syntax
Body clearing withBody("") GET requests should not have bodies per HTTP/1.1 RFC 7231
Content-Type removal removeHeader("Content-Type") GET requests typically omit Content-Type

The withPath(url + separator + queryString) call modifies the request path, which Montoya interprets as the complete request target including query string. This is distinct from withUrl(), which would replace the entire URL including host and scheme.

4.2.3 Header Addition/Removal Primitives

The header manipulation primitives provide the foundation for all header-based bypass techniques.

Header Removal

public static HttpRequest removeHeader(HttpRequest request, String headerName) {
    return request.withRemovedHeader(headerName);
}

The withRemovedHeader() method removes all headers matching the specified name, case-insensitively. This is critical for correct operation: HTTP header names are case-insensitive per RFC 7230, and applications may use Cookie, cookie, or COOKIE interchangeably.

Header Presence Check

private static boolean hasHeader(HttpRequest request, String headerName) {
    return request.headers().stream()
        .anyMatch(h -> h.name().equalsIgnoreCase(headerName));
}

This check is used during GET→POST conversion to determine whether Content-Type should be added:

if (!hasHeader(modified, "Content-Type")) {
    modified = modified.withAddedHeader("Content-Type", "application/x-www-form-urlencoded");
}

Method Switching Composition

The switchMethod() method composes the conversion primitives:

public static HttpRequest switchMethod(HttpRequest request) {
    String method = request.method();
    if (method.equalsIgnoreCase("GET")) {
        return convertGetToPost(request);
    } else if (method.equalsIgnoreCase("POST")) {
        return convertPostToGet(request);
    }
    return request;
}

The method returns the unmodified request for non-GET/POST methods (PUT, DELETE, PATCH, etc.), as these methods typically do not exhibit the GET/POST access control discrepancies that the technique targets.


4.3 Loop Prevention System

The loop prevention system prevents infinite request recursion that would occur if bypass attempts triggered additional bypass attempts, which trigger additional bypass attempts, ad infinitum. This is not merely a theoretical concern: Burp’s HTTP handler captures all HTTP traffic, including traffic generated by api.http().sendRequest() calls within the extension itself.

4.3.1 X-Burp-Processed Header Injection

The loop prevention mechanism injects a sentinel header into all modified requests:

public static final String LOOP_HEADER = "X-Burp-Processed";
public static final String LOOP_VALUE = "true";

This header serves as a marker that the request has already been processed by Sala7-Bypass and should not be re-processed if it re-enters the HTTP handler.

Injection Point

The header is injected in handleHttpRequestToBeSent():

if (loopPrevention) {
    modifiedRequest = modifiedRequest.withAddedHeader(LOOP_HEADER, LOOP_VALUE);
}

This injection occurs before any other modifications (method switching, auth removal), ensuring that even if a subsequent transformation generates a new request object, the loop header is present.

Detection Point

The header is checked at multiple points:

// In handleHttpRequestToBeSent()
if (loopPrevention && hasLoopHeader(requestToBeSent)) {
    return RequestToBeSentAction.continueWith(requestToBeSent);
}

// In triggerBypassAttempts()
if (!hasLoopHeader(request)) {
    String hash = bypassEngine.hashRequest(request);
    // ... trigger bypass ...
}

The early-exit in handleHttpRequestToBeSent() prevents re-processing of requests that already bear the marker. The check in triggerBypassAttempts() prevents bypass attempts on requests that were themselves generated as bypass attempts.

Header Removal in Bypass Attempts

Paradoxically, bypass attempts must remove the loop header before sending:

// In attemptBypass()
HttpRequest bypassRequest = originalRequest;
bypassRequest = RequestModifier.removeHeader(bypassRequest, LOOP_HEADER);

This removal is necessary because the bypass request is sent through api.http().sendRequest(), which routes through Burp’s HTTP stack and would be intercepted by handleHttpRequestToBeSent() if the header were present. The removal ensures the bypass request is treated as a fresh request, subject to normal processing (including re-injection of the loop header if it passes through the handler again).

4.3.2 Infinite Loop Detection

Beyond the loop header, the BypassEngine provides secondary loop detection through deduplication:

// In triggerBypassAttempts()
String hash = bypassEngine.hashRequest(request);
if (bypassEngine.shouldBypass(hash, maxRetries)) {
    bypassEngine.markProcessed(hash);
    // ... submit bypass attempt ...
}

If a bypass attempt somehow generates a request identical to one already processed (same method, URL, and body hash), the deduplication mechanism prevents re-triggering. This provides defense-in-depth against loop scenarios not caught by the header mechanism.

4.3.3 Request Chain Tracking

The current implementation provides single-generation loop prevention: a request processed by Sala7-Bypass is marked and not re-processed. However, it does not track multi-generation chains (request A triggers bypass B, which triggers bypass C). Such chains are theoretically possible if:

  1. A bypass attempt succeeds (200 response) but the response triggers another condition that Sala7-Bypass monitors
  2. A bypass attempt fails with a different blocking code (e.g., 403 bypass attempt returns 401, triggering auth bypass attempts)

The current architecture prevents multi-generation chains through two mechanisms:

Future enhancements could implement explicit chain tracking through a Map<String, Integer> tracking generation depth, with configurable maximum chain length.


5. User Interface & Experience


5.1 Tabbed Interface Design

Sala7-Bypass implements a five-tab interface within Burp Suite’s tabbed panel architecture, providing functional compartmentalization that mirrors the extension’s operational workflow: configuration, monitoring, advanced tuning, payload management, and attribution. The tabbed design follows Swing’s JTabbedPane component, integrated into Burp’s native UI through the Montoya API’s registerSuiteTab() method.

The interface initialization occurs on the Event Dispatch Thread (EDT), preventing UI construction from blocking Burp’s extension loading sequence:

SwingUtilities.invokeLater(() -> {
    ui = new BypassExtensionUI(this);
    api.userInterface().registerSuiteTab("Sala7-Bypass", ui.getUIComponent());
});

This EDT invocation is critical: Burp’s initialize() method executes on a background thread, and direct Swing component construction from non-EDT threads produces undefined behavior ranging from layout corruption to deadlock.

5.1.1 Controls Tab: Master Configuration

The Controls Tab serves as the primary configuration interface, organizing settings into four functional panels using GridBagLayout with TitledBorder groupings. This layout provides responsive resizing while maintaining visual hierarchy.

Master Controls Panel

JPanel masterPanel = new JPanel(new GridLayout(0, 2, 10, 5));
masterPanel.setBorder(new TitledBorder("Master Controls"));

chkEnabled = new JCheckBox("Extension Enabled", true);
chkScopeOnly = new JCheckBox("Process In-Scope Only", true);
chkLoopPrevention = new JCheckBox("Loop Prevention (X-Burp-Processed)", true);
chkDarkMode = new JCheckBox("Dark Mode", true);

Each checkbox binds directly to BurpExtender state through action listeners:

chkEnabled.addActionListener(e -> extender.setExtensionEnabled(chkEnabled.isSelected()));
chkScopeOnly.addActionListener(e -> extender.setScopeOnly(chkScopeOnly.isSelected()));
chkLoopPrevention.addActionListener(e -> extender.setLoopPrevention(chkLoopPrevention.isSelected()));
chkDarkMode.addActionListener(e -> {
    if (chkDarkMode.isSelected()) applyDarkTheme();
    else applyLightTheme();
});

The immediate state synchronization (no apply button required) reflects the penetration testing workflow where rapid configuration changes are common. The volatile modifier on BurpExtender fields ensures visibility across threads:

private volatile boolean extensionEnabled = true;
private volatile boolean scopeOnly = true;
private volatile boolean loopPrevention = true;

Request Modification Panel

JPanel modPanel = new JPanel(new GridLayout(0, 2, 10, 5));
modPanel.setBorder(new TitledBorder("Request Modification"));

chkMethodSwitch = new JCheckBox("Switch GET <-> POST");
chkRemoveAuth = new JCheckBox("Remove Authentication");
chkRemoveCookie = new JCheckBox("Remove Cookie Header");
chkRemoveAuthorization = new JCheckBox("Remove Authorization Header");

The hierarchical relationship between chkRemoveAuth and its subordinates (chkRemoveCookie, chkRemoveAuthorization) is implicit: chkRemoveAuth must be selected for the subordinate checkboxes to have effect, but the UI does not enforce this visually (no grey-out behavior). This design choice prioritizes flexibility over strict validation, allowing testers to pre-configure subordinate settings before enabling the parent toggle.

403/429 Bypass Engine Panel

JPanel bypassPanel = new JPanel(new GridLayout(0, 2, 10, 5));
bypassPanel.setBorder(new TitledBorder("403/429 Bypass Engine"));

chkDetect403 = new JCheckBox("Detect 403/429 Responses", true);
chkBypassHeaders = new JCheckBox("Inject Bypass Headers", true);
chkSingleBypass = new JCheckBox("Single Header Mode (vs Multiple)");
JSpinner spnMaxRetries = new JSpinner(new SpinnerNumberModel(3, 1, 20, 1));

The SpinnerNumberModel(3, 1, 20, 1) constrains retry counts to 1-20 with step size 1, preventing configuration errors that could generate excessive traffic. The JSpinner component provides both direct text entry and increment/decrement buttons, accommodating rapid adjustment during testing.

Filtering Options Panel

JPanel filterPanel = new JPanel(new GridLayout(0, 2, 10, 5));
filterPanel.setBorder(new TitledBorder("Filtering Options"));

chkAuthFilter = new JCheckBox("Auth-Based Filter (Cookie/Authorization)");
chkStatusFilter = new JCheckBox("HTTP Status Filter (200/302/403)");
chkContentTypeFilter = new JCheckBox("Content-Type Filter");
JComboBox<String> cmbContentType = new JComboBox<>(new String[]{"all", "json", "form-data"});

The JComboBox provides three content-type filter options, with “all” as default. The matchesContentType() method in BurpExtender implements the filtering logic:

private boolean matchesContentType(HttpRequest request) {
    if (allowedContentType.equals("all")) return true;
    String contentType = request.headers().stream()
        .filter(h -> h.name().equalsIgnoreCase("Content-Type"))
        .findFirst()
        .map(h -> h.value().toLowerCase())
        .orElse("");
    return switch (allowedContentType) {
        case "json" -> contentType.contains("application/json");
        case "form-data" -> contentType.contains("multipart/form-data") ||
                          contentType.contains("application/x-www-form-urlencoded");
        default -> true;
    };
}

The switch expression (Java 14+ feature) provides clean mapping between UI selection and filter logic.

5.1.2 Live Logs Tab: Real-Time Monitoring

The Live Logs Tab provides the operational heart of Sala7-Bypass, displaying intercepted requests, their modifications, responses, and bypass classifications in a continuously updating table. The tab implements a split-pane layout with the log table occupying the upper 70% and a detail text area the lower 30%.

Table Construction

logTableModel = new LogTableModel();
logTable = new JTable(logTableModel);
logTable.setFillsViewportHeight(true);
logTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
logTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
logTable.setRowHeight(24);

The LogTableModel extends AbstractTableModel, providing custom data binding between LogEntry objects and the Swing table. The seven-column structure reflects the complete request lifecycle:

Table

Column Index Width Renderer
Time 0 60 Default
URL 1 350 Default
Status 2 50 StatusCellRenderer
Method 3 60 Default
Action 4 90 ResultCellRenderer
Bypass Type 5 120 BypassTypeCellRenderer
Time(ms) 6 60 Default

Column Width Configuration

logTable.getColumnModel().getColumn(0).setPreferredWidth(60);   // Time
logTable.getColumnModel().getColumn(1).setPreferredWidth(350);  // URL
logTable.getColumnModel().getColumn(2).setPreferredWidth(50);   // Status
logTable.getColumnModel().getColumn(3).setPreferredWidth(60);   // Method
logTable.getColumnModel().getColumn(4).setPreferredWidth(90);   // Action
logTable.getColumnModel().getColumn(5).setPreferredWidth(120);  // Bypass Type
logTable.getColumnModel().getColumn(6).setPreferredWidth(60);   // Time(ms)

The URL column receives the greatest width allocation (350 pixels), reflecting the high information density of URLs in penetration testing workflows. The status and timing columns receive minimal width, as their content is inherently compact.

Split Pane Layout

JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tableScroll, textScroll);
splitPane.setResizeWeight(0.7);

The setResizeWeight(0.7) ensures that the table panel receives 70% of available vertical space during resizing, maintaining the primary focus on log visibility while preserving detail access.

5.1.3 Advanced Tab: Fine-Tuning

The Advanced Tab provides operational parameters that require less frequent adjustment than Controls Tab settings, organized into two primary sections: Advanced Settings and Bypass Attempt Settings.

Advanced Settings Panel

JPanel advancedPanel = new JPanel(new GridLayout(0, 2, 10, 5));
advancedPanel.setBorder(new TitledBorder("Advanced Settings"));

JLabel lblThreads = new JLabel("Concurrent Threads:");
JSpinner spnThreads = new JSpinner(new SpinnerNumberModel(5, 1, 50, 1));

JLabel lblDelay = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 100));

JCheckBox chkAutoStop = new JCheckBox("Auto-Stop on Success");

JLabel lblSoundPath = new JLabel("Sound File Path:");
JTextField txtSoundPath = new JTextField(extender.getSoundFilePath(), 30);
JButton btnBrowseSound = new JButton("Browse");

The thread count spinner (1-50) accommodates testing scenarios from conservative single-threaded operation (avoiding rate limit triggers) to aggressive high-concurrency testing (maximizing bypass discovery speed). The request delay spinner (0-10000ms, step 100ms) enables deliberate throttling for stealth testing or server protection.

The sound file configuration provides configurable success notification through WAV file playback:

btnBrowseSound.addActionListener(e -> {
    JFileChooser chooser = new JFileChooser();
    chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("WAV Files", "wav"));
    if (chooser.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
        txtSoundPath.setText(chooser.getSelectedFile().getAbsolutePath());
        extender.setSoundFilePath(chooser.getSelectedFile().getAbsolutePath());
    }
});

Bypass Attempt Settings Panel

The Bypass Attempt Settings panel provides configuration specific to automatic bypass attempts triggered by 403/429 detection:

JPanel bypassSettingsPanel = new JPanel();
bypassSettingsPanel.setLayout(new BoxLayout(bypassSettingsPanel, BoxLayout.Y_AXIS));
bypassSettingsPanel.setBorder(new TitledBorder("Bypass Attempt Settings"));
bypassSettingsPanel.setBackground(new Color(30, 30, 40));

JLabel lblBypassInfo = new JLabel("<html><i>These settings control what modifications are applied during automatic bypass attempts (when 403/429 is detected)</i></html>");
lblBypassInfo.setForeground(new Color(127, 255, 0)); // Chartreuse #7FFF00

JCheckBox chkBypassApplyMethodSwitch = new JCheckBox("Apply Method Switching in Bypass Attempts");
JCheckBox chkBypassApplyAuthRemoval = new JCheckBox("Apply Auth Removal in Bypass Attempts");

The BoxLayout.Y_AXIS arrangement ensures vertical stacking without excessive spacing, addressing the layout issue where GridLayout(0, 1) created disproportionate gaps between components.

5.1.4 Payloads Tab: Custom Header Management

The Payloads Tab provides a JTable-based interface for managing the bypass header pool, enabling testers to customize, extend, or reduce the default header set based on target-specific knowledge.

Table Model

String[] columns = {"Header Name", "Value", "Enabled"};
DefaultTableModel payloadsModel = new DefaultTableModel(columns, 0) {
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnIndex == 2 ? Boolean.class : String.class;
    }
    @Override
    public boolean isCellEditable(int row, int column) {
        return true;
    }
};

The custom table model overrides getColumnClass() to return Boolean.class for the “Enabled” column, causing JTable to render checkboxes rather than text fields. The isCellEditable() override enables in-place editing of all columns, supporting rapid payload customization.

Button Panel

Six buttons provide CRUD operations:

JButton btnAdd = createPayloadButton("Add");
JButton btnRemove = createPayloadButton("Remove");
JButton btnClear = createPayloadButton("Clear");
JButton btnReset = createPayloadButton("Reset");
JButton btnSave = createPayloadButton("Save");
JButton btnLoad = createPayloadButton("Load");

The loadDefaultPayloads() method populates the table with the complete default header pool, organized by category:

Table

Category Headers Values Count
IP Spoofing 39 headers 18 IP variants ~700 combinations
Port Manipulation 2 headers 8 ports 16 combinations
Scheme Override 3 headers 2 schemes 6 combinations
Path Rewrite 4 headers 7 paths 28 combinations
Method Override 2 headers 8 methods 16 combinations

The total default payload count exceeds 750 entries, loaded in batches of 50 to prevent UI freezing:

final int batchSize = 50;
for (int i = 0; i < allPayloads.size(); i += batchSize) {
    final int start = i;
    final int end = Math.min(i + batchSize, allPayloads.size());
    SwingUtilities.invokeLater(() -> {
        for (int j = start; j < end; j++) {
            String[] row = allPayloads.get(j);
            payloadsModel.addRow(new Object[]{row[0], row[1], Boolean.TRUE});
        }
    });
}

5.1.5 About Tab: Information & Attribution

The About Tab provides attribution, version information, and a personal dedication, implemented with GridBagLayout for centered content presentation:

JLabel lblTitle = new JLabel("Sala7-Bypass", SwingConstants.CENTER);
lblTitle.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 28));
lblTitle.setForeground(new Color(0, 200, 255));

JLabel lblDeveloper = new JLabel("This Tool Developed by H3X0S3 [ Mahmoud Salah ]", SwingConstants.CENTER);
lblDeveloper.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
lblDeveloper.setForeground(new Color(200, 200, 200));

JLabel lblDuaa = new JLabel("اللهم اجعل عملي كله صالحا واجعله لوجهك خالصا ولا تجعل لأحد فيه شيئا", SwingConstants.CENTER);
lblDuaa.setFont(new Font("Arial", Font.BOLD, 14));
lblDuaa.setForeground(new Color(255, 215, 0)); // Gold

The Arabic dedication reflects the developer’s personal faith and cultural identity, rendered with explicit Font specification to ensure proper Arabic script rendering across platforms.


5.2 Live Logging System

The Live Logging System provides real-time visibility into extension operations, capturing every intercepted request, applied modification, received response, and bypass classification. This transparency is not merely a convenience feature but a core design principle: penetration testers must understand exactly what the extension does to their traffic to interpret results correctly and report findings accurately.

5.2.1 Request/Response Capture

Each LogEntry captures the complete request/response lifecycle:

public class LogEntry {
    private final HttpRequest originalRequest;
    private final HttpRequest modifiedRequest;
    private HttpResponse originalResponse;
    private HttpResponse modifiedResponse;
    
    private int originalStatus;
    private int modifiedStatus;
    private long responseTime;
    private List<String> modifications;
}

The originalRequest and modifiedRequest fields are final, reflecting their immutability: once a request is intercepted and modified, the original and modified versions are fixed. Responses are mutable because they are populated asynchronously when responses arrive.

Capture Timing

Table

Event Data Captured Storage
Request interception Original request, modified request, modifications LogEntry constructor
Original response arrival Original status, response time, original response setOriginalStatus(), setOriginalResponse()
Modified response arrival Modified status, response time, modified response setModifiedStatus(), setModifiedResponse()
Bypass attempt completion Bypass status, headers used, details setBypassType(), setBypassHeadersUsed()

The LogTableModel provides data binding between LogEntry objects and the UI table:

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
    LogEntry entry = entries.get(rowIndex);
    return switch (columnIndex) {
        case 0 -> entry.getTimestamp();
        case 1 -> entry.getUrl();
        case 2 -> entry.getModifiedStatus() > 0 ? String.valueOf(entry.getModifiedStatus()) : "-";
        case 3 -> entry.getModifiedMethod();
        case 4 -> entry.getAction();
        case 5 -> entry.getBypassSummary();
        case 6 -> entry.getResponseTime() > 0 ? String.valueOf(entry.getResponseTime()) : "-";
        default -> null;
    };
}

The getBypassSummary() method provides formatted bypass type display:

public String getBypassSummary() {
    if (!isBypass || bypassType.equals("NONE")) return "-";
    
    StringBuilder sb = new StringBuilder();
    sb.append(bypassType);
    
    if (bypassType.equals("METHOD") && !originalMethod.equals(modifiedMethod)) {
        sb.append(" (").append(originalMethod).append("→").append(modifiedMethod).append(")");
    } else if (bypassType.equals("HEADER") && !bypassHeadersUsed.isEmpty()) {
        String firstHeader = bypassHeadersUsed.keySet().iterator().next();
        sb.append(" (").append(firstHeader).append(")");
    }
    // ... other types ...
    return sb.toString();
}

5.2.2 Color-Coded Status Indicators

The status column (index 2) employs a custom StatusCellRenderer that maps HTTP status codes to color-coded visual feedback:

private static class StatusCellRenderer extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
                                                   boolean isSelected, boolean hasFocus, int row, int column) {
        Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        
        if (value != null && !isSelected) {
            String statusStr = value.toString();
            if (!statusStr.equals("-")) {
                try {
                    int status = Integer.parseInt(statusStr);
                    if (status >= 200 && status < 300) {
                        c.setBackground(new Color(144, 238, 144)); // Light green
                        c.setForeground(new Color(0, 100, 0));       // Dark green
                    } else if (status == 302 || status == 301) {
                        c.setBackground(new Color(255, 200, 100)); // Orange
                        c.setForeground(new Color(150, 80, 0));    // Dark orange
                    } else if (status == 403 || status == 401 || status == 429) {
                        c.setBackground(new Color(255, 150, 150)); // Light red
                        c.setForeground(new Color(150, 0, 0));       // Dark red
                    }
                    // ... other ranges ...
                } catch (NumberFormatException e) {
                    c.setBackground(table.getBackground());
                }
            } else {
                c.setBackground(new Color(200, 200, 200)); // Grey for pending
                c.setForeground(Color.DARK_GRAY);
            }
        }
        setHorizontalAlignment(SwingConstants.CENTER);
        setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
        return c;
    }
}

Table

Status Range Background Foreground Semantic Meaning
200-299 Light green (#90EE90) Dark green (#006400) Success / Bypass achieved
301-302 Orange (#FFC864) Dark orange (#965000) Redirect / Partial success
401, 403, 429 Light red (#FF9696) Dark red (#960000) Blocked / Access denied
400-499 (other) Lighter red (#FFC8C8) Dark red (#960000) Client error
500+ Light red (#FFB4B4) Dark red (#960000) Server error
”-“ (pending) Grey (#C8C8C8) Dark grey Awaiting response

The color mapping follows established conventions: green for success, orange for redirection (often indicating partial bypass), red for blocking, and grey for pending states. The !isSelected guard prevents color override when the user selects a row, maintaining selection visibility.

5.2.3 Bypass Type Classification

The Action column (index 4) uses the ResultCellRenderer to provide semantic classification of request outcomes:

private static class ResultCellRenderer extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, ...) {
        // Retrieve the actual LogEntry for this row
        LogTableModel model = (LogTableModel) table.getModel();
        LogEntry entry = model.getEntry(row);
        boolean isRealBypass = (entry != null && entry.isRealBypass());
        
        String result = value.toString();
        boolean isBypassAttempt = result.contains("BYPASS");
        boolean isSuccess = result.contains("SUCCESS");
        boolean isFailed = result.contains("FAILED");
        boolean isBlocked = result.contains("BLOCKED");
        boolean isModified = result.contains("MODIFIED") || result.contains("METHOD_BYPASS") || result.contains("AUTH_BYPASS");
        
        if (isRealBypass || (isBypassAttempt && isSuccess)) {
            c.setBackground(new Color(34, 139, 34)); // Forest green
            c.setForeground(Color.WHITE);
            setFont(new Font(Font.SANS_SERIF, Font.BOLD, 13));
        } else if (isBypassAttempt && !isSuccess) {
            c.setBackground(new Color(255, 140, 0)); // Dark orange
            c.setForeground(new Color(100, 50, 0));
            setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
        } else if (isFailed) {
            c.setBackground(new Color(200, 50, 50)); // Red
            c.setForeground(Color.WHITE);
        }
        // ... other conditions ...
    }
}

The renderer distinguishes between isRealBypass() (verified transition from blocked to allowed) and mere isBypass() (any bypass attempt), providing visual confirmation of actual vulnerability exploitation versus mere attempt logging.

5.2.4 Response Time Metrics

Response time tracking provides performance context for bypass attempts, distinguishing between network latency, server processing time, and bypass technique efficiency. The Time(ms) column (index 6) displays response times with “-“ for pending requests:

case 6 -> entry.getResponseTime() > 0 ? String.valueOf(entry.getResponseTime()) : "-";

Response times are captured at two points:

long startTime = System.currentTimeMillis();
var httpResponse = api.http().sendRequest(bypassRequest);
long bypassTime = System.currentTimeMillis() - startTime;
bypassEntry.setResponseTime(bypassTime);

5.3 Statistics Dashboard

The Statistics Dashboard provides quantitative summary of extension operations, displayed as a persistent panel above the log table in the Live Logs tab.

5.3.1 Total Requests Counter

The total counter reflects all LogEntry objects in the system:

int total = logEntries.size();

This includes original requests, modified requests, and bypass attempts—each represented as a distinct LogEntry. A single user request that triggers a modified request and three bypass attempts contributes 5 to the total count.

5.3.2 Blocked Requests Detection

Blocked requests are identified by trueOriginalStatus matching blocking codes:

for (LogEntry entry : logEntries) {
    int origStatus = entry.getTrueOriginalStatus() != -1 
        ? entry.getTrueOriginalStatus() 
        : entry.getOriginalStatus();
    if (origStatus == 403 || origStatus == 429 || origStatus == 401) {
        blocked++;
    }
}

The trueOriginalStatus != -1 fallback ensures that entries awaiting original response arrival are still counted based on their preliminary status.

5.3.3 Bypass Success Rate Calculation

The success rate computes the percentage of blocked requests that were successfully bypassed:

double rate = blocked > 0 ? (bypassSuccess * 100.0 / blocked) : 0;

This metric answers the critical question: “Of all the requests that were blocked, what percentage did we successfully bypass?” A rate of 100% indicates complete access control circumvention; 0% indicates no bypassable vulnerabilities detected.

5.3.4 Per-Technique Breakdown (Method/Header/Auth/Rate-Limit/Combined)

The dashboard provides technique-specific counters:

int methodBypass = 0;
int headerBypass = 0;
int authBypass = 0;
int rateLimitBypass = 0;
int combinedBypass = 0;

for (LogEntry entry : logEntries) {
    if (entry.isRealBypass()) {
        String type = entry.getBypassType();
        switch (type) {
            case "METHOD" -> methodBypass++;
            case "HEADER" -> headerBypass++;
            case "AUTH" -> authBypass++;
            case "RATE_LIMIT" -> rateLimitBypass++;
            case "COMBINED" -> combinedBypass++;
        }
    }
}

The compact display format (“M:3 H:5 A:1”) conserves horizontal space while providing immediate technique distribution visibility.

5.3.5 Real-Time Percentage Computation

Statistics update synchronously with each log entry modification:

// In BurpExtender.handleHttpResponseReceived()
if (pendingIdx >= 0 && pendingIdx < logEntries.size()) {
    logEntries.set(pendingIdx, pendingEntry);
    ui.updateLogEntry(pendingIdx, pendingEntry);
    ui.updateStats();
}

The updateStats() method computes all statistics and updates UI labels through SwingUtilities.invokeLater():

public void updateStats() {
    // ... computation ...
    final String rateText = String.format("Rate: %.1f%%", rate);
    
    SwingUtilities.invokeLater(() -> {
        lblStatTotal.setText(totalText);
        lblStatBlocked.setText(blockedText);
        lblStatBypassSuccess.setText(successText);
        lblStatBypassRate.setText(rateText);
        // ... other labels ...
        
        if (hasSuccess) {
            lblStatBypassSuccess.setForeground(new Color(50, 255, 50));
            lblStatBypassRate.setForeground(new Color(50, 255, 50));
        } else {
            lblStatBypassSuccess.setForeground(Color.WHITE);
            lblStatBypassRate.setForeground(Color.WHITE);
        }
    });
}

The success state (hasSuccess) triggers green coloration, providing immediate visual feedback when bypasses are achieved.


5.4 Visual Feedback System

The Visual Feedback System extends beyond the log table to provide comprehensive color coding across all UI components, creating an intuitive visual language that enables rapid pattern recognition during high-volume testing.

5.4.1 Status Code Color Coding (200=Green, 302=Orange, 403/429=Red, 500=Dark Red)

The StatusCellRenderer implements the status code color mapping detailed in section 5.2.2. The specific color values are:

Table

Code Background Foreground Hex Values
200-299 Light green Dark green #90EE90, #006400
301-302 Orange Dark orange #FFC864, #965000
401, 403, 429 Light red Dark red #FF9696, #960000
400-499 (other) Lighter red Dark red #FFC8C8, #960000
500+ Light red Dark red #FFB4B4, #960000
Pending (“-“) Grey Dark grey #C8C8C8, #404040

5.4.2 Result Cell Highlighting (Success=Green, Failed=Red, Blocked=Dark Red)

The ResultCellRenderer provides semantic highlighting:

Table

State Background Foreground Font
Real Bypass Forest green (#228B22) White Bold 13pt
Bypass Attempt (failed) Dark orange (#FF8C00) Brown (#643200) Bold 12pt
Failed Red (#C83232) White Bold 12pt
Blocked Dark red (#8B0000) White Bold 12pt
Modified Steel blue (#4682B4) White Plain 12pt
Passthrough Table default Table default Plain 12pt

5.4.3 Bypass Type Badges (METHOD=Blue, HEADER=Green, AUTH=Purple, RATE_LIMIT=Orange, COMBINED=Dark Purple)

The BypassTypeCellRenderer provides type-specific color coding:

private static class BypassTypeCellRenderer extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, ...) {
        String bypassType = value.toString();
        
        if (bypassType.contains("METHOD")) {
            c.setBackground(new Color(100, 149, 237)); // Cornflower blue
            c.setForeground(Color.WHITE);
        } else if (bypassType.contains("HEADER")) {
            c.setBackground(new Color(60, 179, 113)); // Medium sea green
            c.setForeground(Color.WHITE);
        } else if (bypassType.contains("AUTH")) {
            c.setBackground(new Color(218, 112, 214)); // Orchid
            c.setForeground(Color.WHITE);
        } else if (bypassType.contains("RATE_LIMIT")) {
            c.setBackground(new Color(255, 165, 0)); // Orange
            c.setForeground(Color.WHITE);
        } else if (bypassType.contains("COMBINED")) {
            c.setBackground(new Color(148, 0, 211)); // Dark violet
            c.setForeground(Color.WHITE);
        } else {
            c.setBackground(table.getBackground());
            c.setForeground(new Color(150, 150, 150)); // Grey for NONE
        }
    }
}

Table

Type Color Hex Semantic Association
METHOD Cornflower blue #6495ED HTTP methods (blue = protocol)
HEADER Medium sea green #3CB371 Headers (green = metadata)
AUTH Orchid #DA70D6 Authentication (purple = security)
RATE_LIMIT Orange #FFA500 Rate limiting (orange = warning)
COMBINED Dark violet #9400D3 Combined (intense = complex)
NONE Grey #969696 No bypass (neutral)

5.4.4 Dark/Light Theme Support

The theme system provides global color scheme switching:

private void applyDarkTheme() {
    mainPanel.setBackground(new Color(30, 30, 40));
    if (logTextArea != null) {
        logTextArea.setBackground(new Color(25, 25, 35));
        logTextArea.setForeground(new Color(200, 200, 200));
    }
    SwingUtilities.updateComponentTreeUI(mainPanel);
}

private void applyLightTheme() {
    mainPanel.setBackground(Color.WHITE);
    if (logTextArea != null) {
        logTextArea.setBackground(Color.WHITE);
        logTextArea.setForeground(Color.BLACK);
    }
    SwingUtilities.updateComponentTreeUI(mainPanel);
}

The SwingUtilities.updateComponentTreeUI() call recursively updates all child components to reflect the new Look-and-Feel, though custom renderers maintain their explicit color settings regardless of theme.


5.5 Interactive Features

The interactive features transform the log table from passive display into an active analysis tool, enabling rapid investigation, tool integration, and data export.

5.5.1 Right-Click Context Menu

The context menu provides eight operations accessible through right-click on any log row:

private void createTablePopupMenu() {
    tablePopupMenu = new JPopupMenu();
    tablePopupMenu.setBackground(new Color(40, 40, 50));
    
    JMenuItem miViewDetails = new JMenuItem("View Details");
    JMenuItem miSendRepeater = new JMenuItem("Send to Repeater");
    JMenuItem miSendIntruder = new JMenuItem("Send to Intruder");
    JMenuItem miCopyURL = new JMenuItem("Copy URL");
    JMenuItem miCopyCurl = new JMenuItem("Copy as cURL");
    JMenuItem miCopyBypassInfo = new JMenuItem("Copy Bypass Info");
    JMenuItem miDelete = new JMenuItem("Delete Row");
    
    // ... action listeners ...
}

The menu is organized with visual separators grouping related operations:

5.5.2 Send to Repeater/Intruder Integration

Direct tool integration eliminates manual request reconstruction:

miSendRepeater.addActionListener(e -> {
    int row = logTable.getSelectedRow();
    if (row >= 0 && row < logEntries.size()) {
        LogEntry entry = logEntries.get(row);
        if (entry.getModifiedRequest() != null && extender.getApi() != null) {
            extender.getApi().repeater().sendToRepeater(entry.getModifiedRequest());
            logMessage("Sent to Repeater: " + entry.getUrl());
        }
    }
});

The sendToRepeater() call transfers the modified request—including all applied bypass headers and transformations—directly into Burp’s Repeater tool, preserving the exact request state that achieved bypass.

5.5.3 cURL Command Generation

The cURL generation reconstructs the modified request as a command-line executable:

private String generateCurlCommand(LogEntry entry) {
    StringBuilder curl = new StringBuilder();
    curl.append("curl -X ").append(entry.getModifiedMethod()).append(" ");
    
    HttpRequest req = entry.getModifiedRequest();
    if (req != null) {
        for (var header : req.headers()) {
            curl.append(" -H '").append(header.name()).append(": ").append(header.value()).append("'");
        }
        String body = req.bodyToString();
        if (body != null && !body.isEmpty()) {
            curl.append(" -d '").append(body.replace("'", "\'")).append("'");
        }
    }
    curl.append(" '").append(entry.getUrl()).append("'");
    return curl.toString();
}

This enables immediate command-line verification of bypass discoveries, independent of Burp Suite.

5.5.4 Enhanced Request/Response Viewer

The double-click activated viewer provides comprehensive request/response inspection:

logTable.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        int row = logTable.rowAtPoint(e.getPoint());
        if (row >= 0) {
            logTable.setRowSelectionInterval(row, row);
            if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
                showEnhancedViewer(row);
            }
        }
    }
});

The viewer implements a 1600×1000 pixel dialog with horizontal split pane:

JDialog dialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(mainPanel), title, true);
dialog.setSize(1600, 1000);

JSplitPane mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
mainSplit.setResizeWeight(0.5);

Left Panel: Request Inspection

Right Panel: Response Inspection

5.5.5 Diff View for Original vs Modified

The Diff View highlights structural differences between original and modified requests:

private JPanel createDiffPanel(String original, String modified) {
    JPanel panel = new JPanel(new GridLayout(1, 2, 5, 0));
    
    String[] origLines = original != null ? original.split("\n") : new String[0];
    String[] modLines = modified != null ? modified.split("\n") : new String[0];
    
    // Line-by-line comparison with +/- prefixing
    for (int i = 0; i < maxLines; i++) {
        String origLine = i < origLines.length ? origLines[i] : "";
        String modLine = i < modLines.length ? modLines[i] : "";
        
        if (origLine.equals(modLine)) {
            origDiff.append(" ").append(origLine).append("\n");
            modDiff.append(" ").append(modLine).append("\n");
        } else {
            if (!origLine.isEmpty()) origDiff.append("-").append(origLine).append("\n");
            if (!modLine.isEmpty()) modDiff.append("+").append(modLine).append("\n");
        }
    }
}

The diff output uses standard diff notation:

Color coding reinforces the diff semantics: removed lines display on red background, added lines on green.

5.5.6 Keyboard Shortcuts (Delete, Copy)

Keyboard accelerators provide rapid row manipulation without mouse interaction:

logTable.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        int row = logTable.getSelectedRow();
        if (row < 0) return;
        
        if (e.getKeyCode() == KeyEvent.VK_DELETE) {
            logEntries.remove(row);
            refreshTableFromEntries();
            updateStats();
            logMessage("Deleted row " + row);
        } else if (e.getKeyCode() == KeyEvent.VK_C && e.isControlDown()) {
            String url = logEntries.get(row).getUrl();
            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
                new StringSelection(url), null);
            logMessage("Copied URL: " + url);
        }
    }
});

Table

Shortcut Action Context
Delete Remove selected row Log table focus
Ctrl+C Copy URL to clipboard Log table focus

These shortcuts follow standard conventions, minimizing learning curve for users familiar with spreadsheet and table applications.


6. Filtering & Scope Management


6.1 Scope-Only Processing

The scope-only processing filter ensures that Sala7-Bypass operates exclusively on targets explicitly defined within Burp Suite’s target scope. This capability is fundamental to professional penetration testing workflows where testers must avoid unintended interaction with out-of-scope systems, preventing accidental testing of production environments, third-party services, or legally restricted domains.

Implementation

The scope check occurs as the second gate in handleHttpRequestToBeSent(), immediately following the extension enabled check:

if (scopeOnly && !api.scope().isInScope(requestToBeSent.url())) {
    return RequestToBeSentAction.continueWith(requestToBeSent);
}

The api.scope().isInScope() method delegates to Burp Suite’s internal scope management, which evaluates the request URL against the configured scope rules. Burp’s scope system supports:

Table

Scope Rule Type Example Matching Behavior
Simple domain example.com Matches domain and all subdomains
Wildcard domain *.example.com Matches all subdomains, not root
Exact URL https://example.com/api/v1 Matches exact path only
Regex pattern .*\.example\.com/.* Full regex matching
Protocol-specific https://example.com Protocol-sensitive matching

The isInScope() evaluation occurs synchronously on the HTTP dispatcher thread, with performance characteristics dependent on Burp’s internal scope rule evaluation engine. For typical scope configurations (10-50 rules), this evaluation is sub-millisecond and does not materially impact request throughput.

Configuration Interface

The scope filter is controlled through a simple checkbox in the Controls Tab:

chkScopeOnly = new JCheckBox("Process In-Scope Only", true);
chkScopeOnly.setToolTipText("Only process requests that are in Burp's target scope");
chkScopeOnly.addActionListener(e -> extender.setScopeOnly(chkScopeOnly.isSelected()));

The default true state reflects the principle that scope restriction should be opt-out rather than opt-in, preventing accidental out-of-scope processing by users who fail to explicitly enable the filter.

Operational Implications

When scope-only processing is active, out-of-scope requests pass through unmodified and unlogged. This creates a visibility gap: the extension provides no indication that requests were filtered, potentially confusing users who expect to see all traffic in the Live Logs tab. Future enhancements could implement an “Out-of-Scope” log category or counter to provide filtering transparency.

The scope check applies only to handleHttpRequestToBeSent(); bypass attempts triggered by in-scope requests may generate out-of-scope follow-up requests if the original request contains absolute URLs in parameters or headers. The extension does not currently implement recursive scope checking for bypass-generated requests, representing a potential enhancement for strict scope enforcement.


6.2 Authentication-Based Filtering

The authentication-based filter restricts processing to requests that contain authentication indicators, enabling focused testing of authenticated endpoints where access control bypass is most likely to yield meaningful security findings.

Implementation

The auth filter checks for the presence of Cookie or Authorization headers:

if (authFilter && !hasAuthIndicators(requestToBeSent)) {
    return RequestToBeSentAction.continueWith(requestToBeSent);
}

The hasAuthIndicators() method implements the check:

private boolean hasAuthIndicators(HttpRequest request) {
    return request.headers().stream().anyMatch(h ->
        h.name().equalsIgnoreCase("Cookie") || h.name().equalsIgnoreCase("Authorization")
    );
}

The anyMatch() short-circuit evaluation terminates at the first matching header, providing O(1) average-case performance for authenticated requests and O(n) worst-case for unauthenticated requests (where n = header count).

Configuration Interface

chkAuthFilter = new JCheckBox("Auth-Based Filter (Cookie/Authorization)");
chkAuthFilter.setToolTipText("Only process requests with Cookie or Authorization headers");
chkAuthFilter.addActionListener(e -> extender.setAuthFilter(chkAuthFilter.isSelected()));

Limitations and Considerations

The current implementation recognizes only two authentication indicators, omitting several common alternatives:

Table

Authentication Mechanism Header/Indicator Current Detection
Session cookies Cookie: session=... ✅ Yes
Bearer tokens Authorization: Bearer ... ✅ Yes
Basic authentication Authorization: Basic ... ✅ Yes
API keys (header) X-API-Key: ... ❌ No
JWT in custom header X-Access-Token: ... ❌ No
OAuth 2.0 tokens Authorization: Bearer ... ✅ Yes
Client certificates TLS handshake ❌ No (not header-based)
Custom auth schemes Authorization: Custom ... ✅ Yes (Authorization prefix)

Future enhancements could implement configurable auth indicator patterns, allowing testers to define custom headers or patterns specific to target applications.


6.3 Content-Type Filtering (JSON/Form-Data/All)

The content-type filter enables targeted testing of specific API payload types, concentrating bypass efforts on endpoints most likely to exhibit access control vulnerabilities.

Implementation

The matchesContentType() method implements three filter modes:

private boolean matchesContentType(HttpRequest request) {
    if (allowedContentType.equals("all")) return true;
    
    String contentType = request.headers().stream()
        .filter(h -> h.name().equalsIgnoreCase("Content-Type"))
        .findFirst()
        .map(h -> h.value().toLowerCase())
        .orElse("");
    
    return switch (allowedContentType) {
        case "json" -> contentType.contains("application/json");
        case "form-data" -> contentType.contains("multipart/form-data") ||
                           contentType.contains("application/x-www-form-urlencoded");
        default -> true;
    };
}

Filter Modes

Table

Mode Matches Typical Targets
all All requests General testing
json application/json REST APIs, modern web services
form-data multipart/form-data, application/x-www-form-urlencoded Traditional forms, file uploads

The contains() matching (rather than equals()) accommodates content-type variants that include charset or boundary parameters: application/json; charset=utf-8, multipart/form-data; boundary=----WebKitFormBoundary.

Configuration Interface

chkContentTypeFilter = new JCheckBox("Content-Type Filter");
cmbContentType = new JComboBox<>(new String[]{"all", "json", "form-data"});
cmbContentType.addActionListener(e -> extender.setAllowedContentType((String) cmbContentType.getSelectedItem()));

6.4 HTTP Status Filtering

The HTTP status filter provides log-level filtering for the Live Logs tab, enabling testers to focus analysis on specific response categories. Unlike the request-time filters (scope, auth, content-type), the status filter operates on the log table model, controlling display without affecting interception.

Implementation in Filter Dialog

The status filter appears in the custom filter dialog:

JComboBox<String> cmbStatus = new JComboBox<>(new String[]{"All", "200", "302", "403", "429", "500+"});

The “500+” option aggregates all server error codes (500-599), recognizing that 500-series errors often indicate successful bypass attempts that exposed internal application errors.

Filter Logic

if (!status.equals("All")) {
    int entryStatus = entry.getModifiedStatus();
    if (status.equals("500+") && entryStatus < 500) match = false;
    else if (!status.equals("500+") && entryStatus != Integer.parseInt(status)) match = false;
}

6.5 Custom Log Filtering Dialog

The filter dialog provides multi-criteria log filtering, enabling complex analysis queries across the accumulated request history.

Dialog Interface

private void showFilterDialog() {
    JDialog dialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(mainPanel), "Filter Logs", true);
    dialog.setSize(400, 350);
    
    JTextField txtSearch = new JTextField();
    JComboBox<String> cmbStatus = new JComboBox<>(new String[]{"All", "200", "302", "403", "429", "500+"});
    JComboBox<String> cmbResult = new JComboBox<>(new String[]{"All", "SUCCESS", "FAILED", "BYPASS", "MODIFIED", "BLOCKED", "PASSTHROUGH"});
    JComboBox<String> cmbBypassType = new JComboBox<>(new String[]{"All", "METHOD", "HEADER", "AUTH", "RATE_LIMIT", "COMBINED", "NONE"});
}

Filter Criteria

Table

Criterion Type Matching Logic
URL Search Substring Case-insensitive contains()
Status Code Exact or range Direct comparison or 500+ range
Result Action classification Action string or success state
Bypass Type Classification getBypassType() exact match

Filter Application

private void applyFilter(String search, String status, String result, String bypassType) {
    logTableModel.clear();
    for (LogEntry entry : logEntries) {
        boolean match = true;
        
        // URL substring match
        if (!search.isEmpty() && !entry.getUrl().toLowerCase().contains(search.toLowerCase())) {
            match = false;
        }
        
        // Status filter
        if (!status.equals("All")) {
            int entryStatus = entry.getModifiedStatus();
            if (status.equals("500+") && entryStatus < 500) match = false;
            else if (!status.equals("500+") && entryStatus != Integer.parseInt(status)) match = false;
        }
        
        // Result filter
        if (!result.equals("All")) {
            if (result.equals("SUCCESS") && !entry.isSuccess()) match = false;
            else if (result.equals("FAILED") && entry.isSuccess()) match = false;
            else if (result.equals("BYPASS") && !entry.isBypass()) match = false;
            else if (result.equals("MODIFIED") && !entry.getAction().equals("MODIFIED")) match = false;
            else if (result.equals("BLOCKED") && !entry.getAction().equals("BLOCKED")) match = false;
            else if (result.equals("PASSTHROUGH") && !entry.getAction().equals("PASSTHROUGH")) match = false;
        }
        
        // Bypass type filter
        if (!bypassType.equals("All")) {
            if (!entry.getBypassType().equals(bypassType)) match = false;
        }
        
        if (match) logTableModel.addEntry(entry);
    }
}

The filter creates a view-layer subset of the underlying logEntries list, preserving the complete history while enabling focused analysis. The “Clear Filter” button restores the full view by re-populating the table model from logEntries.


7. Notification & Alert System


7.1 System Tray Notifications

The system tray notification provides non-intrusive success alerts that persist beyond the Burp Suite window lifecycle, ensuring testers are notified of bypass discoveries even when focused on other applications.

Implementation

private void showSuccessNotification(LogEntry entry) {
    if (!entry.isRealBypass()) return;
    
    SwingUtilities.invokeLater(() -> {
        if (SystemTray.isSupported()) {
            try {
                SystemTray tray = SystemTray.getSystemTray();
                Image image = Toolkit.getDefaultToolkit().createImage(
                    getClass().getResource("/icon.png"));
                
                TrayIcon trayIcon = new TrayIcon(image, "Sala7-Bypass");
                trayIcon.setImageAutoSize(true);
                tray.add(trayIcon);
                
                trayIcon.displayMessage("Bypass Success!",
                    "URL: " + truncate(entry.getUrl(), 50) + 
                    "\nType: " + entry.getBypassType() + 
                    "\nStatus: " + entry.getModifiedStatus(),
                    TrayIcon.MessageType.INFO);
                
                tray.remove(trayIcon); // Clean up after display
            } catch (Exception ignored) {}
        }
        // ... JOptionPane fallback ...
    });
}

Platform Support

Table

Platform SystemTray Support Behavior
Windows ✅ Native Native notification balloons
macOS ✅ Native Native notification center
Linux (GNOME/KDE) ✅ Native Desktop notification daemon
Linux (headless) ❌ No Falls through to JOptionPane
WSL ⚠️ Variable Depends on Windows integration

The SystemTray.isSupported() guard prevents crashes on unsupported platforms. The temporary TrayIcon creation (added, displayed, then immediately removed) avoids persistent icon pollution of the system tray.

Limitations

The current implementation does not implement notification queuing or deduplication. Rapid successive bypasses (e.g., multiple endpoints on the same vulnerable application) generate overlapping notifications, potentially overwhelming the system tray. A notification queue with rate limiting would address this in future versions.


7.2 Popup Dialog Alerts

The popup dialog provides modal success notification within the Burp Suite window, ensuring visibility even when system tray notifications are disabled or missed.

Implementation

JOptionPane.showMessageDialog(null,
    notificationText,
    "Sala7-Bypass - Success! [" + entry.getBypassType() + "]",
    JOptionPane.INFORMATION_MESSAGE);

The notificationText is generated by LogEntry.getBypassNotificationText():

public String getBypassNotificationText() {
    StringBuilder sb = new StringBuilder();
    sb.append("🎉 BYPASS SUCCESS!\n\n");
    sb.append("URL: ").append(url).append("\n");
    sb.append("Status: ").append(modifiedStatus).append(" OK\n");
    sb.append("Type: ").append(bypassType).append("\n");
    
    if (bypassType.equals("METHOD")) {
        sb.append("Method Switch: ").append(originalMethod).append(" → ").append(modifiedMethod).append("\n");
    } else if (bypassType.equals("HEADER") && !bypassHeadersUsed.isEmpty()) {
        sb.append("Headers Used:\n");
        for (Map.Entry<String, String> entry : bypassHeadersUsed.entrySet()) {
            sb.append("  • ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
        }
    }
    // ... other types ...
    
    sb.append("Original Status: ").append(trueOriginalStatus).append("\n");
    sb.append("Response Time: ").append(responseTime).append("ms\n");
    return sb.toString();
}

Dialog Behavior

The JOptionPane.showMessageDialog() with null parent centers the dialog on the screen. This can be disruptive during active testing, as the modal dialog blocks interaction with Burp Suite until dismissed. Future enhancements could implement non-modal notifications or configurable notification preferences (tray-only, dialog-only, both, or none).


7.3 Custom Sound Alerts

The sound alert system provides auditory notification of bypass successes, enabling testers to monitor testing progress without continuous visual attention.

7.3.1 WAV File Support

The primary sound mechanism plays user-configurable WAV files:

private void playSuccessSound() {
    try {
        File soundFile = new File(soundFilePath);
        if (soundFile.exists()) {
            javax.sound.sampled.AudioInputStream audioStream =
                javax.sound.sampled.AudioSystem.getAudioInputStream(soundFile);
            javax.sound.sampled.Clip clip = javax.sound.sampled.AudioSystem.getClip();
            clip.open(audioStream);
            clip.start();
        } else {
            Toolkit.getDefaultToolkit().beep();
        }
    } catch (Exception e) {
        Toolkit.getDefaultToolkit().beep();
        api.logging().logToOutput("Sound alert failed: " + e.getMessage());
    }
}

The javax.sound.sampled API provides platform-independent WAV playback through the Java Sound API. Supported formats include:

The default sound file path is hardcoded to a Windows path:

private String soundFilePath = "C:\\Users\\H3X0S3-Sala7\\Desktop\\hunt\\Burp-Extension\\H3X0S3\\success.wav";

This default is non-portable and will fail on non-Windows systems, falling through to the beep fallback.

7.3.2 Fallback Beep

When WAV playback fails (file not found, unsupported format, audio system unavailable), the extension falls back to Toolkit.getDefaultToolkit().beep():

Toolkit.getDefaultToolkit().beep();

This generates the platform-default beep (Windows: system beep, macOS: system alert, Linux: terminal bell), ensuring some auditory feedback regardless of configuration.

7.3.3 Configurable Sound Path

The Advanced Tab provides sound file configuration:

JTextField txtSoundPath = new JTextField(extender.getSoundFilePath(), 30);
JButton btnBrowseSound = new JButton("Browse");

btnBrowseSound.addActionListener(e -> {
    JFileChooser chooser = new JFileChooser();
    chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("WAV Files", "wav"));
    if (chooser.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
        txtSoundPath.setText(chooser.getSelectedFile().getAbsolutePath());
        extender.setSoundFilePath(chooser.getSelectedFile().getAbsolutePath());
    }
});

The JFileChooser with WAV filter restricts selection to .wav files, though the playback code does not enforce this extension at runtime—users could theoretically select other AudioSystem-supported formats (AIFF, AU).


7.4 Success Highlighting in Logs

Beyond explicit notifications, the extension provides persistent visual highlighting of successful bypasses within the Live Logs table, creating a persistent record of discoveries that remains visible after notifications dismiss.

Color Highlighting

As detailed in section 5.4, successful bypass rows receive distinctive formatting:

Table

Element Success State Visual Treatment
Status column 200/302 Green background, dark green text
Action column “BYPASS” + success Forest green background, white bold text
Bypass Type column Any bypass type Type-specific badge color
Statistics labels bypassSuccess > 0 Green foreground color

Log Text Area Highlighting

The highlightSuccess() method appends emphasized text to the log text area:

public void highlightSuccess(HttpRequest request, String message) {
    SwingUtilities.invokeLater(() -> {
        logTextArea.append("*** " + message + " ***" + System.lineSeparator());
    });
}

This produces output like:

The asterisk wrapping and line separation create visual distinction from routine log entries.

Statistics Dashboard Emphasis

When bypassSuccess > 0, the statistics labels change to bright green:

if (hasSuccess) {
    lblStatBypassSuccess.setForeground(new Color(50, 255, 50)); // Bright green
    lblStatBypassRate.setForeground(new Color(50, 255, 50));
} else {
    lblStatBypassSuccess.setForeground(Color.WHITE);
    lblStatBypassRate.setForeground(Color.WHITE);
}

This color transition provides immediate visual confirmation of testing efficacy, transforming the dashboard from neutral white to success-indicating green upon first bypass discovery.


8. Data Export & Persistence


8.1 CSV Export

The CSV export functionality provides tabular data output compatible with spreadsheet applications, vulnerability management systems, and automated reporting pipelines. The implementation prioritizes human readability while maintaining machine parseability through proper escaping and consistent column ordering.

8.1.1 Column Structure

The CSV export generates a header row followed by one data row per LogEntry, with columns capturing the complete request lifecycle from interception through bypass classification:

private void exportToCsv(String fileName) {
    FileWriter writer = new FileWriter(fileName);
    writer.write("Time,URL,OriginalMethod,ModifiedMethod,OriginalStatus,ModifiedStatus," +
                 "Action,BypassType,Bypass,RealBypass,Success,ResponseTime,BypassDetails\n");
    
    for (LogEntry entry : logEntries) {
        writer.write(String.format("%s,\"%s\",%s,%s,%d,%d,%s,%s,%b,%b,%b,%d,\"%s\"%n",
            entry.getTimestamp(),
            entry.getUrl(),
            entry.getOriginalMethod(),
            entry.getModifiedMethod(),
            entry.getOriginalStatus(),
            entry.getModifiedStatus(),
            entry.getAction(),
            entry.getBypassType(),
            entry.isBypass(),
            entry.isRealBypass(),
            entry.isSuccess(),
            entry.getResponseTime(),
            escapeCsv(entry.getBypassDetails())
        ));
    }
    writer.close();
}

Column Definitions

Table

Column Data Type Example Description
Time String 14:32:17 Request interception timestamp (HH:mm:ss)
URL String https://example.com/admin Full request URL
OriginalMethod String GET HTTP method before modification
ModifiedMethod String POST HTTP method after modification
OriginalStatus Integer 403 Response status for original request
ModifiedStatus Integer 200 Response status for modified request
Action String BYPASS Classification: PASSTHROUGH, MODIFIED, BLOCKED, BYPASS, FAILED
BypassType String HEADER Technique: METHOD, HEADER, AUTH, RATE_LIMIT, COMBINED, NONE
Bypass Boolean true Whether any bypass attempt was made
RealBypass Boolean true Whether bypass achieved 403→200/302 transition
Success Boolean true Whether modified/bypass request succeeded (200/302)
ResponseTime Long 145 Response time in milliseconds
BypassDetails String Header bypass: {X-Forwarded-For=127.0.0.1} Human-readable bypass description

The column ordering reflects the temporal request lifecycle: temporal anchor (Time), target identification (URL), request transformation (OriginalMethod→ModifiedMethod), response analysis (OriginalStatus→ModifiedStatus), classification (Action, BypassType), and metadata (ResponseTime, BypassDetails).

8.1.2 Escaping Rules

CSV escaping addresses two primary injection vectors: comma-containing fields (URL query strings, header values) and quote-containing fields (JSON payloads, HTML responses).

URL Escaping

URLs frequently contain commas in query strings (?a=1,b=2), necessitating double-quote enclosure:

writer.write(String.format("%s,\"%s\",%s,%s,...",
    entry.getTimestamp(),
    entry.getUrl(),  // Enclosed in quotes
    entry.getOriginalMethod(),
    // ...
));

All URL values are unconditionally enclosed in double quotes, regardless of comma presence, ensuring consistent parsing.

BypassDetails Escaping

The BypassDetails field contains arbitrary text including quotes and newlines, requiring explicit escaping:

private String escapeCsv(String s) {
    if (s == null) return "";
    return s.replace("\"", "\"\"")      // Double existing quotes
            .replace("\n", " ")         // Normalize newlines
            .replace("\r", " ");        // Normalize carriage returns
}

The escaping rules implement RFC 4180 conventions:

Table

Character Escape Sequence Rationale
" "" CSV quote doubling per RFC 4180
\n   Newlines break row structure
\r   Carriage returns break row structure

The implementation does not enclose the escaped value in additional quotes, relying on the format string’s explicit quote enclosure: "%s,\"%s\"%n" wraps the escaped value in quotes at write time. This dual-layer approach—explicit escaping plus format-level enclosure—provides defense-in-depth against malformed output.

Limitations

The current implementation does not handle other CSV metacharacters:


8.2 JSON Export

The JSON export provides structured data output suitable for programmatic consumption, integration with vulnerability databases, and automated report generation. The implementation uses manual string construction rather than a JSON library, minimizing dependencies while accepting the complexity of proper escaping.

8.2.1 Schema Design

The JSON schema mirrors the CSV column structure while adding nested object support for complex fields:

private void exportToJson(String fileName) {
    StringBuilder json = new StringBuilder();
    json.append("[\n");
    
    for (int i = 0; i < logEntries.size(); i++) {
        LogEntry entry = logEntries.get(i);
        json.append("  {\n");
        json.append("    \"timestamp\": \"").append(escapeJson(entry.getTimestamp())).append("\",\n");
        json.append("    \"url\": \"").append(escapeJson(entry.getUrl())).append("\",\n");
        json.append("    \"originalMethod\": \"").append(entry.getOriginalMethod()).append("\",\n");
        json.append("    \"modifiedMethod\": \"").append(entry.getModifiedMethod()).append("\",\n");
        json.append("    \"originalStatus\": ").append(entry.getOriginalStatus()).append(",\n");
        json.append("    \"modifiedStatus\": ").append(entry.getModifiedStatus()).append(",\n");
        json.append("    \"action\": \"").append(entry.getAction()).append("\",\n");
        json.append("    \"bypassType\": \"").append(entry.getBypassType()).append("\",\n");
        json.append("    \"isBypass\": ").append(entry.isBypass()).append(",\n");
        json.append("    \"isRealBypass\": ").append(entry.isRealBypass()).append(",\n");
        json.append("    \"isSuccess\": ").append(entry.isSuccess()).append(",\n");
        json.append("    \"responseTime\": ").append(entry.getResponseTime()).append(",\n");
        json.append("    \"bypassDetails\": \"").append(escapeJson(entry.getBypassDetails())).append("\"\n");
        json.append("  }");
        if (i < logEntries.size() - 1) json.append(",");
        json.append("\n");
    }
    json.append("]");
    
    FileWriter writer = new FileWriter(fileName);
    writer.write(json.toString());
    writer.close();
}

Schema Structure

[
  {
    "timestamp": "14:32:17",
    "url": "https://example.com/admin",
    "originalMethod": "GET",
    "modifiedMethod": "POST",
    "originalStatus": 403,
    "modifiedStatus": 200,
    "action": "BYPASS",
    "bypassType": "METHOD",
    "isBypass": true,
    "isRealBypass": true,
    "isSuccess": true,
    "responseTime": 145,
    "bypassDetails": "Original: 403 -> Modified: 200 | GET -> POST | Mods: METHOD_SWITCH:GET->POST"
  }
]

The flat structure prioritizes simplicity over normalization, with each entry self-contained for independent analysis. Boolean values use JSON native true/false rather than string representations, enabling direct boolean parsing by consumers.

8.2.2 Nested Object Serialization

The current implementation uses flat fields for all data. Future schema enhancements could introduce nested objects:

{
  "request": {
    "original": { "method": "GET", "url": "..." },
    "modified": { "method": "POST", "url": "..." }
  },
  "response": {
    "original": { "status": 403 },
    "modified": { "status": 200 }
  },
  "bypass": {
    "type": "METHOD",
    "isReal": true,
    "headersUsed": { "X-Forwarded-For": "127.0.0.1" }
  }
}

The manual serialization approach would require significant additional escaping logic for nested structures, motivating migration to a JSON library (Jackson, Gson, org.json) for complex schema evolution.

JSON Escaping

private String escapeJson(String s) {
    if (s == null) return "";
    return s.replace("\\", "\\\\")    // Backslash escape
            .replace("\"", "\\\"")      // Quote escape
            .replace("\n", "\\n")       // Newline escape
            .replace("\r", "\\r");      // Carriage return escape
}

The escaping sequence is order-sensitive: backslash must be escaped before quote, as replace("\"", "\\\"") introduces new backslashes that would be double-escaped if backslash replacement occurred second.

Table

Character Escape Sequence JSON Significance
\ \\ Escape prefix
" \" String delimiter
\n \n Line break (allowed in JSON strings)
\r \r Carriage return (allowed in JSON strings)

The implementation does not escape other control characters (tab, form feed, backspace) or Unicode, which remain valid JSON string content but may cause issues with strict parsers.


8.3 Custom Payload Save/Load

The Payloads Tab provides persistent storage for custom bypass header configurations, enabling testers to save discovered techniques, share configurations across teams, and maintain target-specific payload sets.

8.3.1 JSON-Based Payload Storage

The payload storage format uses a simple JSON array of objects, with each object representing a single header-value pair and its enabled state:

[
  {
    "name": "X-Forwarded-For",
    "value": "127.0.0.1",
    "enabled": true
  },
  {
    "name": "X-Custom-Header",
    "value": "bypass",
    "enabled": false
  }
]

Save Implementation

private void savePayloadsToFile() {
    JFileChooser chooser = new JFileChooser();
    chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("JSON Files", "json"));
    
    if (chooser.showSaveDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
        StringBuilder json = new StringBuilder();
        json.append("[\n");
        
        for (int i = 0; i < payloadsModel.getRowCount(); i++) {
            json.append("  {\n");
            json.append("    \"name\": \"").append(escapeJson((String) payloadsModel.getValueAt(i, 0))).append("\",\n");
            json.append("    \"value\": \"").append(escapeJson((String) payloadsModel.getValueAt(i, 1))).append("\",\n");
            json.append("    \"enabled\": ").append(payloadsModel.getValueAt(i, 2)).append("\n");
            json.append("  }");
            if (i < payloadsModel.getRowCount() - 1) json.append(",");
            json.append("\n");
        }
        json.append("]");
        
        FileWriter writer = new FileWriter(chooser.getSelectedFile());
        writer.write(json.toString());
        writer.close();
    }
}

The save operation iterates the DefaultTableModel directly, serializing the current table state including user modifications, additions, and deletions.

8.3.2 Batch Import/Export

The load implementation parses JSON files through manual string manipulation, reconstructing the table model:

private void loadPayloadsFromFile() {
    JFileChooser chooser = new JFileChooser();
    chooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("JSON Files", "json"));
    
    if (chooser.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
        java.nio.file.Path path = chooser.getSelectedFile().toPath();
        String fileContent = new String(java.nio.file.Files.readAllBytes(path));
        
        payloadsModel.setRowCount(0); // Clear existing
        
        fileContent = fileContent.trim();
        if (fileContent.startsWith("[") && fileContent.endsWith("]")) {
            fileContent = fileContent.substring(1, fileContent.length() - 1).trim();
            
            if (!fileContent.isEmpty()) {
                String[] objects = fileContent.split("}\\s*,\\s*\\{");
                
                for (String obj : objects) {
                    obj = obj.replace("{", "").replace("}", "").trim();
                    if (obj.isEmpty()) continue;
                    
                    String name = "";
                    String value = "";
                    boolean enabled = true;
                    
                    String[] pairs = obj.split(",");
                    for (String pair : pairs) {
                        String[] kv = pair.split(":", 2);
                        if (kv.length == 2) {
                            String key = kv[0].trim().replace("\"", "").replace("'", "");
                            String val = kv[1].trim().replace("\"", "").replace("'", "");
                            
                            if (key.equals("name")) name = val;
                            else if (key.equals("value")) value = val;
                            else if (key.equals("enabled")) enabled = Boolean.parseBoolean(val);
                        }
                    }
                    
                    if (!name.isEmpty()) {
                        payloadsModel.addRow(new Object[]{name, value, enabled});
                    }
                }
            }
        }
    }
}

Parser Limitations

The manual parser exhibits several fragility points:

Table

Issue Cause Impact
Nested braces in values split("}\\s*,\\s*\\{") breaks Crashes on JSON with nested objects
Colons in values split(":", 2) truncates Corrupts header values containing colons
Escaped quotes replace("\"", "") removes Loses quote-containing values
Boolean string variants Only "true"/"false" Fails on "True", "TRUE", "1"

These limitations motivate migration to a proper JSON parser for production reliability. The current implementation suffices for the simple flat structure but would fail on edge cases.

Reset Functionality

The “Reset” button restores the default payload pool, enabling recovery from corrupted configurations or return to baseline after experimentation:

java Copy

btnReset.addActionListener(e -> loadDefaultPayloads());

The loadDefaultPayloads() method programmatically generates the complete default header set, organized by category (IP headers, port headers, scheme headers, URL headers, method headers), and populates the table in batches of 50 rows to prevent UI freezing during large insertions.


9. Configuration & Customization


9.1 Master Controls

The master controls provide global operational parameters that govern the extension’s fundamental behavior, organized in the Controls Tab’s “Master Controls” panel.

9.1.1 Extension Enable/Disable

The enable toggle provides immediate operational control without requiring Burp extension reload:

chkEnabled = new JCheckBox("Extension Enabled", true);
chkEnabled.setToolTipText("Enable/disable the extension processing");
chkEnabled.addActionListener(e -> extender.setExtensionEnabled(chkEnabled.isSelected()));

The volatile boolean extensionEnabled field controls all interception points:

@Override
public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
    if (!extensionEnabled) {
        return RequestToBeSentAction.continueWith(requestToBeSent);
    }
    // ... processing ...
}

@Override
public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
    if (!extensionEnabled) {
        return ResponseReceivedAction.continueWith(responseReceived);
    }
    // ... processing ...
}

The early-exit pattern ensures minimal performance impact when disabled: a single boolean check and immediate return, with no object allocation or method invocation overhead.

9.1.2 Scope Restriction

The scope toggle restricts processing to Burp’s configured target scope, preventing accidental testing of out-of-scope systems:

chkScopeOnly = new JCheckBox("Process In-Scope Only", true);
chkScopeOnly.setToolTipText("Only process requests that are in Burp's target scope");
chkScopeOnly.addActionListener(e -> extender.setScopeOnly(chkScopeOnly.isSelected()));

The api.scope().isInScope() evaluation occurs after the enabled check, ensuring scope evaluation overhead is avoided entirely when the extension is disabled.

9.1.3 Loop Prevention Toggle

The loop prevention toggle controls the X-Burp-Processed header injection and detection:

chkLoopPrevention = new JCheckBox("Loop Prevention (X-Burp-Processed)", true);
chkLoopPrevention.setToolTipText("Add X-Burp-Processed header to prevent infinite loops");
chkLoopPrevention.addActionListener(e -> extender.setLoopPrevention(chkLoopPrevention.isSelected()));

Disabling loop prevention is generally inadvisable but may be necessary for testing scenarios where the header itself triggers server-side filtering or when debugging extension behavior through recursive self-testing.

9.1.4 Theme Selection (Dark/Light)

The theme toggle switches between dark and light color schemes:

chkDarkMode = new JCheckBox("Dark Mode", true);
chkDarkMode.setToolTipText("Toggle dark/light theme");
chkDarkMode.addActionListener(e -> {
    if (chkDarkMode.isSelected()) applyDarkTheme();
    else applyLightTheme();
});

The theme methods apply colors to the main panel and log text area:

private void applyDarkTheme() {
    mainPanel.setBackground(new Color(30, 30, 40));
    if (logTextArea != null) {
        logTextArea.setBackground(new Color(25, 25, 35));
        logTextArea.setForeground(new Color(200, 200, 200));
    }
    SwingUtilities.updateComponentTreeUI(mainPanel);
}

private void applyLightTheme() {
    mainPanel.setBackground(Color.WHITE);
    if (logTextArea != null) {
        logTextArea.setBackground(Color.WHITE);
        logTextArea.setForeground(Color.BLACK);
    }
    SwingUtilities.updateComponentTreeUI(mainPanel);
}

The SwingUtilities.updateComponentTreeUI() call recursively updates all child components to reflect the new Look-and-Feel, though custom renderers maintain their explicit color settings regardless of theme. This creates a hybrid theme system: native components follow the Look-and-Feel, while custom-rendered table cells maintain fixed colors.


9.2 Request Modification Controls

The request modification controls govern transformations applied to intercepted requests before server transmission.

9.2.1 Method Switching Toggle

The method switching toggle enables GET↔POST conversion:

chkMethodSwitch = new JCheckBox("Switch GET <-> POST");
chkMethodSwitch.setToolTipText("Automatically switch between GET and POST methods");
chkMethodSwitch.addActionListener(e -> extender.setMethodSwitching(chkMethodSwitch.isSelected()));

When enabled, all intercepted GET requests are converted to POST (with URL parameters migrated to body), and all POST requests are converted to GET (with body parameters migrated to query string). Non-GET/POST methods (PUT, DELETE, PATCH) pass through unmodified.

9.2.2 Auth Removal (Cookie/Authorization)

The authentication removal controls provide selective header stripping:

chkRemoveAuth = new JCheckBox("Remove Authentication");
chkRemoveAuth.setToolTipText("Remove authentication headers from requests");

chkRemoveCookie = new JCheckBox("Remove Cookie Header");
chkRemoveCookie.setToolTipText("Remove Cookie header from requests");

chkRemoveAuthorization = new JCheckBox("Remove Authorization Header");
chkRemoveAuthorization.setToolTipText("Remove Authorization header from requests");

The hierarchical relationship requires chkRemoveAuth to be selected for subordinate toggles to have effect, but the UI does not enforce this visually. The implementation checks removeAuth before evaluating subordinate flags:

if (removeAuth) {
    if (removeCookie) {
        modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Cookie");
    }
    if (removeAuthorization) {
        modifiedRequest = RequestModifier.removeHeader(modifiedRequest, "Authorization");
    }
}

This structure enables pre-configuration: a tester can enable removeCookie and removeAuthorization while keeping removeAuth disabled, then rapidly enable auth removal by checking removeAuth without reconfiguring subordinates.


9.3 Bypass Engine Controls

The bypass engine controls govern automatic bypass attempt behavior triggered by 403/401/429 response detection.

9.3.1 403/429 Detection Toggle

The detection toggle enables automatic bypass triggering:

chkDetect403 = new JCheckBox("Detect 403/429 Responses", true);
chkDetect403.setToolTipText("Automatically detect 403 Forbidden and 429 Too Many Requests");
chkDetect403.addActionListener(e -> extender.setDetect403(chkDetect403.isSelected()));

When disabled, blocked responses are logged but no automatic bypass attempts are initiated. This is useful for passive monitoring or when manual analysis is preferred over automated response.

9.3.2 Header Injection Toggle

The header injection toggle controls whether bypass attempts include header-based techniques:

chkBypassHeaders = new JCheckBox("Inject Bypass Headers", true);
chkBypassHeaders.setToolTipText("Inject bypass headers (X-Forwarded-For, X-Real-IP, etc.)");
chkBypassHeaders.addActionListener(e -> extender.setBypassHeaders(chkBypassHeaders.isSelected()));

When disabled, bypass attempts rely solely on method switching and auth removal (if configured), excluding the 40+ header injection vectors from the attempt pool.

9.3.3 Single vs Multiple Header Mode

The single header mode restricts bypass attempts to one header at a time:

chkSingleBypass = new JCheckBox("Single Header Mode (vs Multiple)");
chkSingleBypass.setToolTipText("Use only one bypass header at a time");
chkSingleBypass.addActionListener(e -> extender.setSingleBypassMode(chkSingleBypass.isSelected()));

In single mode, only the first header from BYPASS_HEADERS_POOL is attempted per retry cycle. In multiple mode (default), all headers are attempted sequentially. Single mode reduces request volume and server load, while multiple mode maximizes bypass discovery probability.

9.3.4 Max Retries Configuration (1-20)

The max retries spinner controls attempt limits per unique request:

JSpinner spnMaxRetries = new JSpinner(new SpinnerNumberModel(3, 1, 20, 1));
spnMaxRetries.setToolTipText("Maximum bypass attempts per request");
spnMaxRetries.addChangeListener(e -> extender.setMaxRetries((Integer) spnMaxRetries.getValue()));

The SpinnerNumberModel(3, 1, 20, 1) provides:

This range accommodates conservative testing (1 attempt, minimal server impact) through exhaustive enumeration (20 attempts, maximum coverage).


9.4 Advanced Settings

The advanced settings provide operational parameters for bypass execution tuning.

9.4.1 Concurrent Thread Count (1-50)

The thread count controls parallel bypass execution:

JSpinner spnThreads = new JSpinner(new SpinnerNumberModel(5, 1, 50, 1));
spnThreads.setToolTipText("Number of concurrent threads for bypass attempts");
spnThreads.addChangeListener(e -> extender.setThreadCount((Integer) spnThreads.getValue()));

Higher thread counts accelerate bypass discovery but increase server load and detection probability. The dynamic reconfiguration with graceful shutdown (section 4.1.4) enables runtime adjustment without extension reload.

9.4.2 Request Delay (0-10000ms)

The request delay introduces intentional latency between bypass attempts:

JSpinner spnDelay = new JSpinner(new SpinnerNumberModel(0, 0, 10000, 100));
spnDelay.setToolTipText("Delay between bypass attempts in milliseconds");
spnDelay.addChangeListener(e -> extender.setRequestDelay((Integer) spnDelay.getValue()));

The step size of 100ms enables rapid coarse adjustment, while direct text entry supports fine-grained values. Delays are applied per-attempt within the bypass loop:

if (requestDelay > 0) {
    try {
        Thread.sleep(requestDelay);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return;
    }
}

9.4.3 Auto-Stop on Success

The auto-stop toggle terminates bypass attempts after first success:

chkAutoStop = new JCheckBox("Auto-Stop on Success");
chkAutoStop.setToolTipText("Stop bypass attempts once a successful bypass is found");
chkAutoStop.addActionListener(e -> extender.setAutoStopOnSuccess(chkAutoStop.isSelected()));

When enabled, the bypass loop checks bypassEngine.hasSuccess() before each attempt and breaks immediately upon success detection. This optimizes for rapid discovery over comprehensive enumeration.

9.4.4 Sound File Configuration

The sound file path configures success notification audio:

JLabel lblSoundPath = new JLabel("Sound File Path:");
JTextField txtSoundPath = new JTextField(extender.getSoundFilePath(), 30);
JButton btnBrowseSound = new JButton("Browse");

The default path targets a Windows-specific location, requiring manual reconfiguration on other platforms. The browse button restricts selection to .wav files through FileNameExtensionFilter.

9.4.5 Bypass-Specific Method Switching

This toggle controls whether method switching is applied during automatic bypass attempts (distinct from the general method switching applied to all intercepted requests):

chkBypassApplyMethodSwitch = new JCheckBox("Apply Method Switching in Bypass Attempts");
chkBypassApplyMethodSwitch.setToolTipText("Also switch GET<<->POST during automatic bypass attempts");
chkBypassApplyMethodSwitch.addActionListener(e -> extender.setBypassApplyMethodSwitch(chkBypassApplyMethodSwitch.isSelected()));

This enables layered testing: general traffic may pass through unmodified, while blocked responses receive aggressive method switching in bypass attempts.

9.4.6 Bypass-Specific Auth Removal

Similarly, this toggle controls auth removal in bypass attempts:

chkBypassApplyAuthRemoval = new JCheckBox("Apply Auth Removal in Bypass Attempts");
chkBypassApplyAuthRemoval.setToolTipText("Also remove Cookie/Authorization during automatic bypass attempts");
chkBypassApplyAuthRemoval.addActionListener(e -> extender.setBypassApplyAuthRemoval(chkBypassApplyAuthRemoval.isSelected()));

The separation between general auth removal and bypass-specific auth removal enables nuanced testing strategies, such as maintaining authentication for general requests while testing auth-bypass vectors only against blocked endpoints.


10. Testing & Validation


10.1 Custom Vulnerable Application (Sala7_bypass_lab.py)

The Sala7_bypass_lab.py application provides a controlled testing environment for validating Sala7-Bypass functionality across all bypass technique categories. Built on Flask, the application implements intentional vulnerabilities that mirror real-world access control flaws, enabling systematic verification of extension behavior.

10.1.1 IP-Based Restriction Endpoint

The /admin/dashboard endpoint implements IP-based access control with intentional header trust vulnerabilities:

@app.route('/admin/dashboard')
@log_request
def admin_dashboard():
    client_ip = get_client_ip()
    
    # Check bypass headers first (vulnerable logic)
    if check_ip_bypass():
        request_stats['bypassed'] += 1
        return jsonify({
            "status": "BYPASS SUCCESS!",
            "message": "Admin access granted via IP spoofing",
            "your_ip": client_ip,
            "detected_headers": {h: request.headers.get(h) for h in request.headers.keys() 
                                if 'ip' in h.lower() or 'forward' in h.lower()},
            "flag": "Sala7{IP_BYPASS_MASTER}"
        }), 200
    
    # Normal check
    if client_ip not in ADMIN_IPS and not client_ip.startswith('127.'):
        request_stats['blocked'] += 1
        return jsonify({
            "error": "Forbidden",
            "message": "Access denied. Admin only from localhost.",
            "your_ip": client_ip,
            "hint": "Try adding X-Forwarded-For: 127.0.0.1"
        }), 403
    
    return jsonify({"status": "Admin access granted", "flag": "Sala7{ADMIN_ACCESS}"}), 200

The get_client_ip() function demonstrates the vulnerability pattern:

def get_client_ip():
    # Vulnerable: trusts X-Forwarded-For first
    forwarded = request.headers.get('X-Forwarded-For', '')
    if forwarded:
        return forwarded.split(',')[0].strip()
    
    real_ip = request.headers.get('X-Real-IP', '')
    if real_ip:
        return real_ip
    
    return request.remote_addr

The application trusts X-Forwarded-For over the actual connection IP (request.remote_addr), enabling trivial bypass through header injection.

10.1.2 Method-Based Restriction Endpoint

The /api/users endpoint restricts POST requests while allowing all other methods:

@app.route('/api/users', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'])
@log_request
def api_users():
    method = request.method
    
    if method == 'POST':
        request_stats['blocked'] += 1
        return jsonify({
            "error": "Forbidden",
            "message": "POST method is restricted. Use GET instead.",
            "your_method": method,
            "hint": "Try switching to GET or use X-HTTP-Method-Override"
        }), 403
    
    request_stats['bypassed'] += 1
    return jsonify({
        "status": "BYPASS SUCCESS!",
        "message": f"{method} method allowed",
        "users": ["admin", "user1", "user2", "h3x0s3"],
        "flag": "Sala7{METHOD_SWITCH_MASTER}"
    }), 200

This vulnerability mirrors common REST API implementations where framework-level routing enforces method constraints but custom access control logic checks only specific methods.

10.1.3 Path-Based Restriction Endpoint

The /admin/config endpoint implements path-based access control vulnerable to header override:

@app.route('/admin/config')
@log_request
def admin_config():
    original_url = request.headers.get('X-Original-URL', '')
    rewrite_url = request.headers.get('X-Rewrite-URL', '')
    
    if original_url or rewrite_url:
        request_stats['bypassed'] += 1
        return jsonify({
            "status": "BYPASS SUCCESS!",
            "message": "Path bypass worked!",
            "detected_headers": {
                "X-Original-URL": original_url,
                "X-Rewrite-URL": rewrite_url
            },
            "config": {"debug": True, "secret_key": "Sala7-SECRET-1337"},
            "flag": "Sala7{PATH_BYPASS_MASTER}"
        }), 200
    
    if not check_ip_bypass():
        request_stats['blocked'] += 1
        return jsonify({
            "error": "Forbidden",
            "message": "Direct access to /admin/config blocked",
            "hint": "Try X-Original-URL: /admin/config"
        }), 403

The endpoint checks X-Original-URL and X-Rewrite-URL headers before evaluating the actual request path, enabling path bypass through header injection.

10.1.4 Authentication Bypass Endpoint

The /api/secrets endpoint implements inverted authentication logic—granting access when authentication headers are absent:

@app.route('/api/secrets')
@log_request
def api_secrets():
    auth = request.headers.get('Authorization', '')
    cookie = request.headers.get('Cookie', '')
    
    if not auth and not cookie:
        request_stats['bypassed'] += 1
        return jsonify({
            "status": "BYPASS SUCCESS!",
            "message": "Anonymous access granted (no auth detected)",
            "secrets": ["DB_PASSWORD=root123", "API_KEY=sk-1337", "FLAG=Sala7{AUTH_BYPASS_MASTER}"],
            "flag": "Sala7{AUTH_BYPASS_MASTER}"
        }), 200
    
    if not auth.startswith('Bearer ') or 'valid_token' not in auth:
        request_stats['blocked'] += 1
        return jsonify({
            "error": "Unauthorized",
            "message": "Valid Bearer token required",
            "your_auth": auth[:20] + "..." if auth else "None",
            "hint": "Try removing Authorization and Cookie headers"
        }), 401

This “fail-open” pattern, while exaggerated for testing, mirrors real-world scenarios where legacy fallback mechanisms or debugging endpoints remain accessible through authentication bypass.

10.1.5 Rate Limiting Endpoint

The /api/limited endpoint implements per-IP rate limiting vulnerable to IP spoofing:

@app.route('/api/limited')
@log_request
def api_limited():
    client_ip = get_client_ip()
    
    forwarded_for = request.headers.get('X-Forwarded-For', '')
    if forwarded_for and ',' in forwarded_for:
        request_stats['bypassed'] += 1
        return jsonify({
            "status": "BYPASS SUCCESS!",
            "message": "Rate limit bypassed via IP rotation",
            "your_ip": client_ip,
            "forwarded_chain": forwarded_for,
            "data": "Limited resource accessed!",
            "flag": "Sala7{RATE_LIMIT_BYPASS_MASTER}"
        }), 200
    
    # Normal rate limiting
    now = time.time()
    key = f"rate_{client_ip}"
    
    if key not in rate_limit_store:
        rate_limit_store[key] = []
    
    rate_limit_store[key] = [t for t in rate_limit_store[key] if now - t < 60]
    
    if len(rate_limit_store[key]) >= 3:
        request_stats['blocked'] += 1
        return jsonify({
            "error": "Too Many Requests",
            "message": "Rate limit exceeded. Max 3 requests per minute.",
            "your_ip": client_ip,
            "requests_in_window": len(rate_limit_store[key]),
            "hint": "Try X-Forwarded-For with different IPs"
        }), 429

The rate limit bypass triggers on X-Forwarded-For values containing commas, simulating multi-hop proxy chains that bypass per-IP counting.

10.1.6 Combined Protection Endpoint

The /super-admin endpoint implements layered protections requiring multiple bypass techniques simultaneously:

@app.route('/super-admin', methods=['GET', 'POST', 'PUT', 'DELETE'])
@log_request
def super_admin():
    method = request.method
    client_ip = get_client_ip()
    auth = request.headers.get('Authorization', '')
    
    bypass_score = 0
    bypass_details = []
    
    if check_ip_bypass():
        bypass_score += 1
        bypass_details.append("IP spoofing detected")
    
    if method == 'GET':
        bypass_score += 1
        bypass_details.append("GET method bypass")
    
    if not auth and not request.headers.get('Cookie'):
        bypass_score += 1
        bypass_details.append("No auth headers")
    
    if bypass_score >= 2:
        request_stats['bypassed'] += 1
        return jsonify({
            "status": "COMBINED BYPASS SUCCESS!",
            "message": "Multiple bypass techniques detected and combined!",
            "bypass_score": bypass_score,
            "techniques": bypass_details,
            "your_ip": client_ip,
            "your_method": method,
            "super_admin_data": {
                "users": 1337,
                "servers": ["prod1", "prod2", "prod3"],
                "master_key": "Sala7{COMBINED_BYPASS_MASTER}"
            },
            "flag": "Sala7{COMBINED_BYPASS_MASTER}"
        }), 200
    
    request_stats['blocked'] += 1
    return jsonify({
        "error": "Forbidden",
        "message": "Super admin requires: Local IP + POST + Valid Auth",
        "your_ip": client_ip,
        "your_method": method,
        "your_auth": "Present" if auth else "Missing",
        "bypass_score": f"{bypass_score}/3",
        "hint": "Try: GET + X-Forwarded-For: 127.0.0.1 + No Auth"
    }), 403

The scoring system (requiring 2+ bypass techniques) validates Sala7-Bypass’s combined vector capability.


10.2 Test Scenarios & Expected Results

Table

Scenario Extension Configuration Expected Result Verification
IP Bypass Enable header injection, detect 403 200 response with Sala7{IP_BYPASS_MASTER} Log shows BYPASS, type HEADER
Method Switch Enable method switching 200 response with Sala7{METHOD_SWITCH_MASTER} Log shows BYPASS, type METHOD
Auth Removal Enable auth removal 200 response with Sala7{AUTH_BYPASS_MASTER} Log shows BYPASS, type AUTH
Rate Limit Enable header injection, send 4+ requests 200 response with Sala7{RATE_LIMIT_BYPASS_MASTER} Log shows BYPASS, type RATE_LIMIT
Combined Enable all modifications, detect 403 200 response with Sala7{COMBINED_BYPASS_MASTER} Log shows BYPASS, type COMBINED

10.3 Verification of Statistics Accuracy

Statistics accuracy is verified through controlled test execution:

  1. Reset state: Clear logs, reset rate limits
  2. Execute baseline: Send 5 requests to /admin/dashboard without bypass headers → 5 BLOCKED entries
  3. Execute bypass: Send 1 request with X-Forwarded-For: 127.0.0.1 → 1 BYPASS entry
  4. Verify dashboard: Total=6, Blocked=5, Bypass=1, Rate=20.0%

The trueOriginalStatus synchronization (Section 1.4 fix) ensures that pending entries receive correct original status for accurate rate calculation.


11. Technical Implementation Details


11.1 LogEntry Data Structure

The LogEntry class serves as the immutable data container for request lifecycle information, with selective mutability for bypass tracking fields populated asynchronously.

11.1.1 Core Fields (Timestamp, URL, Methods, Statuses)

private final String timestamp;
private final String url;
private final String originalMethod;
private final String modifiedMethod;
private int originalStatus;
private int modifiedStatus;
private String action;
private boolean isBypass;
private boolean isSuccess;
private long responseTime;

The final modifier on identity fields (timestamp, URL, methods) reflects their immutability: once a request is intercepted, these values are fixed. Status fields are mutable because they are populated when responses arrive.

11.1.2 Bypass Tracking Fields (Type, Headers Used, Details)

private String bypassType;
private Map<String, String> bypassHeadersUsed;
private String bypassDetails;
private int trueOriginalStatus;

The trueOriginalStatus field (Section 1.4) stores the actual original response status, distinct from originalStatus which may remain -1 until response arrival.

11.1.3 Request/Response Storage

private final HttpRequest originalRequest;
private final HttpRequest modifiedRequest;
private HttpResponse originalResponse;
private HttpResponse modifiedResponse;

HttpRequest instances are immutable (Montoya API design); HttpResponse instances are mutable references updated on response arrival.

11.1.4 isRealBypass() Algorithm

public boolean isRealBypass() {
    int checkOriginalStatus = (trueOriginalStatus != -1) ? trueOriginalStatus : originalStatus;
    boolean wasBlocked = (checkOriginalStatus == 403 || checkOriginalStatus == 429 || checkOriginalStatus == 401);
    boolean nowAllowed = (modifiedStatus == 200 || modifiedStatus == 302);
    return wasBlocked && nowAllowed && isBypass;
}

The algorithm implements the core bypass definition: blocked (403/401/429) → allowed (200/302) with bypass flag set.

11.1.5 getBypassSummary() Formatting

Provides compact bypass type display for table rendering:

public String getBypassSummary() {
    if (!isBypass || bypassType.equals("NONE")) return "-";
    StringBuilder sb = new StringBuilder();
    sb.append(bypassType);
    if (bypassType.equals("METHOD") && !originalMethod.equals(modifiedMethod)) {
        sb.append(" (").append(originalMethod).append("→").append(modifiedMethod).append(")");
    }
    // ... other type formatting ...
    return sb.toString();
}

11.1.6 getBypassNotificationText() Generation

Generates detailed notification text for popup display:

public String getBypassNotificationText() {
    StringBuilder sb = new StringBuilder();
    sb.append("🎉 BYPASS SUCCESS!\n\n");
    sb.append("URL: ").append(url).append("\n");
    sb.append("Status: ").append(modifiedStatus).append(" OK\n");
    // ... technique-specific details ...
    return sb.toString();
}

11.2 LogTableModel Implementation

11.2.1 Column Definitions (7 Columns)

private final String[] columnNames = {"Time", "URL", "Status", "Method", "Action", "Bypass Type", "Time(ms)"};

11.2.2 Data Binding & Event Firing

public void addEntry(LogEntry entry) {
    entries.add(entry);
    fireTableRowsInserted(entries.size() - 1, entries.size() - 1);
}

public void updateEntry(int row, LogEntry entry) {
    if (row >= 0 && row < entries.size()) {
        entries.set(row, entry);
        fireTableRowsUpdated(row, row);
    }
}

11.2.3 Statistics Computation Methods

public int getBypassSuccessCount() {
    int count = 0;
    for (LogEntry entry : entries) {
        if (entry.isRealBypass()) count++;
    }
    return count;
}

11.3 Pending Request Matching

11.3.1 Key Generation Algorithm

private String generatePendingKey(HttpRequest request, String prefix) {
    long timeWindow = System.currentTimeMillis() / 1000;
    String bodyHash = request.bodyToString() != null 
        ? String.valueOf(request.bodyToString().hashCode()) 
        : "0";
    return prefix + "|" + request.url() + "|" + request.method() + "|" + bodyHash + "|" + timeWindow;
}

11.3.2 Time Window Handling

The System.currentTimeMillis() / 1000 provides 1-second time buckets, preventing stale entries from accumulating while allowing matching of requests and responses within the same second.

11.3.3 Body Hash Inclusion

bodyToString().hashCode() distinguishes requests with identical URLs and methods but different bodies (e.g., POST requests with varying parameters).

11.3.4 Original vs Modified Key Distinguishing

The prefix parameter (“orig” vs “mod”) creates separate key namespaces, enabling independent tracking of original and modified request responses.


12. Security Considerations

12.1 Loop Prevention Mechanisms

The X-Burp-Processed header injection and detection prevents recursive request processing. The header is injected in handleHttpRequestToBeSent() and checked at multiple points before processing or bypass triggering.

12.2 Request Deduplication

The BypassEngine.hashRequest() method provides deduplication through composite hashing of method, URL, and body content hash.

12.3 Thread Safety

Table

Data Structure Mechanism
logEntries CopyOnWriteArrayList
pendingEntries ConcurrentHashMap
bypassEngine state ConcurrentHashMap operations

12.4 Resource Cleanup

The setThreadCount() method implements graceful ExecutorService shutdown with 5-second timeout and shutdownNow() fallback.


13. Future Enhancements & Roadmap

13.1 Additional Bypass Techniques

13.2 Machine Learning-Based Detection

13.3 Automated Report Generation

13.4 Collaborative Testing Features

13.5 Plugin API for Custom Bypass Modules


14. Conclusion

14.1 Summary of Capabilities

Sala7-Bypass provides comprehensive 403/401/429 bypass testing through integrated IP spoofing, method switching, authentication removal, path manipulation, and rate limiting bypass techniques, with real-time statistics, visual feedback, and export capabilities.

14.2 Impact on Web Application Security Testing

The extension transforms access control testing from manual, error-prone individual request manipulation into systematic, observable, and reportable automated testing, increasing bypass discovery probability while reducing testing time.

14.3 Acknowledgments & Dedication

Developed by H3X0S3 (Mahmoud Salah), Sala7-Bypass represents a contribution to the web application security testing community, dedicated to improving access control vulnerability detection through automation and transparency.


Appendices

A. Complete Source Code Listing

The complete source code for all six Java files (BurpExtender.java, BypassEngine.java, BypassExtensionUI.java, LogEntry.java, LogTableModel.java, RequestModifier.java) and the Python test application (Sala7_bypass_lab.py) is available in the GitHub repository: https://github.com/H3X0S3/Sala7-Bypass

B. API Reference (Burp Montoya API Integration Points)

Table

API Domain Key Methods Usage
api.http() registerHttpHandler(), sendRequest() Interception and manual execution
api.proxy() registerRequestHandler() Proxy-specific interception
api.userInterface() registerSuiteTab() UI embedding
api.repeater() sendToRepeater() Tool integration
api.intruder() sendToIntruder() Tool integration
api.logging() logToOutput() Debugging

C. Troubleshooting Guide

Table

Symptom Cause Solution
Statistics show 0% rate trueOriginalStatus not synced Apply Section 1.4 fix
No bypass attempts triggered detect403 disabled or scope mismatch Enable detection, verify scope
Extension not loading Java version incompatibility Use Java 17+ with Montoya API
UI freezing EDT blocking Check SwingUtilities.invokeLater() usage

D. Changelog & Version History

Table

Version Date Changes
1.0.0 2026-05 Initial release with 5 bypass types, statistics, UI