HIGH symlink attackactix

Symlink Attack in Actix

How Symlink Attack Manifests in Actix

Symlink attacks in Actix typically occur when the framework serves static files without properly validating symbolic link targets. This vulnerability allows attackers to traverse the filesystem and access sensitive files outside the intended static directory.

In Actix, the most common attack vector involves the Files service. When configured with Files::new, Actix follows symbolic links by default, potentially exposing files like /etc/passwd, application secrets, or source code.

use actix_files as fs;
use actix_web::{App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(fs::Files::new("/static", "./static")
                .show_files_listing())
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

The above configuration is vulnerable because Files::new follows symlinks. An attacker could place a symlink in the ./static directory pointing to /etc/passwd or other sensitive locations.

Actix's default behavior stems from its use of the underlying tokio::fs implementation, which resolves symlinks when serving files. This becomes problematic when serving user-uploaded content or when the static directory is writable by untrusted users.

Another manifestation occurs with custom file handlers that use std::fs::read_to_string or similar functions on paths derived from user input. If an attacker can control the file path, they might create symlinks to sensitive files.

The risk is amplified in Actix applications that serve files based on user-provided identifiers, such as profile pictures or document downloads, without proper path validation.

Actix-Specific Detection

Detecting symlink attacks in Actix requires both static code analysis and runtime testing. Static analysis should look for Files::new usage without follow_symlinks(false) and file-serving handlers that accept user input for paths.

middleBrick's black-box scanning approach is particularly effective for Actix applications. The scanner tests for symlink traversal by creating controlled symlinks in the served directory and attempting to access sensitive files. This approach works regardless of whether you're using Actix's built-in file serving or custom handlers.

For Actix applications, middleBrick specifically checks:

  • Whether the Files service follows symlinks by default
  • If custom file handlers validate paths against symlink traversal
  • Access to sensitive files like /etc/passwd, /proc, or application configuration files
  • Directory traversal attempts through crafted URLs

The scanner also examines OpenAPI specifications if provided, looking for file-serving endpoints that might be vulnerable to symlink attacks.

To manually test your Actix application:

# Create a test symlink
ln -s /etc/passwd ./static/passwd_link

# Attempt to access it via your Actix server
curl http://localhost:8080/static/passwd_link

If this returns the contents of /etc/passwd, your Actix application is vulnerable. middleBrick automates this testing across multiple attack patterns and reports the findings with severity levels and remediation guidance.

The scanner's LLM security module also checks for AI-specific symlink vulnerabilities, such as when model files or training data are exposed through symbolic links in AI-powered Actix applications.

Actix-Specific Remediation

Remediating symlink attacks in Actix involves multiple approaches depending on your use case. The most straightforward fix is to disable symlink following in the Files service:

use actix_files as fs;
use actix_web::{App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(fs::Files::new("/static", "./static")
                .show_files_listing()
                .follow_symlinks(false)) // Disable symlink following
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

For custom file handlers, implement path validation before serving files:

use actix_web::{get, http::StatusCode, web, HttpResponse, Responder};
use std::path::Path;

#[get("/download/{filename}")]
async fn download_file(path: web::Path<String>) -> impl Responder {
    let base_dir = Path::new("./uploads");
    let requested_path = base_dir.join(&path);
    
    // Resolve symlinks and check if the final path is within base_dir
    match requested_path.canonicalize() {
        Ok(canonical) if canonical.starts_with(base_dir) => {
            match tokio::fs::read(canonical) {
                Ok(content) => HttpResponse::Ok().body(content),
                Err(_) => HttpResponse::new(StatusCode::NOT_FOUND),
            }
        }
        _ => HttpResponse::new(StatusCode::FORBIDDEN),
    }
}

This approach canonicalizes the path to resolve any symlinks and verifies the final path is within the allowed directory. If the resolved path escapes the base directory, it returns a 403 Forbidden response.

For applications serving user-uploaded content, consider these additional measures:

use actix_web::{post, web, HttpResponse, Responder};
use tokio::fs;

#[post("/upload")]
async fn upload_file(mut payload: web::Payload) -> impl Responder {
    // Read file content
    let mut body = web::BytesMut::new();
    while let Some(item) = payload.next().await {
        let item = item?;
        body.extend_from_slice(&item);
    }
    
    // Save to a unique filename in a secure directory
    let filename = uuid::Uuid::new_v4().to_string();
    let filepath = format!("./secure_uploads/{}", filename);
    
    // Ensure the directory exists and is not a symlink
    let dir = Path::new("./secure_uploads");
    if dir.is_symlink() {
        return HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR);
    }
    
    fs::write(filepath, body).await?;
    
    HttpResponse::Ok().body("File uploaded successfully")
}

This example prevents symlink attacks by ensuring the upload directory itself isn't a symlink and by using randomly generated filenames instead of user-provided names.

middleBrick's Pro plan includes continuous monitoring that can alert you if symlink vulnerabilities are introduced in new deployments, helping maintain security as your Actix application evolves.

Frequently Asked Questions

Why does Actix follow symlinks by default?
Actix follows symlinks by default because it uses Tokio's filesystem operations, which resolve symlinks to provide the actual file content. This behavior is consistent with most web servers but creates a security risk when serving files from directories that might contain malicious symlinks. The default behavior prioritizes functionality over security, assuming developers will explicitly disable it if needed.
Can middleBrick detect symlink attacks in Actix applications without source code?
Yes, middleBrick's black-box scanning approach tests for symlink vulnerabilities by attempting controlled symlink traversal attacks against running Actix servers. It creates test symlinks and tries to access sensitive files like /etc/passwd, /proc, and application configuration files. This works regardless of whether you're using Actix's built-in file serving or custom handlers, and provides actionable findings with severity ratings.