CVE-2026-94123 Unauthenticated arbitrary file read in NextGEN Gallery

Intrudify's autonomous pentesting engine discovered an unauthenticated arbitrary file read in NextGEN Gallery, affecting all versions up to and including 4.5.0. Two defects combine: one of four sibling API endpoints performs no authentication, and the path guard behind it returns true for every absolute path it was meant to reject. We used it to retrieve wp-config.php, including the database credentials and all eight WordPress authentication salts.

Discovered by
Intrudify autonomous engine
Validated & reported by
Tudor Lasuschevici
Disclosure
24 September 2026
Severity
High · CVSS 7.5
Vulnerability class
Arbitrary file read, CWE-22
Affected
NextGEN Gallery ≤ 4.5.0
Fixed in
4.5.1
Active installs
400,000+
Privilege required
None, unauthenticated
User interaction
None
Precondition
One pending Lightroom sync job

Summary

NextGEN Gallery exposes four Lightroom API actions to anonymous requests, routed on the init hook. Intrudify's autonomous testing engine found that three of them authenticate correctly and the fourth does not, and that the path guard protecting the file read behind it is inverted by a subtle PHP return-type behaviour.

The endpoint reads a file from an attacker-supplied path and republishes its contents inside a gallery, where it is served over plain HTTP. The attacker then retrieves it with an ordinary GET request.

Technical detail

Defect one: three endpoints authenticate, the fourth does not

The router in nggallery.php dispatches four Lightroom actions for anonymous requests, with a code comment asserting that this is safe because authentication is handled inside the controller class. Sending the same anonymous request to each:

EndpointAnonymous response
get_nextgen_api_token1002 Authentication Failed
get_nextgen_api_path_list1002 Authentication Failed
enqueue_nextgen_api_task_list1002 Authentication Failed
execute_nextgen_api_task_listexecutes

execute_nextgen_api_task_list_action() contains no authenticate_user() call, no permission check, no nonce check and no capability check. The comment in the router was accurate for three of its four subjects.

The handler also calls wp_set_current_user() with the identity stored on the queued job, so the anonymous request proceeds under the account that originally queued the sync.

Defect two: a path guard that cannot reject

The unauthenticated endpoint reads its extra_data parameter as attacker-supplied JSON, takes a path from it, and passes it to is_valid_filename() before calling file_get_contents().

That function does block .phar, :// and ../, and we confirmed all three blocks work. It then attempts to confine the path to the gallery document root or the upload temp directory:

$tmp = $upload_tmp_dir ? $upload_tmp_dir : sys_get_temp_dir();   // "/tmp" on Linux
if ( '/tmp' === $tmp || '/tmp/' === $tmp ) {
    $filename = strstr( $filename, '/tmp' );
}
if ( 0 === strpos( $filename, '/' ) && ( strncmp( $filename, $root, strlen( $root ) ) !== 0
     && strncmp( $filename, $tmp, strlen( $tmp ) ) !== 0 ) ) {
    return false;
}
return true;

strstr() returns boolean false when the needle is absent, not the original subject. On any host where sys_get_temp_dir() is /tmp, which is the Linux default, a path that does not contain the substring /tmp becomes false.

0 === strpos(false, '/') is then false, so the entire containment test is skipped and the function returns true. The guard therefore only ever rejects a path that does contain /tmp while sitting outside both permitted directories. Every other absolute path on the filesystem passes.

Getting the file back

The file contents are written into the gallery by the upload routine and served from /wp-content/gallery/<slug>/<filename>. The attacker fetches them with a plain GET, unauthenticated. Requesting wp-config.php returns HTTP 200 with a Content-Type of image/jpeg and the PHP source as the body.

The precondition

The site must have one pending job in the Lightroom job list, which is the ordinary state of a sync in progress or interrupted. With an empty queue the endpoint reports that the job list is empty and does nothing. Leaving a second queued image untouched keeps the job pending, so the same job can be reused to read further files.

Impact

Any file readable by the web server can be retrieved by an anonymous attacker with no account, no cookie and no nonce. We demonstrated two reads.

wp-config.php, which yields the database name, user, password and host, plus all eight WordPress authentication keys and salts. Those salts sign authentication cookies, so their disclosure undermines session integrity independently of the database credentials.

And /etc/passwd, confirming the read is not confined to the web root.

Because the retrieved file is republished into a publicly served gallery directory, the contents are also exposed to anyone who guesses or discovers the filename, not only to the attacker who requested them.

Remediation

Update NextGEN Gallery to 4.5.1 or later.

Because this vulnerability discloses secrets rather than merely granting access, updating is not sufficient on its own for a site that ran an affected version. Rotate the database password and regenerate the WordPress salts, which invalidates every existing session, and review the gallery upload directory for files that are not images.

For maintainers, there are two independent defects. Add the same authentication call the three sibling endpoints already make, rather than relying on a router comment. And replace the strstr() containment logic with a canonicalising check: resolve the path with realpath() and compare the result against the permitted roots, so the guard fails closed when it cannot establish that a path is safe.

The wider lesson

Both halves of this are failures of assumption rather than omission, and both are worth recognising elsewhere.

The first is a comment asserting a security property. The router explains that nonce verification is unnecessary because the controller authenticates, and that was true when it was written and remained true for three of the four handlers. A comment records a belief about code at one moment; it does not enforce anything, and nothing failed when the fourth handler diverged from it. Wherever a group of sibling handlers shares a security assumption, the assumption is worth testing against each one individually, which is exactly the kind of comparison an automated system does cheaply and a human reviewer skips.

The second is a guard whose failure mode is to allow. strstr() returning false is documented behaviour, and the surrounding code treats that value as though it were still a path. The result is a validator that returns true for nearly every input it was written to reject, while still correctly blocking the three patterns its author had in mind. Security checks should be written so that an unexpected intermediate value produces a refusal, not an approval.

Disclosure timeline

  • 2026-09-24 Published by Patchstack after coordinated disclosure by Intrudify
  • 2026-09-30 CVE-2026-94123 assigned and record published

Questions

What is CVE-2026-94123?

CVE-2026-94123 is an unauthenticated arbitrary file read vulnerability in the NextGEN Gallery WordPress plugin, versions 4.5.0 and earlier. It was discovered autonomously by the Intrudify AI penetration testing engine. One of four sibling Lightroom API endpoints performs no authentication check, and the path guard behind it returns true for every absolute path it was intended to reject, so an anonymous attacker can read any file on the server.

What can an attacker read with CVE-2026-94123?

Any file the web server can read. We demonstrated retrieval of wp-config.php, which contains the database credentials and all eight WordPress authentication salts, and of /etc/passwd from outside the web root. The file is republished inside a gallery and fetched back over ordinary HTTP.

How do I fix CVE-2026-94123?

Update NextGEN Gallery to version 4.5.1 or later. Because the vulnerability exposes database credentials and authentication salts, sites that ran an affected version should also rotate their database password and regenerate their WordPress salts.

References

More advisories from Intrudify

Join the Future of
AI-Driven Pentesting