Skip to content

Commit

Permalink
Disable "Forged JWT" challenge on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
bkimminich committed Mar 17, 2020
1 parent 07d564d commit c1ccf72
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 36 deletions.
2 changes: 1 addition & 1 deletion data/datacreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async function createChallenges () {

await Promise.all(
challenges.map(async ({ name, category, description, difficulty, hint, hintUrl, key, disabledEnv }) => {
const effectiveDisabledEnv = utils.determineDisabledContainerEnv(disabledEnv)
const effectiveDisabledEnv = utils.determineDisabledEnv(disabledEnv)
description = description.replace('juice-sh.op', config.get('application.domain'))
description = description.replace('<iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/771984076&color=%23ff5500&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"></iframe>', entities.encode(config.get('challenges.xssBonusPayload')))
hint = hint.replace(/OWASP Juice Shop's/, `${config.get('application.name')}'s`)
Expand Down
2 changes: 2 additions & 0 deletions data/static/challenges.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@
hint: 'This challenge is explicitly not about acquiring the RSA private key used for JWT signing.'
hintUrl: 'https://pwning.owasp-juice.shop/part2/vulnerable-components.html#forge-an-almost-properly-rsa-signed-jwt-token'
key: jwtForgedChallenge
disabledEnv:
- Windows
-
name: 'Forgotten Developer Backup'
category: 'Sensitive Data Exposure'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
<button *ngIf="challenge.disabledEnv" [id]="challenge.name + '.unavailable'" mat-raised-button
[matTooltip]="challenge.hint" matTooltipPosition="above">
<span>
<i class="{{ 'icon-' + challenge.disabledEnv?.toString().toLowerCase() }}"></i>
<i *ngIf="challenge.disabledEnv !== 'Windows'" class="{{ 'icon-' + challenge.disabledEnv?.toString().toLowerCase() }}"></i>
<i *ngIf="challenge.disabledEnv === 'Windows'" class="{{ 'fab fa-' + challenge.disabledEnv?.toString().toLowerCase() }}"></i>
{{'STATUS_UNAVAILABLE' | translate}}
</span>
</button>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import { Component, Input } from '@angular/core'
import { WindowRefService } from '../Services/window-ref.service'
import { ChallengeService } from '../Services/challenge.service'
import { dom, library } from '@fortawesome/fontawesome-svg-core'
import { faWindows } from '@fortawesome/free-brands-svg-icons'

library.add(faWindows)
dom.watch()

import { Challenge } from '../Models/challenge.model'

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"ADD_BASKET": "Add to Basket",
"BTN_SHOW_ALL": "Show all",
"BTN_SHOW_UNAVAILABLE": "Show unavailable",
"INFO_DISABLED_CHALLENGES": "{{num}} challenges are unavailable on {{env}} due to <a href='https://pwning.owasp-juice.shop/part1/challenges.html#potentially-dangerous-challenges'>security concerns</a>!",
"INFO_DISABLED_CHALLENGES": "{{num}} challenges are unavailable on {{env}} due to <a href='https://pwning.owasp-juice.shop/part1/challenges.html#potentially-dangerous-challenges'>security concerns</a> or technical incompatibility!",
"BTN_HIDE_ALL": "Hide all",
"TYPE_THESE_LETTERS": "Type these {{length}} letters",
"BTN_REQUEST": "Request",
Expand Down
9 changes: 8 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const crypto = require('crypto')
const clarinet = require('clarinet')
const isDocker = require('is-docker')
const isHeroku = require('is-heroku')
const isWindows = require('is-windows')
const logger = require('../lib/logger')

const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
Expand Down Expand Up @@ -205,11 +206,17 @@ exports.disableOnContainerEnv = () => {
return (isDocker() || isHeroku) && !config.get('challenges.safetyOverride')
}

exports.determineDisabledContainerEnv = (disabledEnv) => {
exports.disableOnWindowsEnv = () => {
return isWindows()
}

exports.determineDisabledEnv = (disabledEnv) => {
if (isDocker()) {
return disabledEnv && (disabledEnv === 'Docker' || disabledEnv.includes('Docker')) ? 'Docker' : null
} else if (isHeroku) {
return disabledEnv && (disabledEnv === 'Heroku' || disabledEnv.includes('Heroku')) ? 'Heroku' : null
} else if (isWindows()) {
return disabledEnv && (disabledEnv === 'Windows' || disabledEnv.includes('Windows')) ? 'Windows' : null
}
return null
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"i18n": "^0.8.5",
"is-docker": "^2.0.0",
"is-heroku": "^2.0.0",
"is-windows": "^1.0.2",
"js-yaml": "^3.13.1",
"jsonwebtoken": "0.4.0",
"jssha": "^2.3.1",
Expand Down
2 changes: 1 addition & 1 deletion routes/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ exports.jwtChallenges = () => (req, res, next) => {
if (utils.notSolved(challenges.jwtUnsignedChallenge)) {
jwtChallenge(challenges.jwtUnsignedChallenge, req, 'none', /jwtn3d@/)
}
if (utils.notSolved(challenges.jwtForgedChallenge)) {
if (!utils.disableOnWindowsEnv() && utils.notSolved(challenges.jwtForgedChallenge)) {
jwtChallenge(challenges.jwtForgedChallenge, req, 'HS256', /rsa_lord@/)
}
next()
Expand Down
17 changes: 10 additions & 7 deletions test/e2e/forgedJwtSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) 2014-2020 Bjoern Kimminich.
* SPDX-License-Identifier: MIT
*/
const utils = require('../../lib/utils')

describe('/', () => {
describe('challenge "jwtUnsigned"', () => {
Expand All @@ -13,12 +14,14 @@ describe('/', () => {
protractor.expect.challengeSolved({ challenge: 'Unsigned JWT' })
})

describe('challenge "jwtForged"', () => {
it('should accept a token HMAC-signed with public RSA key with email [email protected] in the payload ', () => {
browser.executeScript('localStorage.setItem("token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAanVpY2Utc2gub3AifSwiaWF0IjoxNTgzMDM3NzExfQ.gShXDT5TrE5736mpIbfVDEcQbLfteJaQUG7Z0PH8Xc8")')
browser.get('/#/')
})
if (!utils.disableOnWindowsEnv()) {
describe('challenge "jwtForged"', () => {
it('should accept a token HMAC-signed with public RSA key with email [email protected] in the payload ', () => {
browser.executeScript('localStorage.setItem("token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAanVpY2Utc2gub3AifSwiaWF0IjoxNTgzMDM3NzExfQ.gShXDT5TrE5736mpIbfVDEcQbLfteJaQUG7Z0PH8Xc8")')
browser.get('/#/')
})

protractor.expect.challengeSolved({ challenge: 'Forged Signed JWT' })
})
protractor.expect.challengeSolved({ challenge: 'Forged Signed JWT' })
})
}
})
51 changes: 27 additions & 24 deletions test/server/verifySpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ chai.use(sinonChai)
const cache = require('../../data/datacache')
const insecurity = require('../../lib/insecurity')
const config = require('config')
const utils = require('../../lib/utils')

describe('verify', () => {
const verify = require('../../routes/verify')
Expand Down Expand Up @@ -281,37 +282,39 @@ describe('verify', () => {
expect(challenges.jwtForgedChallenge.solved).to.equal(false)
})

it('"jwtForgedChallenge" is solved when forged token HMAC-signed with public RSA-key has email [email protected] in the payload', () => {
/*
Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "data": { "email": "rsa_lord@juice-sh.op" }, "iat": 1508639612, "exp": 9999999999 }
*/
this.req.headers = { authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAanVpY2Utc2gub3AifSwiaWF0IjoxNTgyMjIxNTc1fQ.ycFwtqh4ht4Pq9K5rhiPPY256F9YCTIecd4FHFuSEAg' }
if (!utils.disableOnWindowsEnv()) {
it('"jwtForgedChallenge" is solved when forged token HMAC-signed with public RSA-key has email [email protected] in the payload', () => {
/*
Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "data": { "email": "rsa_lord@juice-sh.op" }, "iat": 1508639612, "exp": 9999999999 }
*/
this.req.headers = { authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAanVpY2Utc2gub3AifSwiaWF0IjoxNTgyMjIxNTc1fQ.ycFwtqh4ht4Pq9K5rhiPPY256F9YCTIecd4FHFuSEAg' }

verify.jwtChallenges()(this.req, this.res, this.next)
verify.jwtChallenges()(this.req, this.res, this.next)

expect(challenges.jwtForgedChallenge.solved).to.equal(true)
})
expect(challenges.jwtForgedChallenge.solved).to.equal(true)
})

it('"jwtForgedChallenge" is solved when forged token HMAC-signed with public RSA-key has string "rsa_lord@" in the payload', () => {
/*
Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "data": { "email": "rsa_lord@" }, "iat": 1508639612, "exp": 9999999999 }
*/
this.req.headers = { authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAIn0sImlhdCI6MTU4MjIyMTY3NX0.50f6VAIQk2Uzpf3sgH-1JVrrTuwudonm2DKn2ec7Tg8' }
it('"jwtForgedChallenge" is solved when forged token HMAC-signed with public RSA-key has string "rsa_lord@" in the payload', () => {
/*
Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "data": { "email": "rsa_lord@" }, "iat": 1508639612, "exp": 9999999999 }
*/
this.req.headers = { authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7ImVtYWlsIjoicnNhX2xvcmRAIn0sImlhdCI6MTU4MjIyMTY3NX0.50f6VAIQk2Uzpf3sgH-1JVrrTuwudonm2DKn2ec7Tg8' }

verify.jwtChallenges()(this.req, this.res, this.next)
verify.jwtChallenges()(this.req, this.res, this.next)

expect(challenges.jwtForgedChallenge.solved).to.equal(true)
})
expect(challenges.jwtForgedChallenge.solved).to.equal(true)
})

it('"jwtForgedChallenge" is not solved when token regularly signed with private RSA-key has email [email protected] in the payload', () => {
const token = insecurity.authorize({ data: { email: '[email protected]' } })
this.req.headers = { authorization: 'Bearer ' + token }
it('"jwtForgedChallenge" is not solved when token regularly signed with private RSA-key has email [email protected] in the payload', () => {
const token = insecurity.authorize({ data: { email: '[email protected]' } })
this.req.headers = { authorization: 'Bearer ' + token }

verify.jwtChallenges()(this.req, this.res, this.next)
verify.jwtChallenges()(this.req, this.res, this.next)

expect(challenges.jwtForgedChallenge.solved).to.equal(false)
})
expect(challenges.jwtForgedChallenge.solved).to.equal(false)
})
}
})
})

0 comments on commit c1ccf72

Please sign in to comment.