-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
895 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,61 @@ | ||
#!/usr/bin/env node | ||
|
||
const log = require('log-update') | ||
const { observe } = require('mobx') | ||
const Cracker = require('..') | ||
const EVENTS = Cracker.EVENTS | ||
|
||
let cracker = new Cracker() | ||
|
||
observe(cracker.state, 'intermediary', ({ newValue: intermediary }) => { | ||
console.log('Intermediary value:', intermediary.toString('hex')) | ||
}) | ||
observe(cracker.state, 'plain', ({ newValue: plain }) => { | ||
console.log('Plain text:', plain.toString()) | ||
console.log('Plain text (hex):', plain.toString('hex')) | ||
}) | ||
|
||
cracker.broadcast.addListener(EVENTS.CRACK_START, () => { | ||
console.log('--- Crack start ---') | ||
}) | ||
cracker.broadcast.addListener(EVENTS.CRACK_END, () => { | ||
console.log('--- Crack end ---') | ||
}) | ||
cracker.broadcast.addListener(EVENTS.ORIGINAL_KEY_FOUND, ({ block, padding, hex }) => { | ||
log(`Current block: ${block}, padding: ${padding}\nkey found: (backup)`, `0x${hex}`) | ||
}) | ||
cracker.broadcast.addListener(EVENTS.REPLACEMENT_KEY_FOUND, ({ block, padding, hex }) => { | ||
log(`Current block: ${block}, padding: ${padding}\nkey found:`, `0x${hex}`) | ||
}) | ||
cracker.broadcast.addListener(EVENTS.INVALID_KEY_FOUND, ({ block, padding, hex }) => { | ||
log(`Current block: ${block}, padding: ${padding}\ninvalid:`, `0x${hex}`) | ||
}) | ||
cracker.broadcast.addListener(EVENTS.TRAVERSING_KEY_END, () => { | ||
log.done() | ||
}) | ||
|
||
if (module.parent) { | ||
module.exports = async (...args) => await cracker.crack(...args) | ||
return | ||
} | ||
const { resolve } = require('path') | ||
const React = require('react') | ||
const { render } = require('ink') | ||
const meow = require('meow') | ||
const esm = require('esm') | ||
const jsx = require('import-jsx') | ||
|
||
const ui = jsx('./ui') | ||
|
||
;(() => { | ||
const cli = meow(` | ||
Usage | ||
$ padoracle <challenge-script> --iv-cipher <iv-cipher> --size 16 | ||
Options | ||
challenge-script A script which sends the decryption challenge to the target system. | ||
--iv-cipher An iv-cipher pair which can pass the padding check (with base64 encoded). | ||
--size Size of each block (in bytes). | ||
Examples | ||
$ padoracle ./exmaples/crackme-challenge.js --iv-cipher UGFkT3JhY2xlOml2L2NiYyiFmLTj7lhu4mAJHakEqcIIoYU0lIUXKx+PmTaUHLV0 --size 16 | ||
`, { | ||
flags: { | ||
ivCipher: { | ||
type: 'string', | ||
}, | ||
size: { | ||
type: 'string', | ||
}, | ||
}, | ||
}) | ||
|
||
if (!cli.input.length) { | ||
return cli.showHelp() | ||
} | ||
|
||
const script = esm(module)(resolve(process.cwd(), cli.input[0])) | ||
|
||
if (!script || !script.default) { | ||
throw new Error('Invalid challenge script.') | ||
} | ||
|
||
if (!cli.flags.ivCipher) { | ||
throw new Error('<iv-cipher> is required.') | ||
} | ||
|
||
const token = Buffer.from(cli.flags.ivCipher, 'base64') | ||
const size = +cli.flags.size | ||
|
||
if (!size) { | ||
throw new Error('<size> is required.') | ||
} | ||
|
||
const iv = token.slice(0, size) | ||
const cipher = token.slice(size) | ||
|
||
render(React.createElement(ui, { challenge: script.default, iv, cipher })) | ||
|
||
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
const React = require('react') | ||
const { observe } = require('mobx') | ||
const { Box, Text, Static } = require('ink') | ||
const pad = require('left-pad') | ||
const pkcs7 = require('pkcs7') | ||
const Cracker = require('..') | ||
const EVENTS = Cracker.EVENTS | ||
|
||
const KEY_STATUS = { | ||
ORIGINAL: 'ORIGINAL', | ||
REPlACEMENT: 'REPLACEMENT', | ||
INVALID: 'INVALID', | ||
} | ||
|
||
function App ({ challenge, iv, cipher }) { | ||
const [key, setKey] = React.useState({ | ||
block: -1, | ||
padding: -1, | ||
hex: '??', | ||
sample: '', | ||
status: KEY_STATUS.INVALID, | ||
}) | ||
const [intermediary, setIntermediary] = React.useState([]) | ||
const [plain, setPlain] = React.useState() | ||
|
||
React.useEffect(() => { | ||
let cracker = new Cracker() | ||
observe(cracker.state, 'intermediary', ({ newValue }) => | ||
setIntermediary(newValue) | ||
) | ||
observe(cracker.state, 'plain', ({ newValue }) => | ||
setPlain(newValue) | ||
) | ||
|
||
cracker.broadcast.addListener( | ||
EVENTS.ORIGINAL_KEY_FOUND, | ||
({ block, padding, hex, sample }) => setKey({ | ||
block, | ||
padding, | ||
hex, | ||
sample, | ||
status: KEY_STATUS.ORIGINAL | ||
}) | ||
) | ||
cracker.broadcast.addListener( | ||
EVENTS.REPLACEMENT_KEY_FOUND, | ||
({ block, padding, hex, sample }) => setKey({ | ||
block, | ||
padding, | ||
hex, | ||
sample, | ||
status: KEY_STATUS.REPlACEMENT | ||
}) | ||
) | ||
cracker.broadcast.addListener( | ||
EVENTS.INVALID_KEY_FOUND, | ||
({ block, padding, hex, sample }) =>setKey({ | ||
block, | ||
padding, | ||
hex, | ||
sample, | ||
status: KEY_STATUS.INVALID | ||
}) | ||
) | ||
|
||
cracker.crack(iv, cipher, challenge) | ||
}, []) | ||
|
||
const status = { | ||
ORIGINAL: 'key found: (backup)', | ||
REPLACEMENT: 'key found:', | ||
INVALID: 'invalid:' | ||
}[key.status] | ||
|
||
const format = (buf) => { | ||
const DIVIDER = '|' | ||
return Array.from(buf) | ||
.map((v) => v > 0 | ||
? pad(v.toString(16), 2, '0') | ||
: '??' | ||
) | ||
.join(DIVIDER) | ||
} | ||
|
||
const slice = ((size) => (buf, block) => | ||
buf.slice(block * size, (block + 1) * size) | ||
)(iv.length) | ||
|
||
const tamperedPlain = ((size, padding) => { | ||
let buf = Buffer.alloc(size) | ||
return buf.map((v, i) => { | ||
if (size - i > padding - 1) { | ||
return v | ||
} | ||
return padding | ||
}) | ||
})(iv.length, key.padding) | ||
|
||
return ( | ||
<Box flexDirection="column"> | ||
<Box> | ||
<Text>----- Block {key.block} -----</Text> | ||
</Box> | ||
<Box> | ||
<Text>Cipher ({key.block}) : </Text> | ||
<Text>{format(slice(cipher, key.block))}</Text> | ||
</Box> | ||
<Box> | ||
<Text>Intermediary ({key.block}) : </Text> | ||
<Text>{format(slice(intermediary, key.block))}</Text> | ||
</Box> | ||
<Box> | ||
{ | ||
key.block > 0 | ||
? <Text>Cipher ({key.block - 1}) (Tampered) : </Text> | ||
: <Text>Initial Vector (Tampered) : </Text> | ||
} | ||
<Text>{format(slice(key.sample, key.block))}</Text> | ||
</Box> | ||
<Box> | ||
<Text>Plain ({key.block}) (Tampered) : </Text> | ||
<Text>{format(tamperedPlain)}</Text> | ||
</Box> | ||
{ | ||
plain | ||
? <div> | ||
<Box> | ||
Plain text: {pkcs7.unpad(plain).toString()} | ||
</Box> | ||
<Box> | ||
Plain text (hex): {plain.toString('hex')} | ||
</Box> | ||
</div> | ||
: <div> | ||
<Box> | ||
padding: {key.padding} | ||
</Box> | ||
<Box> | ||
{status} {key.hex} | ||
</Box> | ||
</div> | ||
} | ||
</Box> | ||
) | ||
} | ||
|
||
module.exports = App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const crackme = require('./crackme') | ||
|
||
const challenge = async (iv, cipher) => { | ||
try { | ||
await crackme.api.auth(Buffer.concat([iv, cipher]).toString('base64')) | ||
} catch (error) { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
export default challenge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const { Crypto } = require('../../test/helpers/crypto') | ||
const delay = require('delay') | ||
|
||
const SPEED = 1 | ||
|
||
const crackme = (() => { | ||
const KEY = Buffer.from('abcdefghijklmnopqrstuvwxyz123456') | ||
const DEFAULT_IV = Buffer.from('PadOracle:iv/cbc') | ||
const BLOCK_SIZE = DEFAULT_IV.length | ||
|
||
const DEFAULT_SESSION = '{"id":100,"roleAdmin":false}' | ||
|
||
const crypto = new Crypto(KEY) | ||
|
||
function createToken (session = DEFAULT_SESSION, iv = DEFAULT_IV) { | ||
const cipher = crypto.encrypt(iv, session) | ||
return `${Buffer.concat([iv, cipher]).toString('base64')}` | ||
} | ||
|
||
function getSession (token) { | ||
let buf = Buffer.from(token, 'base64') | ||
let iv = buf.slice(0, BLOCK_SIZE) | ||
let cipher = buf.slice(BLOCK_SIZE) | ||
return crypto.decrypt(iv, cipher).toString('utf8') | ||
} | ||
|
||
return { | ||
api: { | ||
welcome () { | ||
return createToken() | ||
}, | ||
async auth (token) { | ||
if (SPEED) { | ||
await delay(SPEED) | ||
} | ||
return getSession(token) | ||
}, | ||
BLOCK_SIZE, | ||
}, | ||
verifyPlainText (session) { | ||
return session === DEFAULT_SESSION | ||
}, | ||
} | ||
})() | ||
|
||
module.exports = crackme |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.