MEDIUM symlink attackaxum

Symlink Attack in Axum

How Symlink Attack Manifests in Axum

Symlink attacks in Axum applications typically occur when file operations are performed without validating that a path is not a symbolic link pointing to sensitive locations. In Axum, this vulnerability often appears in endpoint handlers that process file uploads, downloads, or configuration management.

The most common attack pattern involves creating a symbolic link that points to a protected system file or another user's data. When the Axum application processes this link without verification, it may inadvertently read, write, or expose sensitive information. For example, an attacker might create a symlink to /etc/passwd or a database file, then upload it through an endpoint expecting only regular files.

Consider this vulnerable Axum handler:

async fn upload_file(
    MultipartParts<File> parts,
    path: web::Path<String>,
) -> impl Responder {
    let file = parts.files.get("file").unwrap();
    let dest_path = format!("/uploads/{}/{}.{}", path, uuid::Uuid::new_v4(), file.content_type);
    
    // Vulnerable: no symlink check
    tokio::fs::write(dest_path, file.data).await.unwrap();
    
    HttpResponse::Ok().finish()
}

This code is vulnerable because it writes data to a path without verifying that the destination isn't a symbolic link. An attacker could create a symlink to a critical system file and the application would overwrite it.

Another manifestation occurs in configuration endpoints where Axum applications might read files based on user input:

async fn read_config(
    config_path: web::Path<String>,
) -> impl Responder {
    let path = format!("/configs/{}.json", config_path);
    
    // Vulnerable: could be a symlink to sensitive data
    let content = tokio::fs::read_to_string(path).await.unwrap();
    
    HttpResponse::Ok().json(json!({ "content": content }))
}

In this case, an attacker could create a symlink to /etc/shadow or other protected files and read their contents through the API.

Axum-Specific Detection

Detecting symlink attacks in Axum requires both static code analysis and runtime scanning. For static analysis, look for file operations that don't validate paths before use. The middleBrick API security scanner can automatically detect these patterns when scanning Axum applications.

When scanning with middleBrick, the tool specifically checks for:

  • File write operations without path validation
  • File read operations that could follow symlinks
  • Path construction from user input without sanitization
  • Lack of follow parameter checks on file operations

Here's how to scan your Axum API with middleBrick CLI:

npm install -g middlebrick
middlebrick scan https://yourapi.com/api/upload

The scanner tests for symlink vulnerabilities by attempting to create symlinks during its black-box assessment and checking if the application follows them to sensitive locations. It also analyzes your OpenAPI spec if available to identify endpoints that handle file operations.

For runtime detection, you can add middleware to your Axum application that validates paths before file operations:

use std::path::Path;

async fn validate_path_is_not_symlink(path: &Path) -> Result<(), &'static str> {
    if path.symlink_metadata().await?.file_type().is_symlink() {
        return Err("Path is a symbolic link");
    }
    Ok(())
}

// Wrap file operations with validation
async fn safe_write(path: &Path, data: &[u8]) -> Result<(), &'static str> {
    validate_path_is_not_symlink(path).await?;
    tokio::fs::write(path, data).await.map_err(|_| "Write failed")?;
    Ok(())
}

middleBrick's continuous monitoring feature (Pro plan) can automatically re-scan your API on a schedule, alerting you if new symlink vulnerabilities are introduced in deployments.

Axum-Specific Remediation

Remediating symlink attacks in Axum requires a multi-layered approach. The most effective strategy combines path validation, secure file handling, and proper error handling.

First, always validate that paths are not symbolic links before performing file operations:

use axum::extract::{Path, MultipartParts};
use axum::http::StatusCode;
use std::path::Path;
use tokio::fs;

async fn safe_upload(
    MultipartParts<File> parts: MultipartParts<File>,
    path: Path<String>,
) -> impl IntoResponse {
    let file = parts.files.get("file").unwrap();
    let dest_path = format!("/uploads/{}/{}.{}", path, uuid::Uuid::new_v4(), file.content_type);
    let dest_path = Path::new(&dest_path);
    
    // Check if path is a symlink
    if let Ok(metadata) = dest_path.symlink_metadata().await {
        if metadata.file_type().is_symlink() {
            return (StatusCode::BAD_REQUEST, "Invalid file path");
        }
    }
    
    // Write file safely
    if let Err(e) = fs::write(dest_path, file.data).await {
        return (StatusCode::INTERNAL_SERVER_ERROR, "File write failed");
    }
    
    (StatusCode::OK, "File uploaded successfully")
}

For reading files, use open with the follow parameter set to false to prevent following symlinks:

use std::fs::OpenOptions;
use std::io::ErrorKind;

async fn safe_read_config(
    config_name: String,
) -> impl IntoResponse {
    let base_path = Path::new("/configs");
    let target_path = base_path.join(format!("{}.json", config_name));
    
    // Verify path is within base directory
    if !target_path.starts_with(base_path) {
        return (StatusCode::BAD_REQUEST, "Invalid path");
    }
    
    // Check if it's a symlink
    if let Ok(metadata) = target_path.symlink_metadata().await {
        if metadata.file_type().is_symlink() {
            return (StatusCode::FORBIDDEN, "Symlink not allowed");
        }
    }
    
    // Read file safely
    match fs::read_to_string(&target_path).await {
        Ok(content) => (StatusCode::OK, json!({ "content": content })),
        Err(e) if e.kind() == ErrorKind::NotFound => {
            (StatusCode::NOT_FOUND, "Config not found")
        }
        Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Read failed"),
    }
}

For enterprise deployments, middleBrick Pro's continuous monitoring can scan your Axum APIs on a configurable schedule (every 5, 15, or 60 minutes) and alert your team via Slack or Teams if symlink vulnerabilities are detected in production.

Frequently Asked Questions

How does middleBrick detect symlink attacks in Axum applications?
middleBrick performs black-box scanning by attempting to create symbolic links to sensitive system files and testing if your Axum API follows them. It also analyzes your OpenAPI spec to identify endpoints that handle file operations, then tests these endpoints with symlink payloads. The scanner runs 12 security checks in parallel and provides a risk score with specific findings about symlink vulnerabilities.
Can symlink attacks be prevented at the Axum middleware level?
Yes, you can create Axum middleware that validates all file paths before they reach your handlers. The middleware can check if paths are symlinks, ensure they're within allowed directories, and reject requests that attempt path traversal. middleBrick's GitHub Action can integrate this validation into your CI/CD pipeline, failing builds if symlink vulnerabilities are detected in your code.