VYPR
High severityNVD Advisory· Published Oct 9, 2023· Updated Sep 18, 2024

Uptime Kuma has Persistentent User Sessions

CVE-2023-44400

Description

Uptime Kuma is a self-hosted monitoring tool. Prior to version 1.23.3, attackers with access to a user's device can gain persistent account access. This is caused by missing verification of Session Tokens after password changes and/or elapsed inactivity periods. Version 1.23.3 has a patch for the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
uptime-kumanpm
< 1.23.31.23.3

Affected products

1

Patches

1
88afab6571ef

Merge pull request from GHSA-g9v2-wqcj-j99g

https://github.com/louislam/uptime-kumaLouis LamOct 8, 2023via ghsa
3 files changed · +47 9
  • server/model/user.js+15 0 modified
    @@ -1,6 +1,8 @@
     const { BeanModel } = require("redbean-node/dist/bean-model");
     const passwordHash = require("../password-hash");
     const { R } = require("redbean-node");
    +const jwt = require("jsonwebtoken");
    +const { shake256, SHAKE256_LENGTH } = require("../util-server");
     
     class User extends BeanModel {
         /**
    @@ -27,6 +29,19 @@ class User extends BeanModel {
             this.password = newPassword;
         }
     
    +    /**
    +     * Create a new JWT for a user
    +     * @param {User} user
    +     * @param {string} jwtSecret
    +     * @return {string}
    +     */
    +    static createJWT(user, jwtSecret) {
    +        return jwt.sign({
    +            username: user.username,
    +            h: shake256(user.password, SHAKE256_LENGTH),
    +        }, jwtSecret);
    +    }
    +
     }
     
     module.exports = User;
    
  • server/server.js+14 9 modified
    @@ -83,8 +83,11 @@ const app = server.app;
     log.info("server", "Importing this project modules");
     log.debug("server", "Importing Monitor");
     const Monitor = require("./model/monitor");
    +const User = require("./model/user");
    +
     log.debug("server", "Importing Settings");
    -const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests } = require("./util-server");
    +const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests, shake256, SHAKE256_LENGTH
    +} = require("./util-server");
     
     log.debug("server", "Importing Notification");
     const { Notification } = require("./notification");
    @@ -296,6 +299,11 @@ let needSetup = false;
                         decoded.username,
                     ]);
     
    +                // Check if the password changed
    +                if (decoded.h !== shake256(user.password, SHAKE256_LENGTH)) {
    +                    throw new Error("The token is invalid due to password change or old token");
    +                }
    +
                     if (user) {
                         log.debug("auth", "afterLogin");
                         afterLogin(socket, user);
    @@ -316,9 +324,10 @@ let needSetup = false;
                         });
                     }
                 } catch (error) {
    -
                     log.error("auth", `Invalid token. IP=${clientIP}`);
    -
    +                if (error.message) {
    +                    log.error("auth", error.message, `IP=${clientIP}`);
    +                }
                     callback({
                         ok: false,
                         msg: "Invalid token.",
    @@ -357,9 +366,7 @@ let needSetup = false;
     
                         callback({
                             ok: true,
    -                        token: jwt.sign({
    -                            username: data.username,
    -                        }, server.jwtSecret),
    +                        token: User.createJWT(user, server.jwtSecret),
                         });
                     }
     
    @@ -387,9 +394,7 @@ let needSetup = false;
     
                             callback({
                                 ok: true,
    -                            token: jwt.sign({
    -                                username: data.username,
    -                            }, server.jwtSecret),
    +                            token: User.createJWT(user, server.jwtSecret),
                             });
                         } else {
     
    
  • server/util-server.js+18 0 modified
    @@ -33,6 +33,7 @@ const dayjs = require("dayjs");
     // SASLOptions used in JSDoc
     // eslint-disable-next-line no-unused-vars
     const { Kafka, SASLOptions } = require("kafkajs");
    +const crypto = require("crypto");
     
     const isWindows = process.platform === /^win/.test(process.platform);
     /**
    @@ -1055,6 +1056,23 @@ module.exports.grpcQuery = async (options) => {
         });
     };
     
    +module.exports.SHAKE256_LENGTH = 16;
    +
    +/**
    + *
    + * @param {string} data
    + * @param {number} len
    + * @return {string}
    + */
    +module.exports.shake256 = (data, len) => {
    +    if (!data) {
    +        return "";
    +    }
    +    return crypto.createHash("shake256", { outputLength: len })
    +        .update(data)
    +        .digest("hex");
    +};
    +
     // For unit test, export functions
     if (process.env.TEST_BACKEND) {
         module.exports.__test = {
    

Vulnerability mechanics

Generated by null/stub 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.