CVE-2018-12457 (published 2018-06-15T14:29Z)

expressCart before 1.1.6 allows remote attackers to create an admin user via a /admin/setup Referer header.

Severity: MEDIUM
CVSS v2 base score: 6.5 (AV:N/AC:L/Au:S/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-732
cwe_name: Incorrect Permission Assignment for Critical Resource
description: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
extended_description: When a resource is given a permission setting that provides access to a wider range of actors than required, it could lead to the exposure of sensitive information, or the modification of that resource by unintended parties. This is especially dangerous when the resource is related to program configuration, execution, or sensitive user data. For example, consider a misconfigured storage account for the cloud that can be read or written by a public or anonymous user.
         

Fix information:
repo_url: https://github.com/mrvautin/expressCart
commit_details: repo_url: https://github.com/mrvautin/expressCart
author: Mark Moffat
author_date: 2018-05-30 18:35:17+09:30
msg: Fix issue with ability to create admin user by setting referrer
repository: repo_url: https://github.com/mrvautin/expressCart
repo_name: mrvautin/expressCart
description: A fully functioning Node.js shopping cart with Stripe, PayPal, Authorize.net, PayWay, Blockonomics, Adyen, Zip and Instore payments.
repo_language: JavaScript
owner: mrvautin
file_changes: filename: user.js
change_type: ModificationType.MODIFY
diff: @@ -146,56 +146,61 @@ router.post('/admin/user/insert', common.restrict, (req, res) => {
     // set the account to admin if using the setup form. Eg: First user account
     let urlParts = url.parse(req.header('Referer'));
 
-    let isAdmin = false;
-    if(urlParts.path === '/admin/setup'){
-        isAdmin = true;
-    }
+    // Check number of users
+    db.users.count({}, (err, userCount) => {
+        let isAdmin = false;
 
-    let doc = {
-        usersName: req.body.usersName,
-        userEmail: req.body.userEmail,
-        userPassword: bcrypt.hashSync(req.body.userPassword, 10),
-        isAdmin: isAdmin
-    };
-
-    // check for existing user
-    db.users.findOne({'userEmail': req.body.userEmail}, (err, user) => {
-        if(user){
-            // user already exists with that email address
-            console.error(colors.red('Failed to insert user, possibly already exists: ' + err));
-            req.session.message = 'A user with that email address already exists';
-            req.session.messageType = 'danger';
-            res.redirect('/admin/user/new');
-            return;
+        // if no users, setup user as admin
+        if(userCount === 0){
+            isAdmin = true;
         }
-        // email is ok to be used.
-        db.users.insert(doc, (err, doc) => {
-            // show the view
-            if(err){
-                if(doc){
-                    console.error(colors.red('Failed to insert user: ' + err));
-                    req.session.message = 'User exists';
-                    req.session.messageType = 'danger';
-                    res.redirect('/admin/user/edit/' + doc._id);
-                    return;
-                }
-                console.error(colors.red('Failed to insert user: ' + err));
-                req.session.message = 'New user creation failed';
+
+        let doc = {
+            usersName: req.body.usersName,
+            userEmail: req.body.userEmail,
+            userPassword: bcrypt.hashSync(req.body.userPassword, 10),
+            isAdmin: isAdmin
+        };
+
+        // check for existing user
+        db.users.findOne({'userEmail': req.body.userEmail}, (err, user) => {
+            if(user){
+                // user already exists with that email address
+                console.error(colors.red('Failed to insert user, possibly already exists: ' + err));
+                req.session.message = 'A user with that email address already exists';
                 req.session.messageType = 'danger';
                 res.redirect('/admin/user/new');
                 return;
             }
-            req.session.message = 'User account inserted';
-            req.session.messageType = 'success';
-
-            // if from setup we add user to session and redirect to login.
-            // Otherwise we show users screen
-            if(urlParts.path === '/admin/setup'){
-                req.session.user = req.body.userEmail;
-                res.redirect('/admin/login');
-                return;
-            }
-            res.redirect('/admin/users');
+            // email is ok to be used.
+            db.users.insert(doc, (err, doc) => {
+                // show the view
+                if(err){
+                    if(doc){
+                        console.error(colors.red('Failed to insert user: ' + err));
+                        req.session.message = 'User exists';
+                        req.session.messageType = 'danger';
+                        res.redirect('/admin/user/edit/' + doc._id);
+                        return;
+                    }
+                    console.error(colors.red('Failed to insert user: ' + err));
+                    req.session.message = 'New user creation failed';
+                    req.session.messageType = 'danger';
+                    res.redirect('/admin/user/new');
+                    return;
+                }
+                req.session.message = 'User account inserted';
+                req.session.messageType = 'success';
+
+                // if from setup we add user to session and redirect to login.
+                // Otherwise we show users screen
+                if(urlParts.path === '/admin/setup'){
+                    req.session.user = req.body.userEmail;
+                    res.redirect('/admin/login');
+                    return;
+                }
+                res.redirect('/admin/users');
+            });
         });
     });
 });

programming_language: TypeScript