psf-nginx-ui-check(8)

Script: /opt/psf-security-patches/nginx-ui/nginx-ui-check.sh. Tool page: nginx-ui. Source. With the package installed: man psf-nginx-ui-check.

nginx-ui restricted-directive bypass (authenticated RCE): read-only checker  (v2)
CVE-2026-84316, CVE-2026-84317, CVE-2026-84318, CVE-2026-84319

If this software is helpful, consider a donation to our 501c3 nonprofit:
https://privacysafe.foundation

nginx-ui is a web interface for managing nginx. It has a security control,
ValidateConfigFile, that refuses configuration containing directives that can run
code or raise privileges (load_module of the njs or Perl modules, js_import,
js_content, perl_*, user root). Four advisories published 12 October 2026 describe
ways around that control for a logged-in user. Where nginx loads the njs, Perl or
Lua module (the official Docker image does), the injected configuration runs code
in the nginx worker.

  CVE-2026-84316  CVSS 8.8  The validator split the configuration on newlines and
                  looked only at the first token of each line. nginx separates
                  directives with ";", so a harmless directive first and a
                  restricted one after a semicolon on the same line passed.
  CVE-2026-84317  CVSS 8.8  The MCP tool nginx_config_add wrote the caller's
  CVE-2026-84318  CVSS 8.8  configuration to disk and reloaded nginx without calling
                  ValidateConfigFile at all. The REST paths do call it. Two IDs
                  describe this one gap, with different affected ranges.
  CVE-2026-84319  CVSS 8.3  The MCP configuration interface let a secondary
                  authenticated user write and apply the restricted directives the
                  regular API refuses (load_module of the njs module, js_import),
                  which executes njs in the nginx worker.

  Affected: nginx-ui below 2.4.1 (84318 from 2.0.0-rc.6).
  Fixed:    nginx-ui 2.4.1.

How this script reads an install. The nginx-ui binary has no version flag, and its
version endpoint needs a login, so a read-only check cannot ask it. Instead:
  0. The official container image, uozi/nginx-ui, is judged by its tag when the tag names a
     version (a tag such as latest does not, and is a warning). With no binary and no image
     on the host, a version you supply in NGINX_UI_VERSION is judged on its own.
  1. NGINX_UI_VERSION (you set it) or the package version, when there is one;
  2. a string inside the binary. The 2.4.1 validator adds the restricted names
     lua_package_cpath and lua_package_path, which 2.4.0 does not contain. A binary
     without them is the older validator (strong finding). A binary with them is
     consistent with 2.4.1 or later; it is a string match, so confirm the version
     shown in the web interface.

This script is read-only.

Usage:
  ./nginx-ui-check.sh [command]
  check (default)  find nginx-ui, read its version and the binary signature, report.
                   Exit 0 = nothing exposed, 1 = exposed or unclear.
  hunt             read nginx configuration for the semicolon bypass shape and list
                   restricted directives present, for review. Exit 0 = nothing found,
                   1 = a strong lead. A clean hunt means none of the published indicators were found.
  version          print the script version.

Env: NGINX_UI_BIN      path to the nginx-ui binary
     NGINX_UI_VERSION  the version, from the web interface, if you know it
     NGINX_UI_CONF_DIRS  space separated nginx configuration directories for hunt
                         (default /etc/nginx /etc/angie /usr/local/etc/nginx /usr/local/nginx/conf)
Containers: with no argument the check also looks in running docker and podman containers
(SCAN_CONTAINERS=1, the default; 0 skips them). A read-only probe runs the two signature tests
this check applies to the binary (lua_package_cpath and lua_package_path) inside the container
and copies only a small file recording the answer, so the binary itself is never copied. The
version is not read by running nginx-ui, so a container is judged by that signature alone. Paths
shown as .../docker_NAME/... are inside the container NAME.

The Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.

The PrivacySafe Ecosystem

Shop at Ivy Cyber

Ivy Cyber offers PrivacySafe products and services in the USA and has partnerships with 3NSoft in Canada and Kuip Limited in the European Union. Enterprise services and consulting are available at ivycyber.com

PrivacySafe Foundation is a 501(c)(3) nonprofit public charity that supports the development of the PrivacySafe applications, along with the components they rely upon: 3NWeb protocols, filesystem, documentation, and reference implementations.

PrivacySafe Foundation

Donations to PrivacySafe Foundation support education, research, documentation, and public code. Purchases from Ivy Cyber support PrivacySafe products, services, hosting, and technical support. Together, we're building a better web.