interactsh 0.2.0

Async Rust client for polling out-of-band interaction servers.
Documentation

interactsh

Async client for polling out-of-band interaction servers. Generate unique URLs, inject them into payloads, and correlate DNS/HTTP interactions back to the original probe. Used for detecting blind XSS, SSRF, SQL injection, and other vulnerabilities where the target makes outbound requests.

use interactsh::{InteractshClient, ClientConfig, InteractionContext};

#[tokio::main]
async fn main() -> interactsh::Result<()> {
    let client = InteractshClient::new(ClientConfig::default()).await?;
    
    // Generate a URL tagged with context
    let url = client.generate_url(
        InteractionContext::new("blind-xss")
            .with_attribute("target", "https://app.example.com")
    )?;
    
    println!("Payload URL: {}", url.url);
    
    // Poll for interactions
    let interactions = client.poll().await?;
    for interaction in interactions {
        println!("Got {} interaction from {:?}",
            interaction.event.protocol,
            interaction.context.label
        );
    }
    
    Ok(())
}

Why this exists

Blind vulnerabilities require out-of-band detection. You inject a URL into a payload. If the target is vulnerable, it makes a request back to that URL. Most tools either lack OOB detection entirely or embed hardcoded interactsh logic that cannot be reused.

This crate provides an async client that handles URL generation with cryptographic nonces, correlation ID management, and interaction polling. It works with public interactsh servers or self-hosted instances.

URL generation

Each generated URL contains:

  • A correlation ID (configurable length, default 14 chars)
  • A unique nonce (configurable length, default 16 chars)
  • The server hostname

The combination ensures global uniqueness while letting you correlate interactions back to specific probes.

let url = client.generate_url(InteractionContext::new("probe-123"))?;
// URL format: {correlation_id}{nonce}.{server}
// Example: abc123def456ghi.oast.pro

Context tracking

Attach metadata to URLs for later correlation:

let context = InteractionContext::new("ssrf-test")
    .with_attribute("template_id", "CVE-2021-44228")
    .with_attribute("target_host", "api.internal");

let url = client.generate_url(context)?;

When polling returns interactions, the context is restored from the nonce mapping.

Polling

The poll() method retrieves all interactions for your correlation ID:

let interactions = client.poll().await?;
for item in interactions {
    println!("Protocol: {}", item.event.protocol);
    println!("Raw request: {:?}", item.event.raw_request);
    println!("Matched context: {}", item.context.label);
}

Unknown interactions (wrong correlation ID or forgotten nonces) are filtered out automatically.

Configuration

use interactsh::ClientConfig;

let config = ClientConfig {
    server: "oast.fun".to_string(),
    token: Some("api-key".to_string()),
    correlation_id_length: 20,
    nonce_length: 12,
    ..ClientConfig::default()
};

Load from TOML:

let config = ClientConfig::from_toml_str(r#"
    server = "oast.fun"
    token = "secret-api-key"
    correlation_id_length = 16
"#)?;

Error handling

Errors are typed by stage:

  • ConfigProblem::Empty — Missing required fields
  • ConfigProblem::MustBeGreaterThanZero — Invalid length settings
  • TransportStage::Send — Network failure
  • TransportStage::Timeout — Request timeout
  • TransportStage::ReadBody — Response body read failure

Self-hosted servers

Point at your own interactsh instance:

let config = ClientConfig {
    server: "oast.internal.corp".to_string(),
    default_scheme: "https".to_string(),
    ..ClientConfig::default()
};

Contributing

Pull requests are welcome. There is no such thing as a perfect crate. If you find a bug, a better API, or just a rough edge, open a PR. We review quickly.

License

MIT. Copyright 2026 CORUM COLLECTIVE LLC.

crates.io docs.rs