Skip to content

Commit

Permalink
Merge pull request #122 from OWASP/dockerize-second-degree
Browse files Browse the repository at this point in the history
Dockerize second degree - First four challenges
  • Loading branch information
paul-ion authored Aug 30, 2023
2 parents a28bd4e + f27d181 commit 70050c7
Show file tree
Hide file tree
Showing 23 changed files with 1,054 additions and 681 deletions.
3 changes: 2 additions & 1 deletion build/trainingportal/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ COPY config.json /home/node/app/config.json
WORKDIR /home/node/app

RUN npm install
RUN npm install pm2@latest -g

EXPOSE 8081
ENTRYPOINT ["node","/home/node/app/server.js"]
ENTRYPOINT ["pm2","start","/home/node/app/server.js","--no-daemon"]
36 changes: 36 additions & 0 deletions hackerden/bucket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2023 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/
listBucket = (req, res) => {
res.setHeader('Content-type', 'text/xml');
let responseXML = `<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>scd-c2</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents><Key>secret.txt</Key><LastModified>2023-06-07T15:24:36.000Z</LastModified><ETag>&quot;9f00fa953c30966bf2c361b01ab60cab&quot;</ETag><Size>16</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
<Contents><Key>messages/</Key><LastModified>2023-06-07T15:24:36.000Z</LastModified><ETag>&quot;9f00fa953c30966bf2c361b01ab60cab&quot;</ETag><Size>16</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
<Contents><Key>chat/</Key><LastModified>2023-06-07T15:24:36.000Z</LastModified><ETag>&quot;9f00fa953c30966bf2c361b01ab60cab&quot;</ETag><Size>16</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
</ListBucketResult>
`
res.send(responseXML)
}

listChatFolder = (req, res) => {
res.setHeader('Content-type', 'text/xml');
let responseXML = `<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Contents><Key>chatUsers.json</Key><LastModified>2023-06-07T15:24:34.000Z</LastModified><ETag>&quot;b40064d8a0c616639625dd2788b0a4db&quot;</ETag><Size>354</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
<Contents><Key>cncChat.css</Key><LastModified>2023-06-07T15:24:35.000Z</LastModified><ETag>&quot;9c3948279137ca3194a3ef103d39a0cd&quot;</ETag><Size>508358</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
<Contents><Key>cncChat.html</Key><LastModified>2023-06-07T15:24:35.000Z</LastModified><ETag>&quot;c407c85c2dc8bb901e8cf8041222fbbf&quot;</ETag><Size>10832</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
<Contents><Key>messages.json</Key><LastModified>2023-06-07T15:24:34.000Z</LastModified><ETag>&quot;d663f1f40451dd745d91b8faba273e28&quot;</ETag><Size>476</Size><Owner><ID>scd</ID><DisplayName>scd</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents>
</ListBucketResult>`

res.send(responseXML)

}

module.exports = {
listBucket,
listChatFolder
}
55 changes: 55 additions & 0 deletions hackerden/challenge-code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
Copyright 2023 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/
const jwt = require('jsonwebtoken');
const crypto = require('crypto');


//issue challenge tokens and redirect to challenge code signer
getChallengeUrl = async(challengeId) => {
var token = await jwt.sign({"sub": challengeId}, process.env.HDEN_SIGN_SECRET, {expiresIn:15*60});
return {"message":"YOU GOT IT!","challengeCodeUrl":"/code/getCode.html#"+token, "challengeId":challengeId};
}

validate = async(req,resp) => {
//validate the token
try {
let decoded = await jwt.verify(req.body.token, process.env.HDEN_SIGN_SECRET)
console.log('authorized:', decoded);
var challengeId = decoded.sub;

if(challengeId===null || typeof challengeId==='undefined'){
resp.status(400)
return resp.send({"errorMessage":"Invalid challenge id"});
}

if(!req.body.codeSalt || req.body.codeSalt.length < 5){
return resp.send({"errorMessage":"Invalid salt"});
}

var masterSalt = "";
if(process.env.CHALLENGE_MASTER_SALT){
masterSalt=process.env.CHALLENGE_MASTER_SALT;
}

var verificationHash = crypto.createHash('sha256').update(challengeId+req.body.codeSalt+masterSalt).digest('base64');

return resp.send({
"verificationCode":verificationHash
});

} catch (error) {
console.log('Failed challenge code JWT verify');
resp.status(400)
return resp.send({"errorMessage":"Invalid or expired token"});
}

}



module.exports = {
getChallengeUrl,
validate
}
151 changes: 151 additions & 0 deletions hackerden/chat-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
Copyright 2023 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/
const jwt = require('jsonwebtoken')
const challengeCode = require('./challenge-code')
const chatUsers = require('./chat/chatUsers.json')
const JSEncrypt = require('nodejs-jsencrypt').default
const crypto = require('crypto')
let messages = require('./messages.json')

authenticate = (req, resp) => {
var user = chatUsers[req.body.userName]; //get the user entry from the db
var userPassHash = user.passHash;
var vfHash = crypto.createHash('sha1').update(req.body.userPass).digest('hex');
if(userPassHash===vfHash){
//generate JWT to identify this user
let permissions = ["currentuser"]

if(req.body.userName == "badspaghetti" || req.body.userName == "stinkyfish"){
permissions.add("messages")
}

let tokenInfo = {"sub": req.body.userName,"name": user.name, "permissions":permissions}

var token = jwt.sign(tokenInfo, process.env.HDEN_AUTH_SECRET);
resp.send({"token": token});
}
else{
resp.status(401)
resp.send("Invalid credentials");
}

}

getAuthorizedUser = async(req) => {
var authToken = req.headers.Authorization;
if(typeof authToken === 'undefined'){
authToken = req.headers.authorization;
}

if(authToken && authToken.split(' ')[0] === 'Bearer') {
let idToken = authToken.split(' ')[1];
try {
let decoded = await jwt.verify(idToken, process.env.HDEN_AUTH_SECRET)

for(let perm of decoded.permissions){
if(req.path.indexOf(perm) > -1) return decoded
}
} catch (error) {
console.log('failed jwt verify: ', err, 'auth: ', idToken);
return null
}
}
return null
}



getCurrentUser = async(req, resp) => {
//validate the token

let user = await getAuthorizedUser(req);

if(user===null){
resp.status(403)
return resp.send("Unauthorized")
}

var challengeId = null;

switch(user.sub){
case "test": challengeId = "owasp2017sensitive"; break;
case "badspaghetti": challengeId = "owasp2017brokenauth"; break;
case "stinkyfish": challengeId = "owasp2017brokenauth"; break;
}


if(challengeId!==null){
let challengeResponse = await challengeCode.getChallengeUrl(challengeId)
user.challengeCodeUrl = challengeResponse.challengeCodeUrl
resp.send(user)

}
}

getMessages = async(req,resp) => {
let user = await getAuthorizedUser(req);

if(user===null){
resp.status(403)
return resp.send("Unauthorized")
}

resp.send(messages)
}

postMessage = async(req,resp) => {
let user = await getAuthorizedUser(req);

if(user===null){
resp.status(403)
return resp.send("Unauthorized")
}

let message = JSON.parse(JSON.stringify(req.body))
if(message.type==='encMessage'){
let challengeResponse = await challengeCode.getChallengeUrl("owasp2017xss")
let challengeCodeUrl = challengeResponse.challengeCodeUrl
message = validateMessage(message, challengeCodeUrl)
}

messages.push(message)
if(messages.length>1000) messages.pop()
resp.send("Message received.")
}

validateMessage = (message, challengeCodeUrl) => {
//check integrity
var toHash = "<img src='https://gov.logger.good' width='0px'>"+message.pubKey;
var hash = crypto.createHash('sha256').update(toHash).digest('hex');

if(message.integrity===hash){
var encrypt = new JSEncrypt();
encrypt.setPublicKey(message.pubKey);
var re = new RegExp('.{1,40}', 'g');
var challengeCodeUrlParts = challengeCodeUrl.match(re);
var encChallengeCodeUrlParts = [];
challengeCodeUrlParts.forEach(part => {
encChallengeCodeUrlParts.push(encrypt.encrypt(part));
});
message.challengeCodeUrl = encChallengeCodeUrlParts;
message.nextChallenge = encrypt.encrypt("/ping");
}
else{
message.error = "Integrity check failed for:'"+JSON.stringify(message)+"'";
}
return message;
}


module.exports = {
authenticate,
getAuthorizedUser,
getCurrentUser,
getMessages,
postMessage
}



File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 70050c7

Please sign in to comment.