Skip to content

Missing Authorization on System Configuration Backup Download and Listing

High
cardigliano published GHSA-7gqc-vjwr-6rh5 Jul 17, 2026

Package

ntopng (Package)

Affected versions

<= 6.7.260717

Patched versions

<= 6.7.260718

Description

Summary

scripts/lua/rest/v2/get/system/configurations/download_backup.lua and .../list_available_backups.lua expose full historical system-configuration backups to any authenticated ntopng user, with no administrator or capability check. Sibling endpoints performing equivalent operations (scripts/lua/rest/v2/export/all/config.lua, import/all/config.lua) correctly gate on isAdministratorOrPrintErr(); these two backup endpoints are the outliers.

Critically, these backups are not merely "configuration" in the informal sense — they are a raw dump of every Redis key under ntopng.prefs.* and ntopng.user.*, which includes every local user's password hash (ntopng.user.<name>.password), and, where configured, API tokens (ntopng.user.<name>.api_token), TOTP secrets (ntopng.user.<name>.totp_secret), and WebAuthn credential blobs (ntopng.user.<name>.webauthn_cred_*). Combined with the unsalted, unkeyed MD5 password hashing documented separately in this audit (12-unsalted-md5-password-hashing.md), a non-administrator user can download one backup, crack the disclosed admin password hash offline, and obtain full administrative control of the application — a direct, low-effort, guaranteed-not-speculative escalation chain, not merely a config-confidentiality issue.

Details

scripts/lua/rest/v2/get/system/configurations/download_backup.lua (entire file):

local download = _GET["download"]
local epoch = _GET["epoch"]

local success, response = backup_config.export_backup(epoch)
...
if download then
    sendHTTPContentTypeHeader('application/json', 'attachment; filename="ntopng_backup_' .. epoch .. '.json"')
    print(response)
else
    rest_utils.answer(rest_utils.consts.success.ok, rsp)
end

scripts/lua/rest/v2/get/system/configurations/list_available_backups.lua (entire file):

local order = _GET["order"] or "desc"
local epoch_list = backup_config.list_backup(_SESSION["user"], order)
...
rest_utils.extended_answer(rc, epoch_list, extra_rsp_data)

Neither file contains isAdministrator() or auth.has_capability(...). Compare with scripts/lua/rest/v2/export/all/config.lua:24:

if not isAdministratorOrPrintErr() then
   rest_utils.answer(rest_utils.consts.err.not_granted)
   return
end

What the backup actually contains — traced end to end: both backup_config.exec_backup() (scripts/lua/modules/system_config/backup_config.lua, triggered daily by scripts/callbacks/daily-delayed/system/backup_configurations.lua, and reachable on-demand indirectly via the sibling admin-gated export endpoint using the identical mechanism) and the vulnerable download path ultimately call all_import_export:export() (scripts/lua/modules/import_export/all_import_export.lua), which calls prefs_dump_utils.build_prefs_dump_table() (scripts/lua/modules/prefs_dump_utils.lua:20,36-54):

local patterns = {"ntopng.prefs.*", "ntopng.user.*"}
...
function prefs_dump_utils.build_prefs_dump_table()
    local out = {}
    for _, pattern in pairs(patterns) do
        local keys = ntop.getKeysCache(pattern)
        for k in pairs(keys or {}) do
            local dump = ntop.dumpCache(k)          -- Redis DUMP-serialized value
            if dump ~= empty_string_dump then
                out[k] = dump
            end
        end
    end
    return out
end

The ntopng.user.* glob unconditionally matches every key defined under NTOPNG_USER_PREFIX in include/ntop_defines.h, including:

  • CONST_STR_USER_PASSWORDntopng.user.%s.password (unsalted MD5 hash, see 12-unsalted-md5-password-hashing.md)
  • CONST_STR_USER_API_TOKENntopng.user.%s.api_token
  • CONST_STR_USER_TOTP_SECRETntopng.user.%s.totp_secret
  • CONST_STR_USER_WEBAUTHN_CREDntopng.user.%s.webauthn_cred_%d

Live confirmation (same session, captured while testing the properly-gated sibling endpoint as admin):

GET /lua/rest/v2/export/all/config.lua   (as admin — this endpoint IS correctly gated; used here only to observe real export content)

Response included:

"ntopng.user.testadmin.password":"002035346638323235313431343464376262313464373063613063613165356661330A003F0F8F68F9CA7733", ...

confirming the export mechanism genuinely embeds the raw password-hash key. This value is a hex-encoded Redis DUMP blob (per ntop.dumpCache()), directly restorable via RESTORE into an attacker-controlled Redis instance to recover the plain MD5 hash — a trivial, standard-tooling step, not a novel technique. The vulnerable, unauthenticated-by-role download_backup.lua reaches the identical export mechanism with no admin gate in front of it.

PoC

As any authenticated, non-administrator user:

GET /lua/rest/v2/get/system/configurations/list_available_backups.lua
GET /lua/rest/v2/get/system/configurations/download_backup.lua?epoch=<epoch-from-list-above>&download=1

returns a full JSON backup containing, among other keys, ntopng.user.admin.password and equivalent entries for every other local account.

Impact

Disclosure of the complete local-user credential store (password hashes for every account, plus API tokens/TOTP secrets/WebAuthn credentials where configured) to any logged-in, non-privileged account. Because ntopng hashes passwords with unsalted, single-round MD5 (12-unsalted-md5-password-hashing.md), this is not merely a confidentiality issue confined to "configuration data" — it is a direct, low-effort path to full administrative account takeover: download one backup, crack the disclosed admin hash offline, log in as administrator. If an API token happens to be configured for any user, no cracking step is even required.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CVE ID

No known CVE

Weaknesses

Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information. Learn more on MITRE.

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

Credits