diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 6b0b727c..ad9a1bd9 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -338,6 +338,7 @@ "Extract domains", "Extract file paths", "Extract dates", + "Extract hashes", "Regular expression", "XPath expression", "JPath expression", diff --git a/src/core/operations/ExtractHashes.mjs b/src/core/operations/ExtractHashes.mjs new file mode 100644 index 00000000..386aab0e --- /dev/null +++ b/src/core/operations/ExtractHashes.mjs @@ -0,0 +1,84 @@ +/** + * @author mshwed [m@ttshwed.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; + +/** + * Extract Hash Values operation + */ +class ExtractHashes extends Operation { + + /** + * ExtractHashValues constructor + */ + constructor() { + super(); + + this.name = "Extract hashes"; + this.module = "Regex"; + this.description = "Extracts potential hashes based on hash character length"; + this.infoURL = "https://en.wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Hash character length", + type: "number", + value: 40 + }, + { + name: "All hashes", + type: "boolean", + value: false + }, + { + name: "Display Total", + type: "boolean", + value: false + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const results = []; + let hashCount = 0; + + const [hashLength, searchAllHashes, showDisplayTotal] = args; + + // Convert character length to bit length + let hashBitLengths = [(hashLength / 2) * 8]; + + if (searchAllHashes) hashBitLengths = [4, 8, 16, 32, 64, 128, 160, 192, 224, 256, 320, 384, 512, 1024]; + + for (const hashBitLength of hashBitLengths) { + // Convert bit length to character length + const hashCharacterLength = (hashBitLength / 8) * 2; + + const regex = new RegExp(`(\\b|^)[a-f0-9]{${hashCharacterLength}}(\\b|$)`, "g"); + const searchResults = search(input, regex, null, false); + + hashCount += searchResults.length; + results.push(...searchResults); + } + + let output = ""; + if (showDisplayTotal) { + output = `Total Results: ${hashCount}\n\n`; + } + + output = output + results.join("\n"); + return output; + } + +} + +export default ExtractHashes; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 3851e8d9..9d0e418b 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -62,6 +62,7 @@ import "./tests/DefangIP.mjs"; import "./tests/ELFInfo.mjs"; import "./tests/Enigma.mjs"; import "./tests/ExtractEmailAddresses.mjs"; +import "./tests/ExtractHashes.mjs"; import "./tests/Float.mjs"; import "./tests/FileTree.mjs"; import "./tests/FletcherChecksum.mjs"; diff --git a/tests/operations/tests/ExtractHashes.mjs b/tests/operations/tests/ExtractHashes.mjs new file mode 100644 index 00000000..fe739418 --- /dev/null +++ b/tests/operations/tests/ExtractHashes.mjs @@ -0,0 +1,77 @@ +/** + * ExtractHashes tests. + * + * @author mshwed [m@ttshwed.com] + * @copyright Crown Copyright 2024 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Extract MD5 hash", + input: "The quick brown fox jumps over the lazy dog\n\nMD5: 9e107d9d372bb6826bd81d3542a419d6", + expectedOutput: "9e107d9d372bb6826bd81d3542a419d6", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [32, false, false] + }, + ], + }, + { + name: "Extract SHA1 hash", + input: "The quick brown fox jumps over the lazy dog\n\nSHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + expectedOutput: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [40, false, false] + }, + ], + }, + { + name: "Extract SHA256 hash", + input: "The quick brown fox jumps over the lazy dog\n\nSHA256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + expectedOutput: "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [64, false, false] + }, + ], + }, + { + name: "Extract SHA512 hash", + input: "The quick brown fox jumps over the lazy dog\n\nSHA512: 07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", + expectedOutput: "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [128, false, false] + }, + ], + }, + { + name: "Extract all hashes", + input: "The quick brown fox jumps over the lazy dog\n\nMD5: 9e107d9d372bb6826bd81d3542a419d6\nSHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\nSHA256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + expectedOutput: "9e107d9d372bb6826bd81d3542a419d6\n2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\nd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [0, true, false] + }, + ], + }, + { + name: "Extract hashes with total count", + input: "The quick brown fox jumps over the lazy dog\n\nMD5: 9e107d9d372bb6826bd81d3542a419d6\nSHA1: 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\nSHA256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + expectedOutput: "Total Results: 3\n\n9e107d9d372bb6826bd81d3542a419d6\n2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\nd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", + recipeConfig: [ + { + "op": "Extract hashes", + "args": [0, true, true] + }, + ], + } +]);