Permalink
Browse files

Add "Access Log" challenge

  • Loading branch information...
bkimminich committed Jan 15, 2019
1 parent 3bcf336 commit 094446bfab16281cf1d2d787c5a5384fbecee686
@@ -230,3 +230,6 @@ ctf:
resetPasswordBjoernOwaspChallenge:
name: Kazakhstan
code: KZ
accessLogDisclosureChallenge:
name: Singapore
code: SG
@@ -599,3 +599,12 @@
hint: 'He might have spoilered it on at least one occasion where a camera was running. Maybe elsewhere as well.'
hintUrl: 'https://bkimminich.gitbooks.io/pwning-owasp-juice-shop/content/part2/broken-authentication.html#reset-the-password-of-bjoerns-owasp-account-via-the-forgot-password-mechanism'
key: resetPasswordBjoernOwaspChallenge
-
name: 'Access Log'
category: 'Security Misconfiguration'
description: 'Gain access to any access log file of the server.'
difficulty: 4
hint: 'Who would want a server access log to be accessible through a web application?'
hintUrl: ''
key: accessLogDisclosureChallenge

@@ -141,6 +141,17 @@ exports.toMMMYY = date => {
return months[month] + year.toString().substring(2, 4)
}

exports.toISO8601 = date => {
let day = '' + date.getDate()
let month = '' + (date.getMonth() + 1)
const year = date.getFullYear()

if (month.length < 2) month = '0' + month
if (day.length < 2) day = '0' + day

return [year, month, day].join('-')
}

exports.downloadToFile = (url, dest) => {
download(url).then(data => {
fs.writeFileSync(dest, data)
@@ -0,0 +1,14 @@
const path = require('path')

module.exports = function serveLogFiles () {
return ({ params }, res, next) => {
const file = params.file

if (!file.includes('/')) {
res.sendFile(path.resolve(__dirname, '../logs/', file))
} else {
res.status(403)
next(new Error('File names cannot contain forward slashes!'))
}
}
}
@@ -56,6 +56,8 @@ exports.accessControlChallenges = () => ({ url }, res, next) => {
utils.solve(challenges.retrieveBlueprintChallenge)
} else if (utils.notSolved(challenges.securityPolicyChallenge) && utils.endsWith(url, '/security.txt')) {
utils.solve(challenges.securityPolicyChallenge)
} else if (utils.notSolved(challenges.accessLogDisclosureChallenge) && url.match(/access\.log(0-9-)*/)) {
utils.solve(challenges.accessLogDisclosureChallenge)
}
next()
}
@@ -31,6 +31,7 @@ const continueCode = require('./routes/continueCode')
const restoreProgress = require('./routes/restoreProgress')
const fileServer = require('./routes/fileServer')
const keyServer = require('./routes/keyServer')
const logFileServer = require('./routes/logfileServer')
const authenticatedUsers = require('./routes/authenticatedUsers')
const currentUser = require('./routes/currentUser')
const login = require('./routes/login')
@@ -119,6 +120,11 @@ app.use('/ftp/:file', fileServer())
app.use('/encryptionkeys', serveIndex('encryptionkeys', { 'icons': true, 'view': 'details' }))
app.use('/encryptionkeys/:file', keyServer())

/* /logs directory browsing */
app.use('/support/logs', serveIndex('logs', { 'icons': true, 'view': 'details' }))
app.use('/support/logs', verify.accessControlChallenges())
app.use('/support/logs/:file', logFileServer())

/* Swagger documentation for B2B v2 endpoints */
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument))

@@ -1,5 +1,6 @@
const frisby = require('frisby')
const config = require('config')
const utils = require('../../lib/utils')

const URL = 'http://localhost:3000'

@@ -123,4 +124,10 @@ describe('Hidden URL', () => {
return frisby.get(URL + '/assets/public/images/products/' + blueprint)
.expect('status', 200)
})

it('GET folder containing access log files for "Access Log" challenge', () => {
return frisby.get(URL + '/support/logs/access.log.' + utils.toISO8601(new Date()))
.expect('status', 200)
.expect('header', 'content-type', /application\/octet-stream/)
})
})
@@ -1,4 +1,5 @@
const config = require('config')
const utils = require('../../lib/utils')
let blueprint

for (const product of config.get('products')) {
@@ -56,4 +57,12 @@ describe('/', () => {

protractor.expect.challengeSolved({ challenge: 'Email Leak' })
})

describe('challenge "accessLogDisclosure"', () => {
it('should be able to access today\'s access log file', () => {
browser.driver.get(browser.baseUrl + '/support/logs/access.log.' + utils.toISO8601(new Date()))
})

protractor.expect.challengeSolved({ challenge: 'Access Log' })
})
})
@@ -68,7 +68,7 @@ describe('verify', () => {
})

describe('accessControlChallenges', () => {
it('"scoreBoardChallenge" is solved when the 1px.png transpixel is this.requested', () => {
it('"scoreBoardChallenge" is solved when the 1px.png transpixel is requested', () => {
challenges.scoreBoardChallenge = { solved: false, save: this.save }
this.req.url = 'http://juice-sh.op/public/images/padding/1px.png'

@@ -77,7 +77,7 @@ describe('verify', () => {
expect(challenges.scoreBoardChallenge.solved).to.equal(true)
})

it('"adminSectionChallenge" is solved when the 19px.png transpixel is this.requested', () => {
it('"adminSectionChallenge" is solved when the 19px.png transpixel is requested', () => {
challenges.adminSectionChallenge = { solved: false, save: this.save }
this.req.url = 'http://juice-sh.op/public/images/padding/19px.png'

@@ -86,7 +86,7 @@ describe('verify', () => {
expect(challenges.adminSectionChallenge.solved).to.equal(true)
})

it('"tokenSaleChallenge" is solved when the 56px.png transpixel is this.requested', () => {
it('"tokenSaleChallenge" is solved when the 56px.png transpixel is requested', () => {
challenges.tokenSaleChallenge = { solved: false, save: this.save }
this.req.url = 'http://juice-sh.op/public/images/padding/56px.png'

@@ -95,7 +95,7 @@ describe('verify', () => {
expect(challenges.tokenSaleChallenge.solved).to.equal(true)
})

it('"extraLanguageChallenge" is solved when the Klingon translation file is this.requested', () => {
it('"extraLanguageChallenge" is solved when the Klingon translation file is requested', () => {
challenges.extraLanguageChallenge = { solved: false, save: this.save }
this.req.url = 'http://juice-sh.op/public/i18n/tlh_AA.json'

@@ -104,7 +104,7 @@ describe('verify', () => {
expect(challenges.extraLanguageChallenge.solved).to.equal(true)
})

it('"retrieveBlueprintChallenge" is solved when the blueprint file is this.requested', () => {
it('"retrieveBlueprintChallenge" is solved when the blueprint file is requested', () => {
challenges.retrieveBlueprintChallenge = { solved: false, save: this.save }
cache.retrieveBlueprintChallengeFile = 'test.dxf'
this.req.url = 'http://juice-sh.op/public/images/products/test.dxf'
@@ -113,6 +113,15 @@ describe('verify', () => {

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

it('"accessLogDisclosureChallenge" is solved when any server access log file is requested', () => {
challenges.accessLogDisclosureChallenge = { solved: false, save: this.save }
this.req.url = 'http://juice-sh.op/support/logs/access.log.2019-01-15'

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

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

describe('"errorHandlingChallenge"', () => {

0 comments on commit 094446b

Please sign in to comment.