Skip to content
Closed

xd #2

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions auth/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"use stricts";

/**
* Module dependencies.
*/

var express = require("../..");
var hash = require("pbkdf2-password")();
var path = require("node:path");
var session = require("express-session");

var app = (module.exports = express());

// config

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

// middleware

app.use(express.urlencoded());
app.use(
session({
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
secret: "shhhh, very secret",
Comment on lines +21 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Severity: Medium

The session secret is hard-coded to a weak, guessable value. An attacker who knows or can guess this secret can forge or tamper with session data, potentially leading to authentication bypass and session hijacking.

Details The Express application configures `express-session` with a single, hard-coded secret value (“shhhh, very secret”) and no environment-based override. This secret is used at runtime to sign session cookies. Knowledge of the secret enables forging validly signed session cookies, facilitating session fixation and hijacking when combined with obtaining or influencing a victim’s session ID. No alternative configuration, rotation, or production-only override is present; the same hard-coded secret governs verification.

Why it's exploitable:

  • express-session is used with secret: "shhhh, very secret" and no override logic.
  • The file directly starts the server, applying this configuration at runtime.
  • Knowing the secret allows generating signed connect.sid cookies, enabling session fixation or hijacking if an attacker can set or learn a session ID.

Why it's not exploitable:

  • Not applicable; there is no evidence of environment-based secret management, rotation, or alternate production configuration.

Attack vector: network - An attacker can interact with the REST API over the network and supply forged cookies once the secret is known.

CVSS v3.1 vector: AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N - Exploitation generally requires either user interaction (session fixation) or knowledge of an existing session ID; impacts include confidentiality and integrity via account takeover.

Evidence:

  1. auth/index.js:9 — Session library import
9  | var session = require("express-session");
  1. auth/index.js:21-27 — Session middleware with hard-coded secret
21 | app.use(
22 |   session({
23 |     resave: false, // don't save session if unmodified
24 |     saveUninitialized: false, // don't create session until something stored
25 |     secret: "shhhh, very secret",
26 |   })
27 | );
  1. auth/index.js:133-137 — Server bootstrap applying this configuration
133 | /* istanbul ignore next */
134 | if (!module.parent) {
135 |   app.listen(3000);
136 |   console.log("Express started on port 3000");
137 | }
Debug { "id": "019b12b3-8015-74b8-8860-49dd0f1a65a6", "codebaseId": "019ac6c3-c4db-70b0-aed6-bb82698030de", "path": "auth/index.js", "rangeStart": 21, "rangeEnd": 26, "line": 25, "signature": "019b12b3-47f1-74a9-abc0-4b11f4ac2577" }

})
);

// Session-persisted message middleware

app.use(function (req, res, next) {
var err = req.session.error;
var msg = req.session.success;
delete req.session.error;
delete req.session.success;
res.locals.message = "";
if (err) res.locals.message = '<p class="msg error">' + err + "</p>";
if (msg) res.locals.message = '<p class="msg success">' + msg + "</p>";
next();
});

// dummy database

var users = {
tj: { name: "tj" },
};

// when you create a user, generate a salt
// and hash the password ('foobar' is the pass here)

hash({ password: "foobar" }, function (err, pass, salt, hash) {
if (err) throw err;
// store the salt & hash in the "db"
users.tj.salt = salt;
users.tj.hash = hash;
Comment on lines +51 to +56
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Severity: Medium

A user credential is embedded directly in code by hashing a hard-coded password. This creates a default/backdoor account that attackers can exploit if the application is deployed with this code.

Details A default credential is created by hashing the hard-coded password "foobar" and assigning the resulting salt and hash to the user `tj`. The authentication flow uses these values to validate logins. The server starts on port 3000 when the module is run directly, exposing a `/login` route that explicitly instructs use of "tj"/"foobar". There is no environment gating, first-run password change, or disabling mechanism, making this a network-exploitable default account wherever this app is deployed.

Why it's exploitable:

  • The password "foobar" is hard-coded and hashed at startup, populating users.tj.salt and users.tj.hash.
  • The /login route validates credentials using the stored salt/hash and advertises "tj"/"foobar".
  • No environment checks or protections prevent this from running in production.
  • The app listens on port 3000 when executed, exposing a network-accessible login.

Why it's not exploitable:

  • None observed; no guards or rotation requirements are implemented.

Attack vector: network - The Express server starts and exposes /login over the network when run.

CVSS v3.1 vector: AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Network-accessible default credentials; post-auth access appears limited to a restricted page, reducing impact.

Evidence:

  1. auth/index.js:44-56 — users object and hard-coded password hashing
44 | var users = {
45 |   tj: { name: "tj" },
46 | };
...
51 | hash({ password: "foobar" }, function (err, pass, salt, hash) {
52 |   if (err) throw err;
53 |   // store the salt & hash in the "db"
54 |   users.tj.salt = salt;
55 |   users.tj.hash = hash;
56 | });
  1. auth/index.js:60-73 — authenticate() verifies against stored salt/hash
60 | function authenticate(name, pass, fn) {
62 |   var user = users[name];
64 |   if (!user) return fn(null, null);
68 |   hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
70 |     if (hash === user.hash) return fn(null, user);
71 |     fn(null, null);
72 |   });
73 | }
  1. auth/index.js:104-131 — /login uses authenticate and advertises default creds
104 | app.post("/login", function (req, res, next) {
106 |   authenticate(req.body.username, req.body.password, function (err, user) {
127 |         ' (use "tj" and "foobar")';
128 |       res.redirect("/login");
130 |   });
131 | });
  1. auth/index.js:133-137 — server starts when run directly
134 | if (!module.parent) {
135 |   app.listen(3000);
136 |   console.log("Express started on port 3000");
137 | }
Debug { "id": "019b12b3-8018-73f9-a755-473081a6c4f4", "codebaseId": "019ac6c3-c4db-70b0-aed6-bb82698030de", "path": "auth/index.js", "rangeStart": 51, "rangeEnd": 56, "line": 51, "signature": "019b12b3-47f1-74a9-abc0-536eee2e061c" }

});

// Authenticate using our plain-object database of doom!

function authenticate(name, pass, fn) {
if (!module.parent) console.log("authenticating %s:%s", name, pass);
Comment on lines +60 to +62
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Severity: Medium

The code logs both the username and the plaintext password during authentication. Logging credentials can leak sensitive data to log files, monitoring systems, or third-party services, leading to account compromise.

Details The authentication routine logs plaintext credentials when the module is executed directly, causing username and password to be written to stdout on every login attempt. The same file also starts the server when run without a parent module, making the logging active during real authentication traffic. While the logging is gated by `module.parent`, this application is designed to run standalone, so the guard evaluates true in that mode and credentials are exposed to logs that are typically collected by process managers or centralized logging systems.

Why it's exploitable:

  • The POST /login handler invokes authenticate() with req.body.username and req.body.password.
  • Inside authenticate(), a console.log prints both the username and plaintext password when the module has no parent (i.e., executed directly), which is the same condition under which the server is started.

Why it's not exploitable:

  • If this module is only ever required by a parent module (module has a parent), the logging statement does not execute.

Attack vector: local - sensitive data exposure requires access to application logs/stdout, though any remote login request triggers the logging.

CVSS v3.1 vector: AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N - Reading the exposed credentials requires local or privileged access to logs; impact is high for confidentiality only. Base score: 1.4.

Evidence:

  1. auth/index.js:L60-L66 — authenticate
60 | function authenticate(name, pass, fn) {
61 |   if (!module.parent) console.log("authenticating %s:%s", name, pass);
62 |   var user = users[name];
63 |   // query the db for the given username
64 |   if (!user) return fn(null, null);
65 |   // apply the same algorithm to the POSTed password, applying
  1. auth/index.js:L104-L108 — /login route
104 | app.post("/login", function (req, res, next) {
105 |   if (!req.body) return res.sendStatus(400);
106 |   authenticate(req.body.username, req.body.password, function (err, user) {
107 |     if (err) return next(err);
108 |     if (user) {
  1. auth/index.js:L133-L137 — server startup
133 | /* istanbul ignore next */
134 | if (!module.parent) {
135 |   app.listen(3000);
136 |   console.log("Express started on port 3000");
137 | }
Debug { "id": "019b12b3-801b-755a-abbe-4ea57ae7e7a5", "codebaseId": "019ac6c3-c4db-70b0-aed6-bb82698030de", "path": "auth/index.js", "rangeStart": 60, "rangeEnd": 62, "line": 61, "signature": "019b12b3-47f1-74a9-abc0-586489c98e82" }

var user = users[name];
// query the db for the given username
if (!user) return fn(null, null);
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
if (err) return fn(err);
if (hash === user.hash) return fn(null, user);
fn(null, null);
});
}

function restrict(req, res, next) {
if (req.session.user) {
next();
} else {
req.session.error = "Access denied!";
res.redirect("/login");
}
}

app.get("/", function (req, res) {
res.redirect("/login");
});

app.get("/restricted", restrict, function (req, res) {
res.send('Wahoo! restricted area, click to <a href="/logout">logout</a>');
});

app.get("/logout", function (req, res) {
// destroy the user's session to log them out
// will be re-created next request
req.session.destroy(function () {
res.redirect("/");
});
});

app.get("/login", function (req, res) {
res.render("login");
});

app.post("/login", function (req, res, next) {
if (!req.body) return res.sendStatus(400);
authenticate(req.body.username, req.body.password, function (err, user) {
if (err) return next(err);
if (user) {
// Regenerate session when signing in
// to prevent fixation
req.session.regenerate(function () {
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
req.session.success =
"Authenticated as " +
user.name +
' click to <a href="/logout">logout</a>. ' +
' You may now access <a href="/restricted">/restricted</a>.';
res.redirect(req.get("Referrer") || "/");
});
} else {
req.session.error =
"Authentication failed, please check your " +
" username and password." +
' (use "tj" and "foobar")';
res.redirect("/login");
}
});
});

/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log("Express started on port 3000");
}
Loading