Skip to main content
Vulnerability Database/CVE-2026-93712

CVE-2026-93712: Dancer2 Path Traversal Vulnerability

CVE-2026-93712 is a path traversal vulnerability in Dancer2 for Perl that allows attackers to access files outside the public directory. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-93712 Overview

CVE-2026-93712 is a path traversal vulnerability in the Perl web framework Dancer2, affecting versions from 2.1.0 before 2.2.0. The Dancer2::Handler::File route handler joins the request path onto public_dir without collapsing relative path segments. An unauthenticated attacker can request paths containing ../ sequences, or their percent-encoded equivalents, to read files outside the intended web root. Any file readable by the worker process is exposed, including the application's config.yml located above public_dir. The vulnerability is tracked under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory).

Critical Impact

Unauthenticated remote attackers can read arbitrary files accessible to the Perl worker process, including application configuration files containing secrets.

Affected Products

  • Dancer2 for Perl, versions 2.1.0 through 2.1.x
  • Applications that register File in route_handlers and set static_handler to 0
  • Fixed in Dancer2 version 2.2.0

Discovery Timeline

  • 2026-09-22 - CVE-2026-93712 published to the National Vulnerability Database
  • 2026-09-22 - Vulnerability disclosed on the OpenWall oss-security mailing list
  • 2026-09-22 - Last updated in NVD database

Technical Details for CVE-2026-93712

Vulnerability Analysis

The flaw resides in Dancer2::Handler::File, which serves static files from a configured public_dir. When the handler receives a request, it joins the request PATH_INFO onto public_dir using Path::Tiny and then verifies only that the resulting path is a readable regular file. Because Path::Tiny does not collapse ../ segments, a joined path such as public_dir/../outside.txt still resolves to a file outside the intended directory. The -f check passes and the file is streamed to the client.

Percent-encoded variants of the dot characters bypass the same check because URL decoding occurs before the join. Any file readable by the Perl worker process, including config.yml above public_dir, is served to unauthenticated requests.

Root Cause

The handler relied on a naïve file-existence check rather than a canonicalized containment check. Dancer2's default static_handler normally refuses requests containing dot segments before routing, but naming File in route_handlers with static_handler set to 0 disables that protection. The File handler itself never called realpath and never verified that the resolved path was subsumed by public_dir.

Attack Vector

An unauthenticated attacker sends an HTTP request with a crafted path such as /../config.yml or its percent-encoded form /%2E%2E/config.yml. The request traverses out of public_dir and returns the target file contents. No authentication, user interaction, or elevated privileges are required.

perl
# Security patch in lib/Dancer2/Handler/File.pm
        my $file_path = Path::Tiny::path( $self->_public_dir_path, $path );
        my $file_path_str = $file_path->stringify;

        if ( !-f $file_path_str ) {
            $app->response->has_passed(1);
            return;
        }

        # Path::Tiny does not collapse '../' segments, so the joined path
        # above may still point outside public_dir even though it is a
        # readable file (F4). Resolve it and refuse anything that escapes,
        # the same containment check send_file uses at
        # Dancer2/Core/App.pm:1180-1182.
        $file_path      = $file_path->realpath;
        $file_path_str  = $file_path->stringify;

        if ( !$self->_public_dir_path->realpath->subsumes($file_path) ) {
            return $self->standard_response( $app, 403 );
        }

        if ( !-r $file_path_str ) {
            return $self->standard_response( $app, 403 );
        }

Source: Dancer2 patch commit 2446a09. The fix resolves the joined path with realpath and enforces subsumes containment against public_dir.

Detection Methods for CVE-2026-93712

Indicators of Compromise

  • HTTP access log entries containing ../, ..%2F, %2E%2E/, or %2E%2E%2F in the request path served by a Dancer2 application.
  • HTTP 200 responses to requests that resolve outside the configured public_dir, especially for files such as config.yml, .env, or environments/*.yml.
  • Repeated requests probing for common sensitive filenames from a single client IP.

Detection Strategies

  • Inspect web server and reverse proxy logs for percent-encoded dot sequences targeting Dancer2 endpoints.
  • Audit application configuration for any use of File in route_handlers combined with static_handler => 0.
  • Deploy web application firewall rules that normalize URL encoding and block traversal patterns before they reach the Perl worker.

Monitoring Recommendations

  • Alert on unauthenticated reads of configuration files, credential stores, or files outside the documented web root.
  • Track outbound responses containing YAML or configuration markers such as password:, api_key:, or database DSN strings served over HTTP.
  • Correlate scanner-like request patterns with subsequent authenticated activity from newly seen credentials.

How to Mitigate CVE-2026-93712

Immediate Actions Required

  • Upgrade Dancer2 to version 2.2.0 or later from CPAN.
  • Rotate any secrets, API keys, or database credentials stored in config.yml or environment files that may have been exposed.
  • Review web server logs for prior traversal attempts and treat successful reads of sensitive files as a confirmed incident.

Patch Information

The fix is included in Dancer2 2.2.0, published on MetaCPAN. See the MetaCPAN Dancer2 2.2.0 change log, the GitHub Security Advisory GHSA-6xw8-v24c-m783, and the upstream patch commit. The patch calls realpath on the joined path and enforces a subsumes containment check against public_dir.

Workarounds

  • Remove File from route_handlers and rely on the default static_handler, which rejects dot segments before routing.
  • If a custom static handler is required, restore static_handler => 1 so Dancer2's built-in traversal filter runs first.
  • Front the application with a reverse proxy such as nginx or Apache that normalizes and rejects ../ sequences in request paths.
bash
# Upgrade Dancer2 to the patched release
cpanm Dancer2@2.2.0

# Verify the installed version
perl -MDancer2 -e 'print "$Dancer2::VERSION\n"'

# Example config.yml hardening: keep the default static handler enabled
# static_handler: 1
# route_handlers:
#   - AutoPage: 1

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.