HIGH dns cache poisoningaxum

Dns Cache Poisoning in Axum

How Dns Cache Poisoning Manifests in Axum

DNS cache poisoning in Axum applications typically occurs when the framework's HTTP client or DNS resolver is used without proper validation and timeout controls. Axum's default Tokio runtime uses the system's DNS resolver, which can be vulnerable to cache poisoning attacks if not properly configured.

The most common manifestation in Axum applications is through the http::Uri handling in extractors and middleware. When an attacker controls DNS responses, they can redirect API calls to malicious servers, leading to data exfiltration or supply chain attacks. This is particularly dangerous in microservices architectures where Axum services communicate with each other.

Consider this vulnerable pattern in Axum:

use axum::extract::TypedHeader;
use axum::http::header::HeaderMap;
use axum::http::Uri;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;

async fn proxy_request(
    TypedHeader(headers): TypedHeader,
) -> impl IntoResponse {
    // Vulnerable: No DNS validation or timeout
    let uri = headers.get("x-forwarded-uri").and_then(|h| h.to_str().ok())?.parse::().ok()?;
    
    // Make HTTP request to potentially poisoned DNS
    let res = reqwest::get(uri.to_string()).await;
    
    (res.status(), res.headers(), res.bytes().await.unwrap())
}

let app = Router::new().route("/proxy", get(proxy_request));

This code is vulnerable because it trusts the x-forwarded-uri header without DNS validation. An attacker can poison the DNS cache to redirect requests to their controlled server, potentially exposing sensitive data or enabling further attacks.

Another Axum-specific vulnerability occurs in WebSocket connections where the DNS resolution happens once during connection establishment:

use axum::extract::ws::{WebSocket, WebSocketUpgrade};
use axum::http::HeaderValue;
use axum::routing::get;
use axum::Router;

async fn websocket_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(|socket| async move {
        // DNS resolution happens once here
        // If poisoned, connection goes to malicious server
        let (mut sender, mut receiver) = socket.split();
        
        while let Some(msg) = receiver.next().await {
            // Process messages from potentially malicious source
        }
    })
}

let app = Router::new().route("/ws", get(websocket_handler));

The WebSocket connection establishes a single TCP connection based on DNS resolution. If the DNS cache is poisoned at connection time, all subsequent communication happens with the malicious server, bypassing any TLS verification that might occur later.

Axum-Specific Detection

Detecting DNS cache poisoning in Axum applications requires examining both the code patterns and runtime behavior. middleBrick's black-box scanning approach is particularly effective for this, as it tests the actual API endpoints without requiring source code access.

middleBrick scans for DNS-related vulnerabilities by examining how your Axum application handles external requests. The scanner tests for:

  • Insecure DNS resolution patterns in HTTP client usage
  • Missing timeout configurations that allow DNS poisoning to persist
  • Unsafe handling of user-controlled URLs and host headers
  • WebSocket connection establishment without DNS validation

The scanner specifically looks for Axum's common patterns like TypedHeader extractors that might accept malicious URLs, and WebSocketUpgrade handlers that establish connections based on potentially poisoned DNS.

For manual detection in Axum code, look for these patterns:

use axum::extract::Path;
use axum::http::Uri;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;

// Vulnerable pattern - no DNS validation
async fn user_profile(
    Path(user_id): Path<u32>,
) -> impl IntoResponse {
    let api_url = format!("https://api.example.com/users/{}", user_id);
    
    // This request is vulnerable to DNS cache poisoning
    let res = reqwest::get(api_url).await;
    
    res.unwrap()
}

let app = Router::new().route("/user/:id", get(user_profile));

middleBrick's scanning would flag this because it makes external HTTP requests without proper DNS validation or timeout controls. The scanner tests by attempting to resolve DNS entries and checking if the application properly handles DNS resolution failures or timeouts.

The scanner also examines middleware that might perform DNS lookups:

use axum::middleware::Next;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;

async fn auth_middleware(
    mut req: axum::http::Request,
    next: Next,
) -> impl IntoResponse {
    // Vulnerable: DNS resolution without validation
    let auth_url = "https://auth.example.com/validate".parse().unwrap();
    let client = reqwest::Client::new();
    let auth_res = client.get(auth_url).send().await;
    
    if auth_res.status().is_success() {
        next.run(req).await
    } else {
        (401, "Unauthorized")
    }
}

let app = Router::new().route("/protected", get(protected_handler));
let app = app.layer(auth_middleware);

middleBrick would detect this middleware pattern as high risk because it performs external authentication calls without DNS security controls, making it vulnerable to DNS cache poisoning attacks that could bypass authentication.

Axum-Specific Remediation

Remediating DNS cache poisoning in Axum requires implementing proper DNS validation, timeout controls, and secure HTTP client configurations. The key is to use Axum's extensibility to add security layers without breaking existing functionality.

First, implement DNS over HTTPS (DoH) or DNS over TLS (DoT) for all external requests:

use axum::extract::Path;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;
use reqwest::Client;
use std::time::Duration;

// Create a secure client with DNS over HTTPS
let secure_client = Client::builder()
    .pool_idle_timeout(Duration::from_secs(30))
    .pool_conn_timeout(Duration::from_secs(10))
    .dns_overrides(reqwest::dns_overrides::cloudflare())
    .build()
    .unwrap();

async fn user_profile(
    Path(user_id): Path<u32>,
    client: Client,
) -> impl IntoResponse {
    let api_url = format!("https://api.example.com/users/{}", user_id);
    
    // Use secure client with DNS over HTTPS
    let res = client
        .get(api_url)
        .timeout(Duration::from_secs(10))
        .send()
        .await;
    
    match res {
        Ok(response) => (response.status(), response.headers(), response.bytes().await.unwrap()),
        Err(_) => (500, "Internal Server Error"),
    }
}

let app = Router::new().route("/user/:id", get(user_profile));
let app = app.with_state(secure_client);

This code uses Cloudflare's DoH resolver, which prevents cache poisoning by encrypting DNS queries and using a trusted resolver. The timeout controls ensure that poisoned DNS entries don't persist indefinitely.

For WebSocket connections, implement DNS validation before establishing connections:

use axum::extract::ws::{WebSocket, WebSocketUpgrade};
use axum::http::HeaderValue;
use axum::routing::get;
use axum::Router;
use tokio::net::lookup_host;

async fn secure_websocket_handler(
    ws: WebSocketUpgrade,
    target_host: String,
) -> impl IntoResponse {
    // Validate DNS before upgrading
    let resolved_ips = lookup_host(target_host).await;
    
    if let Ok(ips) = resolved_ips {
        if ips.len() > 0 {
            // DNS resolved successfully, proceed with connection
            ws.on_upgrade(|socket| async move {
                // Connection established with validated DNS
                let (mut sender, mut receiver) = socket.split();
                
                while let Some(msg) = receiver.next().await {
                    // Process messages from verified source
                }
            })
        } else {
            (400, "Invalid target host")
        }
    } else {
        (500, "DNS resolution failed")
    }
}

let app = Router::new().route("/ws/:host", get(secure_websocket_handler));

This approach validates DNS resolution before establishing WebSocket connections, preventing poisoned DNS from redirecting connections to malicious servers.

Implement middleware for comprehensive DNS security:

use axum::middleware::Next;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;
use reqwest::Client;
use std::time::Duration;

async fn dns_secure_middleware(
    mut req: axum::http::Request,
    next: Next,
    client: Client,
) -> impl IntoResponse {
    // Check if request contains user-controlled URLs
    if let Some(host) = req.uri().host() {
        // Validate host against allowlist or use secure DNS
        if is_trusted_domain(host) {
            return next.run(req).await;
        }
    }
    
    // For external requests, use secure client
    let secure_client = Client::builder()
        .pool_idle_timeout(Duration::from_secs(30))
        .pool_conn_timeout(Duration::from_secs(10))
        .dns_overrides(reqwest::dns_overrides::cloudflare())
        .build()
        .unwrap();
    
    // Replace client in request context
    req.extensions_mut().insert(secure_client);
    
    next.run(req).await
}

fn is_trusted_domain(host: &str) -> bool {
    // Implement domain allowlist or use secure DNS resolution
    vec!["trusted.com", "api.example.com"].contains(&host)
}

let app = Router::new().route("/api/*", get(api_handler));
let app = app.layer(dns_secure_middleware);

This middleware layer ensures all requests use secure DNS resolution and validates hostnames against trusted domains, preventing DNS cache poisoning from affecting your Axum application.

Frequently Asked Questions

How does middleBrick detect DNS cache poisoning in Axum applications?
middleBrick performs black-box scanning of your Axum API endpoints, testing how the application handles external requests and DNS resolution. The scanner examines HTTP client usage patterns, WebSocket connections, and middleware that might be vulnerable to DNS cache poisoning. It specifically looks for missing timeout controls, unsafe URL handling, and lack of DNS validation in Axum's common patterns like TypedHeader extractors and WebSocketUpgrade handlers.
Can DNS cache poisoning affect Axum WebSocket connections?
Yes, DNS cache poisoning can severely impact Axum WebSocket connections. Since WebSocket connections establish a single TCP connection based on initial DNS resolution, if the DNS cache is poisoned at connection time, all subsequent communication happens with the malicious server. This is particularly dangerous because the connection might appear legitimate (using HTTPS/TLS) while actually communicating with an attacker-controlled endpoint. The remediation is to validate DNS resolution before establishing WebSocket connections and use DNS over HTTPS for all external lookups.