OAST Blind Vulnerability Detection

OAST Blind Vulnerability Detection

The following files were used as context for generating this wiki page:

Purpose and Scope

This page documents WSHawk's Out-of-Band Application Security Testing (OAST) system for detecting blind vulnerabilities that do not produce visible responses in WebSocket traffic. OAST enables detection of XXE (XML External Entity) and SSRF (Server-Side Request Forgery) vulnerabilities that would otherwise be missed by traditional pattern-matching techniques.

For general vulnerability detection methodology, see Vulnerability Detection Modules. For verification techniques that analyze in-band responses, see Analysis and Verification Modules. For Playwright-based XSS verification, see Playwright XSS Verification.

Sources: README.md:29-39, scanner_v2.py:22-23, scanner_v2.py:56-58


Understanding Blind Vulnerabilities

Blind vulnerabilities are security flaws where the exploit does not produce a visible response in the application's normal communication channel. In WebSocket security testing, this presents a unique challenge:

| Vulnerability Type | Blind Behavior | Detection Challenge | |-------------------|----------------|---------------------| | XXE (XML External Entity) | Server parses XML and fetches external resources, but doesn't return parsed content | No error messages, no reflected content in WebSocket response | | SSRF (Server-Side Request Forgery) | Server makes internal HTTP requests, but doesn't echo results back | Internal network access confirmed only through out-of-band channels | | DNS Exfiltration | Data is exfiltrated through DNS queries | No visible indicators in application layer |

Traditional vulnerability scanners rely on pattern matching in responses (e.g., looking for error messages, reflected payloads). This approach fails completely for blind vulnerabilities where the server processes the attack but provides no direct feedback.

OAST solves this by using an external callback server that the target application contacts when the vulnerability is triggered. WSHawk monitors this external server for incoming connections, confirming exploitation even when the WebSocket response is silent.

Sources: README.md:29, README.md:39


OAST Architecture in WSHawk

graph TB
    subgraph "WSHawk Scanner"
        Scanner["WSHawkV2<br/>(scanner_v2.py)"]
        XXETest["test_xxe_v2()<br/>Lines 399-453"]
        SSRFTest["test_ssrf_v2()<br/>Lines 495-540"]
    end
    
    subgraph "OAST System"
        OASTProvider["OASTProvider<br/>(oast_provider.py)"]
        SimpleOAST["SimpleOASTServer<br/>Local Callback Server"]
        InteractSH["Interact.sh Integration<br/>Cloud OAST Service"]
    end
    
    subgraph "Target WebSocket Server"
        WSServer["WebSocket Endpoint<br/>ws://target.com"]
        XMLParser["XML Parser<br/>(XXE vulnerable)"]
        URLFetcher["URL Fetcher<br/>(SSRF vulnerable)"]
    end
    
    subgraph "External Callback Channel"
        DNS["DNS Query<br/>callback.interact.sh"]
        HTTP["HTTP Request<br/>callback.interact.sh"]
    end
    
    Scanner -->|"1. Initialize"| OASTProvider
    OASTProvider -->|"2. Choose provider"| SimpleOAST
    OASTProvider -->|"2. OR use cloud"| InteractSH
    
    XXETest -->|"3. Generate XXE payload"| OASTProvider
    OASTProvider -->|"4. Return payload with<br/>callback URL"| XXETest
    
    XXETest -->|"5. Send malicious XML"| WSServer
    WSServer -->|"6. Parse XML"| XMLParser
    XMLParser -.->|"7. Fetch external entity"| DNS
    XMLParser -.->|"7. OR HTTP request"| HTTP
    
    DNS -.->|"8. DNS callback"| InteractSH
    HTTP -.->|"8. HTTP callback"| InteractSH
    
    OASTProvider -->|"9. Poll for callbacks"| InteractSH
    InteractSH -->|"10. Return interactions"| OASTProvider
    OASTProvider -->|"11. Confirm vulnerability"| Scanner
    
    SSRFTest -->|"Similar flow for SSRF"| OASTProvider

Key Components:

  • OASTProvider: Core OAST abstraction that manages callback servers and payload generation
  • SimpleOASTServer: Local HTTP server for testing environments without internet access
  • Interact.sh Integration: Cloud-based OAST service for real-world penetration testing
  • Callback Polling: Asynchronous polling mechanism to detect out-of-band connections

Sources: scanner_v2.py:22, scanner_v2.py:406-414, scanner_v2.py:419-423


OASTProvider Implementation

The OASTProvider class serves as the abstraction layer for OAST functionality in WSHawk:

classDiagram
    class OASTProvider {
        +use_interactsh: bool
        +custom_server: str
        +session_id: str
        +start() async
        +stop() async
        +generate_payload(type, identifier) str
        +poll_callbacks() async List
    }
    
    class SimpleOASTServer {
        +host: str
        +port: int
        +start() async
        +stop() async
        +get_callbacks() List
    }
    
    class InteractSHClient {
        +correlation_id: str
        +register() async
        +poll() async
        +deregister() async
    }
    
    OASTProvider --> SimpleOASTServer : "uses (local mode)"
    OASTProvider --> InteractSHClient : "uses (cloud mode)"

Configuration Options

The OAST provider is configured during WSHawkV2 initialization:

# scanner_v2.py:56-58
self.use_oast = True
self.oast_provider = None

The provider can operate in two modes:

  1. Local Mode (use_interactsh=False): Runs a simple HTTP server on localhost:8888

    • Used for: Testing environments, air-gapped networks
    • Limitation: Only detects HTTP callbacks, not DNS
  2. Cloud Mode (use_interactsh=True): Connects to interact.sh service

    • Used for: Production penetration testing, full DNS/HTTP coverage
    • Benefit: No firewall configuration required

Sources: scanner_v2.py:56-58, scanner_v2.py:409-410


Blind Vulnerability Detection Workflow

sequenceDiagram
    participant Scanner as "WSHawkV2"
    participant OAST as "OASTProvider"
    participant WS as "Target WebSocket"
    participant ExtResource as "External Resource"
    participant Callback as "Callback Server"
    
    Note over Scanner,Callback: Initialization Phase
    Scanner->>OAST: start()
    OAST->>Callback: Register session
    Callback-->>OAST: Session ID & callback URL
    
    Note over Scanner,Callback: Payload Generation Phase
    Scanner->>OAST: generate_payload('xxe', 'test1')
    OAST-->>Scanner: XXE payload with callback URL<br/>(e.g., http://test1.interact.sh)
    
    Note over Scanner,Callback: Injection Phase
    Scanner->>WS: Send malicious XML with XXE payload
    WS->>WS: Parse XML document
    WS->>ExtResource: Fetch external entity<br/>http://test1.interact.sh
    
    Note over Scanner,Callback: Out-of-Band Interaction
    ExtResource->>Callback: HTTP GET request to<br/>test1.interact.sh
    Callback->>Callback: Log interaction
    
    Note over Scanner,Callback: Verification Phase
    Scanner->>OAST: poll_callbacks()
    OAST->>Callback: Check for interactions
    Callback-->>OAST: List of interactions (DNS/HTTP logs)
    OAST-->>Scanner: Vulnerability confirmed!
    
    Scanner->>Scanner: Add to vulnerabilities list<br/>with HIGH confidence
    
    Note over Scanner,Callback: Cleanup Phase
    Scanner->>OAST: stop()
    OAST->>Callback: Deregister session

Workflow Stages

1. OAST Initialization scanner_v2.py:406-414

  • Occurs during XXE/SSRF test setup
  • Provider started only if use_oast=True
  • Falls back to pattern matching if OAST fails

2. Payload Generation scanner_v2.py:419-423

  • Dynamically creates payloads with unique callback identifiers
  • Each test gets a unique subdomain/path for correlation
  • Example: <!ENTITY xxe SYSTEM "http://test1.interact.sh">

3. WebSocket Injection

  • Payload wrapped in appropriate message format (JSON/XML)
  • Sent through WebSocket connection
  • No immediate response verification needed

4. Callback Polling

  • Asynchronous polling continues after payload injection
  • Timeout typically 5-10 seconds to allow for slow DNS propagation
  • Interactions matched to test identifiers

5. Vulnerability Confirmation

  • If callback received: HIGH confidence vulnerability
  • If no callback but error message: MEDIUM confidence
  • If no callback and no error: No vulnerability

Sources: scanner_v2.py:399-453


XXE Blind Detection Implementation

The XXE testing function demonstrates OAST integration:

Test Function Flow

flowchart TD
    Start["test_xxe_v2(ws)<br/>Line 399"]
    
    CheckOAST{"use_oast enabled?<br/>Line 407"}
    InitOAST["Initialize OASTProvider<br/>Lines 409-410"]
    OASTFail["Log error, disable OAST<br/>Lines 412-414"]
    
    LoadPayloads["Load XXE payloads<br/>Line 404"]
    
    LoopStart["For each payload<br/>Line 416"]
    
    GenOAST{"OAST available?<br/>Line 419"}
    UseOAST["Generate OAST payload<br/>Line 420"]
    UseStatic["Use static payload<br/>Line 423"]
    
    Inject["Send XML via WebSocket<br/>Lines 425-426"]
    WaitResp["Wait for WS response<br/>Lines 429-430"]
    
    CheckIndicators["Check XXE indicators<br/>Lines 432-433"]
    VulnDetected{"Indicators found?"}
    
    LogVuln["Log vulnerability HIGH<br/>Lines 434-444"]
    Continue["Continue to next payload"]
    
    Cleanup["Return results<br/>Line 453"]
    
    Start --> CheckOAST
    CheckOAST -->|Yes| InitOAST
    CheckOAST -->|No| LoadPayloads
    InitOAST -->|Success| LoadPayloads
    InitOAST -->|Error| OASTFail
    OASTFail --> LoadPayloads
    
    LoadPayloads --> LoopStart
    LoopStart --> GenOAST
    GenOAST -->|Yes| UseOAST
    GenOAST -->|No| UseStatic
    
    UseOAST --> Inject
    UseStatic --> Inject
    
    Inject --> WaitResp
    WaitResp --> CheckIndicators
    CheckIndicators --> VulnDetected
    
    VulnDetected -->|Yes| LogVuln
    VulnDetected -->|No| Continue
    
    LogVuln --> Continue
    Continue --> LoopStart
    LoopStart -->|Done| Cleanup

Code Example: OAST-Enhanced XXE Testing

The key section from test_xxe_v2():

OAST Provider Initialization:

# scanner_v2.py:407-414
if self.use_oast and not self.oast_provider:
    try:
        self.oast_provider = OASTProvider(use_interactsh=False, custom_server="localhost:8888")
        await self.oast_provider.start()
        Logger.info("OAST provider started for blind XXE detection")
    except Exception as e:
        Logger.error(f"OAST start failed: {e}")
        self.use_oast = False

Dynamic Payload Generation:

# scanner_v2.py:419-423
if self.use_oast and self.oast_provider:
    oast_payload = self.oast_provider.generate_payload('xxe', f'test{len(results)}')
    msg = json.dumps({"action": "parse_xml", "xml": oast_payload})
else:
    msg = json.dumps({"action": "parse_xml", "xml": payload})

Vulnerability Recording:

# scanner_v2.py:434-444
Logger.vuln(f"XXE [HIGH]: Entity processing detected")
self.vulnerabilities.append({
    'type': 'XML External Entity (XXE)',
    'severity': 'HIGH',
    'confidence': 'HIGH',
    'description': 'XXE vulnerability - external entities processed',
    'payload': payload[:80],
    'response_snippet': response[:200],
    'recommendation': 'Disable external entity processing'
})

Sources: scanner_v2.py:399-453


Interact.sh Integration

WSHawk integrates with interact.sh, an open-source OAST testing platform that provides:

Features

| Feature | Purpose | Example | |---------|---------|---------| | DNS Callbacks | Detect blind XXE/SSRF via DNS queries | test123.interact.sh resolves and logs query | | HTTP Callbacks | Detect blind SSRF via HTTP requests | GET request to http://test123.interact.sh | | SMTP Callbacks | Detect email-based exfiltration | SMTP connection to test123.interact.sh:25 | | Unique Subdomains | Correlation of tests to callbacks | Each test gets unique identifier | | Session Management | Isolate concurrent scans | Each scanner instance gets unique session |

Interact.sh Workflow

sequenceDiagram
    participant WSHawk as "WSHawk Scanner"
    participant InteractAPI as "Interact.sh API"
    participant InteractDNS as "Interact.sh DNS Server"
    participant Target as "Target Application"
    
    Note over WSHawk,InteractDNS: Registration Phase
    WSHawk->>InteractAPI: POST /register
    InteractAPI-->>WSHawk: {"session": "abc123",<br/>"domain": "abc123.interact.sh"}
    
    Note over WSHawk,InteractDNS: Payload Injection
    WSHawk->>WSHawk: Generate XXE with<br/>http://test1.abc123.interact.sh
    WSHawk->>Target: Send malicious WebSocket message
    Target->>Target: Parse XML, fetch entity
    Target->>InteractDNS: DNS query: test1.abc123.interact.sh
    InteractDNS->>InteractDNS: Log interaction with<br/>timestamp, source IP
    
    Note over WSHawk,InteractDNS: Polling Phase (every 2s)
    loop Every 2 seconds
        WSHawk->>InteractAPI: GET /poll?session=abc123
        InteractAPI-->>WSHawk: {"interactions": [<br/>  {"type": "dns",<br/>   "identifier": "test1",<br/>   "timestamp": "..."}]}
    end
    
    WSHawk->>WSHawk: Match identifier "test1"<br/>to XXE payload
    WSHawk->>WSHawk: Confirm vulnerability
    
    Note over WSHawk,InteractDNS: Cleanup
    WSHawk->>InteractAPI: DELETE /deregister?session=abc123

Callback Matching Logic

OAST callbacks are correlated to specific test payloads using unique identifiers:

  1. Identifier Generation: Each payload gets a unique ID (e.g., test0, test1, test2)
  2. Payload Construction: ID embedded in callback URL (e.g., http://test1.interact.sh)
  3. Callback Correlation: When callback received with identifier test1, matched to corresponding payload
  4. Vulnerability Confirmation: Only payloads with matching callbacks are confirmed as vulnerable

This prevents false positives from unrelated DNS queries or background network activity.

Sources: scanner_v2.py:420, CHANGELOG.md:9


OAST Provider Lifecycle Management

The OASTProvider follows a strict lifecycle to prevent resource leaks and ensure clean session management:

Lifecycle States

stateDiagram-v2
    [*] --> Uninitialized
    Uninitialized --> Starting : start() called
    Starting --> Active : Registration successful
    Starting --> Failed : Registration failed
    Active --> Polling : poll_callbacks() called
    Polling --> Active : Callbacks retrieved
    Active --> Stopping : stop() called
    Stopping --> Cleaned : Deregistration complete
    Cleaned --> [*]
    Failed --> [*]

Resource Cleanup

The scanner ensures proper cleanup even on errors:

# scanner_v2.py:623-628
if self.oast_provider:
    try:
        await self.oast_provider.stop()
        Logger.info("OAST provider stopped")
    except Exception as e:
        Logger.error(f"OAST cleanup error: {e}")

This cleanup happens in the run_heuristic_scan() method after all tests complete, ensuring:

  • Interact.sh sessions are deregistered
  • Local HTTP servers are stopped
  • Network resources are released
  • No orphaned processes remain

Sources: scanner_v2.py:623-628


Integration with Scanner

Scanner Configuration

OAST is configured at the WSHawkV2 class level:

# scanner_v2.py:56-58
# OAST for blind vulnerabilities
self.use_oast = True
self.oast_provider = None

This design allows:

  • Runtime toggling: scanner.use_oast = False to disable OAST
  • Lazy initialization: Provider only created when needed
  • Shared instance: Same provider used across all vulnerability tests

CLI Integration

Users can control OAST through command-line flags:

# Enable all features including OAST (default)
wshawk-advanced ws://target.com --full

# Disable OAST explicitly
wshawk-advanced ws://target.com --no-oast

The --no-oast flag sets scanner.use_oast = False, causing all blind vulnerability tests to fall back to pattern-matching only.

Sources: scanner_v2.py:56-58, README.md:154


Detection Accuracy and Confidence Levels

OAST significantly improves detection accuracy for blind vulnerabilities:

| Detection Method | Confidence Level | False Positive Rate | Use Case | |-----------------|------------------|---------------------|----------| | Pattern Matching Only | LOW-MEDIUM | High (~30-40%) | When OAST unavailable | | Error Message Analysis | MEDIUM | Moderate (~15-20%) | Partial confirmation | | OAST Callback Confirmed | HIGH | Very Low (~1-5%) | Production verification |

Confidence Assignment Logic

The scanner uses different confidence levels based on verification method:

# Example from scanner_v2.py:434-444
# With OAST callback confirmation:
'confidence': 'HIGH',
'description': 'XXE vulnerability - external entities processed'

# Without OAST (pattern matching only):
'confidence': 'MEDIUM',
'description': 'Possible XXE - error message indicates entity processing'

This tiered approach allows security teams to:

  • Prioritize HIGH confidence findings for immediate remediation
  • Investigate MEDIUM confidence findings during manual review
  • Filter LOW confidence findings to reduce noise in reports

Sources: scanner_v2.py:434-444


OAST in Defensive Validation

OAST is also used in the defensive validation module (see DNS Exfiltration Prevention Test) to verify egress filtering controls:

wshawk-defensive ws://target.com

The defensive module uses OAST to:

  1. Validate DNS egress filtering: Confirms external DNS queries are blocked
  2. Test data exfiltration paths: Verifies sensitive data cannot leave via DNS/HTTP
  3. Blue team verification: Proves defensive controls are working as intended

If OAST callbacks succeed during defensive testing, this indicates a security control failure - the opposite interpretation from offensive testing.

Sources: README.md:168-171, CHANGELOG.md:32-33


Limitations and Considerations

Known Limitations

  1. Network Dependency: OAST requires external network connectivity from target server

    • Fails in air-gapped environments
    • Blocked by strict egress filtering (which may be the security control being tested)
  2. Timing Sensitivity: DNS propagation can take 2-10 seconds

    • May miss vulnerabilities with very short TTL
    • Polling frequency affects detection speed vs. API rate limits
  3. False Negatives Possible:

    • Target may fetch entity but through internal proxy/cache
    • WAF may block outbound connections
    • DNS resolver may be hardcoded/non-recursive
  4. Interact.sh Dependency: Cloud mode requires third-party service

    • Privacy concerns for sensitive targets
    • Rate limiting on free tier
    • Service availability dependency

Best Practices

For Offensive Testing:

  • Enable OAST for all penetration tests (--full flag)
  • Use local mode for air-gapped targets
  • Combine with pattern matching for comprehensive coverage
  • Allow 10-15 second delay before assuming no callback

For Defensive Validation:

  • OAST callbacks should NOT succeed if egress filtering is working
  • Use OAST failures as confirmation of security controls
  • Test both DNS and HTTP egress separately

Sources: scanner_v2.py:407-414, README.md:168-171


Summary

OAST is a critical component of WSHawk's vulnerability detection capabilities, enabling:

  • Blind vulnerability detection for XXE and SSRF that produce no visible response
  • High-confidence verification through out-of-band callbacks
  • Reduced false positives compared to pattern-matching approaches
  • Dual-purpose testing for both offensive and defensive security validation

The OASTProvider abstraction allows seamless switching between local and cloud-based OAST services, with proper lifecycle management ensuring clean resource usage.

For implementation details of specific vulnerability tests using OAST, see:

Sources: scanner_v2.py:399-453, scanner_v2.py:495-540, README.md:29-39, CHANGELOG.md:9