VYPR
High severityNVD Advisory· Published Jul 22, 2025· Updated Jul 23, 2025

NodeJS version of the HAX CMS application is distributed with Default Secrets

CVE-2025-54137

Description

HAX CMS NodeJS allows users to manage their microsite universe with a NodeJS backend. Versions 11.0.9 and below were distributed with hardcoded default credentials for the user and superuser accounts. Additionally, the application has default private keys for JWTs. Users aren't prompted to change credentials or secrets during installation, and there is no way to change them through the UI. An unauthenticated attacker can read the default user credentials and JWT private keys from the public haxtheweb GitHub repositories. These credentials and keys can be used to access unconfigured self-hosted instances of the application, modify sites, and perform further attacks. This is fixed in version 11.0.10.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

HAX CMS NodeJS versions ≤11.0.9 ship hardcoded default credentials and JWT keys, enabling unauthenticated remote attackers to gain admin access and take full control of unconfigured instances.

Description

HAX CMS NodeJS is a content management system for microsites. In versions 11.0.9 and below, the application is distributed with hardcoded default credentials for both the regular user and superuser accounts (admin/admin), along with default private keys used for JSON Web Token (JWT) signing [1]. The source code reveals that upon first startup, if no user configuration file exists, the application creates one with these static credentials and prints a warning to the console, but does not require the administrator to change them [3]. The JWT private keys are likewise initialized with predetermined values [1].

Exploitation

An unauthenticated attacker can obtain the default credentials and JWT private keys directly from the public haxtheweb GitHub repositories, where the source code is hosted [1]. No authentication or prior access is needed. With the default admin/admin credentials, an attacker can log in to any self-hosted instance that has not been properly configured [1]. The stolen JWT private keys allow the attacker to forge valid authentication tokens, bypassing login entirely [1]. The official issue tracker and commit history confirm that the credentials are printed in plaintext during startup and written to a .user file with the username admin and password admin [3][4].

Impact

Successful exploitation grants the attacker full superuser privileges, enabling them to modify, delete, or create microsites, access sensitive data, and potentially pivot to other attacks on the host system [1]. Because the default keys are identical across all installations, a single set of credentials and JWTs compromises every unpatched deployment that has not changed its defaults.

Mitigation

The vulnerability is fixed in version 11.0.10 [1]. The commit shows that the code now attempts to load a user-defined configuration file and warns if default credentials are still in use [3]. Administrators of earlier versions should immediately upgrade to 11.0.10 and ensure that custom credentials and JWT secrets are set. As a workaround, users can manually edit the .user file and replace the private key files (.pk and .rpk) with unique values [3].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@haxtheweb/haxcms-nodejsnpm
< 11.0.1011.0.10

Affected products

2

Patches

1
6dc2441c8763

https://github.com/haxtheweb/issues/security/advisories/GHSA-5fpv-5qvh-7cf3

1 file changed · +36 4
  • src/lib/HAXCMS.js+36 4 modified
    @@ -1785,6 +1785,39 @@ class HAXCMSClass {
           this.refreshPrivateKey = uuidv4();
           fs.writeFileSync(path.join(this.configDirectory, ".rpk"), this.refreshPrivateKey);
         }
    +    // allow for loading in user defined config
    +    // pk/rpk test for files that can contain these
    +    try {
    +      this.user = JSON.parse(fs.readFileSync(path.join(this.configDirectory, ".user")),
    +      {encoding:'utf8', flag:'r'}, 'utf8');
    +      this.superUser = {...this.user};
    +    }
    +    catch (e) {
    +      console.warn('***************************************************************');
    +      console.warn('\nHAXcms USER CONFIGURATION FILE NOT FOUND, creating default user');
    +      console.warn(`${path.join(this.configDirectory, ".user")} is being created with default credentials`);
    +      console.warn("MAKE SURE YOU EDIT THIS FILE IF PUTTING IN PRODUCTION!!!!!");
    +      console.warn("username: admin");
    +      console.warn("password: admin");
    +      console.warn("\n***************************************************************");
    +      // create a default user
    +      this.superUser = {
    +        name: 'admin',
    +        password: 'admin',
    +      };
    +      this.user = {
    +        name: 'admin',
    +        password: 'admin',
    +      };
    +      fs.writeFileSync(path.join(this.configDirectory, ".user"), JSON.stringify(this.user, null, 2));
    +    }
    +    // warn if we have default credentials
    +    if (this.user.name == 'admin' && this.user.password == 'admin') {
    +      console.warn('***************************************************************');
    +      console.warn('\nHAXcms USER CONFIGURATION FILE HAS DEFAULT CREDENTIALS, change them!!');
    +      console.warn(`\n${path.join(this.configDirectory, ".user")}`);
    +      console.warn("\n***************************************************************");
    +    }
       }
       /**
        * Load a site off the file system with option to create
    @@ -2661,7 +2694,7 @@ class HAXCMSClass {
           let refreshToken = req.cookies['haxcms_refresh_token'];
           // if there isn't one then we have to bail hard
           if (!refreshToken) {
    -       res.send(401);
    +       res.sendStatus(401);
           }
           // if there is a refresh token then decode it
           let refreshTokenDecoded = this.decodeRefreshToken(refreshToken);
    @@ -2681,7 +2714,7 @@ class HAXCMSClass {
           // kick back the end if its invalid
           if (endOnInvalid) {
             res.cookie('haxcms_refresh_token', '1', { maxAge: 1 });
    -        res.send(401);
    +        res.sendStatus(401);
           }
           return false;
         }
    @@ -2701,7 +2734,7 @@ class HAXCMSClass {
                 return true;
             }
             else {
    -            usr = {};
    +            let usr = {};
                 usr.name = name;
                 usr.grantAccess = false;
                 // fire custom event for things to respond to as needed
    @@ -2734,7 +2767,6 @@ class HAXCMSClass {
             else {
                 let usr = {};
                 usr.name = name;
    -            usr.password = pass;
                 usr.adminFallback = adminFallback;
                 usr.grantAccess = false;
                 // fire custom event for things to respond to as needed
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.