Skip to content

Commit

Permalink
feat: check for child processes to finish, improve display, add new f…
Browse files Browse the repository at this point in the history
…eatures
  • Loading branch information
flibustier committed Nov 1, 2023
1 parent f4d7427 commit 3a9b6b4
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 37 deletions.
16 changes: 13 additions & 3 deletions __tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { describe, expect, test } from '@jest/globals'
import { spawn } from 'node:child_process'

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imp3dC1jcmFjbGVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.29OQn8UytvagAsG-OwnkzxO2lBw8QEWOuc8ltSZRWCU'
const tokenHS256 = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imp3dC1jcmFjbGVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.29OQn8UytvagAsG-OwnkzxO2lBw8QEWOuc8ltSZRWCU'
const tokenHS512 = 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiJ9.tR6snQQ6RIf0RH9oEl_v5xDlLLduU2gzZhD86QO64ZtXv30Vjcpi61vbB7kBMFAvZFozGrtdhlonAzQ-k9OuZA'

describe('Jwt-cracker', () => {
test('should return secret found', (done) => {
const app = spawn('node', ['index.js', '-t', token])
test('should return secret found with HS256', (done) => {
const app = spawn('node', ['index.js', '-t', tokenHS256])

app.on('exit', (code) => {
expect(code).toBe(0)
done()
})
}, 15000) // 15 Seconds timeout

test('should return secret found with HS512', (done) => {
const app = spawn('node', ['index.js', '-t', tokenHS512])

app.on('exit', (code) => {
expect(code).toBe(0)
Expand Down
102 changes: 68 additions & 34 deletions index.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,67 +1,99 @@
#!/usr/bin/env node

import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import { fork } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import { createReadStream } from 'node:fs'
import { createInterface } from 'node:readline'

import variationsStream from 'variations-stream'

import Constants from './constants.js'
import ArgsParser from './argsParser.js'
import JWTValidator from './jwtValidator.js'
import Constants from './constants.js'

const __dirname = fileURLToPath(new URL('.',
import.meta.url))

const args = new ArgsParser()
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const numberFormatter = Intl.NumberFormat('en', { notation: 'compact' }).format

const token = args.token
const alphabet = args.alphabet
const maxLength = args.maxLength
const {
token,
alphabet,
maxLength,
dictionaryFilePath
} = new ArgsParser()

const validToken = JWTValidator.validateToken(token)
const { isTokenValid, algorithm } = JWTValidator.validateToken(token)

if (!validToken) {
if (!isTokenValid) {
process.exit(Constants.EXIT_CODE_FAILURE)
}

const timeTaken = (startTime) => (new Date().getTime() - startTime) / 1000

const printResult = function (startTime, attempts, result) {
if (result) {
console.log('SECRET FOUND:', result)
} else {
console.log('SECRET NOT FOUND')
}
console.log('Time taken (sec):', (new Date().getTime() - startTime) / 1000)
console.log('Attempts:', attempts)
console.log('Time taken (sec):', timeTaken(startTime))
console.log('Total attempts:', attempts)
}

const [header, payload, signature] = token.split('.')
const content = `${header}.${payload}`

const startTime = new Date().getTime()
let attempts = 0
const chunkSize = 20000
let chunk = []
let attempts = 0
let isStreamClosed = false
const startTime = new Date().getTime()
const childProcesses = []

variationsStream(alphabet, maxLength)
.on('data', function (comb) {
chunk.push(comb)
if (chunk.length >= chunkSize) {
// save chunk and reset it
forkChunk(chunk)
chunk = []
}
if (dictionaryFilePath) {
const lineReader = createInterface({
input: createReadStream(dictionaryFilePath)
})
.on('end', function () {
printResult(startTime, attempts)

lineReader.on('error', function () {
console.log(`Unable to read the dictionary file "${dictionaryFilePath}" (make sure the file path exists)`)
process.exit(Constants.EXIT_CODE_FAILURE)
})
lineReader.on('line', addToQueue)
lineReader.on('close', closeStream)
} else {
variationsStream(alphabet, maxLength)
.on('data', addToQueue)
.on('end', closeStream)
}

function closeStream () {
// purge remaining items in chunk
purgeQueue()
isStreamClosed = true
}

function purgeQueue () {
// save chunk and reset it
forkChunk(chunk)
chunk = []
}

function addToQueue (comb) {
chunk.push(comb)
if (chunk.length >= Constants.CHUNK_SIZE) {
purgeQueue()
}
}

function forkChunk (chunk) {
const child = fork(join(__dirname, 'process-chunk.js'))
child.send({ chunk, content, signature })
childProcesses.push(child)
child.send({ chunk, content, signature, algorithm })
child.on('message', function (result) {
attempts += chunkSize
if (result === null && attempts % 100000 === 0) {
console.log('Attempts:', attempts)
attempts += chunk.length
if (result === null && attempts % (Constants.CHUNK_SIZE * 5) === 0) {
const speed = numberFormatter(Math.trunc(attempts / timeTaken(startTime)))
console.log(`Attempts: ${attempts} (${speed}/s last attempt was '${chunk[chunk.length - 1]}')`)
}
if (result) {
// secret found, print result and exit
Expand All @@ -70,12 +102,14 @@ function forkChunk (chunk) {
}
})

child.on('exit', function () {
// check if all child processes have finished, and if so, exit
checkFinished()
})
child.on('exit', checkFinished)
}

function checkFinished () {
// check if all child processes have finished, and if so, exit
childProcesses.pop()
if (isStreamClosed && childProcesses.length === 0) {
printResult(startTime, attempts)
process.exit(Constants.EXIT_CODE_FAILURE)
}
}

0 comments on commit 3a9b6b4

Please sign in to comment.