diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e7877799..3fa7a23b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,12 +22,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 4271e5da..d7880ff9 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -10,10 +10,10 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set node version - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '18.x' @@ -47,7 +47,7 @@ jobs: - name: Deploy to GitHub Pages if: success() && github.ref == 'refs/heads/master' - uses: crazy-max/ghaction-github-pages@v2 + uses: crazy-max/ghaction-github-pages@v3 with: target_branch: gh-pages build_dir: ./build/prod diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml index 0c191607..0e746497 100644 --- a/.github/workflows/pull_requests.yml +++ b/.github/workflows/pull_requests.yml @@ -9,10 +9,10 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set node version - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '18.x' diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 9f09e99f..966fe1d5 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -10,10 +10,10 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set node version - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '18.x' diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d0712d..f4d21a41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ All major and minor version changes will be documented in this file. Details of ## Details +### [9.48.0] - 2022-10-14 +- Added 'LM Hash' and 'NT Hash' operations [@n1474335] [@brun0ne] | [#1427] + +### [9.47.0] - 2022-10-14 +- Added 'LZMA Decompress' and 'LZMA Compress' operations [@mattnotmitt] | [#1421] + ### [9.46.0] - 2022-07-08 - Added 'Cetacean Cipher Encode' and 'Cetacean Cipher Decode' operations [@valdelaseras] | [#1308] @@ -315,6 +321,8 @@ All major and minor version changes will be documented in this file. Details of +[9.48.0]: https://github.com/gchq/CyberChef/releases/tag/v9.48.0 +[9.47.0]: https://github.com/gchq/CyberChef/releases/tag/v9.47.0 [9.46.0]: https://github.com/gchq/CyberChef/releases/tag/v9.46.0 [9.45.0]: https://github.com/gchq/CyberChef/releases/tag/v9.45.0 [9.44.0]: https://github.com/gchq/CyberChef/releases/tag/v9.44.0 @@ -450,6 +458,7 @@ All major and minor version changes will be documented in this file. Details of [@crespyl]: https://github.com/crespyl [@thomasleplus]: https://github.com/thomasleplus [@valdelaseras]: https://github.com/valdelaseras +[@brun0ne]: https://github.com/brun0ne [8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7 [9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513 @@ -552,4 +561,6 @@ All major and minor version changes will be documented in this file. Details of [#1266]: https://github.com/gchq/CyberChef/pull/1266 [#1250]: https://github.com/gchq/CyberChef/pull/1250 [#1308]: https://github.com/gchq/CyberChef/pull/1308 +[#1421]: https://github.com/gchq/CyberChef/pull/1421 +[#1427]: https://github.com/gchq/CyberChef/pull/1427 diff --git a/package-lock.json b/package-lock.json index cbcb2e57..deb1d7a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "cyberchef", - "version": "9.46.5", + "version": "9.48.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cyberchef", - "version": "9.46.5", + "version": "9.48.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@babel/polyfill": "^7.12.1", + "@blu3r4y/lzma": "^2.3.3", "arrive": "^2.4.1", "avsc": "^5.7.4", "bcryptjs": "^2.4.3", @@ -52,7 +53,7 @@ "jsrsasign": "^10.5.23", "kbpgp": "2.1.15", "libbzip2-wasm": "0.0.4", - "libyara-wasm": "^1.1.0", + "libyara-wasm": "^1.2.1", "lodash": "^4.17.21", "loglevel": "^1.8.0", "loglevel-message-prefix": "^3.0.0", @@ -65,6 +66,7 @@ "node-md6": "^0.1.0", "nodom": "^2.4.0", "notepack.io": "^3.0.1", + "ntlm": "^0.1.3", "nwmatcher": "^1.4.4", "otp": "0.1.3", "path": "^0.12.7", @@ -1667,6 +1669,14 @@ "node": ">=6.9.0" } }, + "node_modules/@blu3r4y/lzma": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@blu3r4y/lzma/-/lzma-2.3.3.tgz", + "integrity": "sha512-2ckRSsYewLAgq/s8tUW3o5gurtCNYga1f9l0egV4QlT8hgVEilQHRt18s+behmPL2M/BPBxUINaOz67u++r0wA==", + "bin": { + "lzma.js": "bin/lzma.js" + } + }, "node_modules/@codemirror/commands": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.0.tgz", @@ -8420,8 +8430,9 @@ "license": "ISC" }, "node_modules/libyara-wasm": { - "version": "1.1.0", - "license": "ISC" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-1.2.1.tgz", + "integrity": "sha512-PNqUNWnwjZLe55iA8Rv6vLQRjSdO2OnVg24aRE8v+ytR8CRB8agIG6pS9h2VQejuJP1A/uR4pwcBggUxoNC7DA==" }, "node_modules/lie": { "version": "3.3.0", @@ -9660,6 +9671,14 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/ntlm": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ntlm/-/ntlm-0.1.3.tgz", + "integrity": "sha512-pPlHxhAegZP4QAaOYd51vRd6VXTGfF7VLKJwuwN0iEB1aIi3SnqXYuS/bH/6wWBOq+Ehdil49mHm1Nseon085w==", + "engines": [ + "node" + ] + }, "node_modules/nwmatcher": { "version": "1.4.4", "license": "MIT" @@ -14008,6 +14027,11 @@ "to-fast-properties": "^2.0.0" } }, + "@blu3r4y/lzma": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@blu3r4y/lzma/-/lzma-2.3.3.tgz", + "integrity": "sha512-2ckRSsYewLAgq/s8tUW3o5gurtCNYga1f9l0egV4QlT8hgVEilQHRt18s+behmPL2M/BPBxUINaOz67u++r0wA==" + }, "@codemirror/commands": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.0.tgz", @@ -18472,7 +18496,9 @@ "version": "0.0.4" }, "libyara-wasm": { - "version": "1.1.0" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-1.2.1.tgz", + "integrity": "sha512-PNqUNWnwjZLe55iA8Rv6vLQRjSdO2OnVg24aRE8v+ytR8CRB8agIG6pS9h2VQejuJP1A/uR4pwcBggUxoNC7DA==" }, "lie": { "version": "3.3.0", @@ -19298,6 +19324,11 @@ "boolbase": "^1.0.0" } }, + "ntlm": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ntlm/-/ntlm-0.1.3.tgz", + "integrity": "sha512-pPlHxhAegZP4QAaOYd51vRd6VXTGfF7VLKJwuwN0iEB1aIi3SnqXYuS/bH/6wWBOq+Ehdil49mHm1Nseon085w==" + }, "nwmatcher": { "version": "1.4.4" }, diff --git a/package.json b/package.json index aa425261..ec5fc6d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "9.46.5", + "version": "9.48.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", @@ -92,6 +92,7 @@ }, "dependencies": { "@babel/polyfill": "^7.12.1", + "@blu3r4y/lzma": "^2.3.3", "arrive": "^2.4.1", "avsc": "^5.7.4", "bcryptjs": "^2.4.3", @@ -133,7 +134,7 @@ "jsrsasign": "^10.5.23", "kbpgp": "2.1.15", "libbzip2-wasm": "0.0.4", - "libyara-wasm": "^1.1.0", + "libyara-wasm": "^1.2.1", "lodash": "^4.17.21", "loglevel": "^1.8.0", "loglevel-message-prefix": "^3.0.0", @@ -146,6 +147,7 @@ "node-md6": "^0.1.0", "nodom": "^2.4.0", "notepack.io": "^3.0.1", + "ntlm": "^0.1.3", "nwmatcher": "^1.4.4", "otp": "0.1.3", "path": "^0.12.7", @@ -174,7 +176,7 @@ "build": "npx grunt prod", "node": "npx grunt node", "repl": "node --experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings src/node/repl.mjs", - "test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/operations/index.mjs", + "test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider tests/operations/index.mjs", "testnodeconsumer": "npx grunt testnodeconsumer", "testui": "npx grunt testui", "testuidev": "npx nightwatch --env=dev", diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index fec3b9be..e9944868 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -217,7 +217,7 @@ class Utils { * Utils.parseEscapedChars("\\n"); */ static parseEscapedChars(str) { - return str.replace(/\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a) { + return str.replace(/\\([abfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a) { switch (a[0]) { case "\\": return "\\"; @@ -230,6 +230,8 @@ class Utils { case "6": case "7": return String.fromCharCode(parseInt(a, 8)); + case "a": + return String.fromCharCode(7); case "b": return "\b"; case "t": diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8ac60048..43d5dc4e 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -330,8 +330,10 @@ "Bzip2 Compress", "Tar", "Untar", + "LZString Decompress", "LZString Compress", - "LZString Decompress" + "LZMA Decompress", + "LZMA Compress" ] }, { @@ -367,6 +369,8 @@ "Bcrypt compare", "Bcrypt parse", "Scrypt", + "NT Hash", + "LM Hash", "Fletcher-8 Checksum", "Fletcher-16 Checksum", "Fletcher-32 Checksum", diff --git a/src/core/operations/DNSOverHTTPS.mjs b/src/core/operations/DNSOverHTTPS.mjs index 4dde2f13..87381226 100644 --- a/src/core/operations/DNSOverHTTPS.mjs +++ b/src/core/operations/DNSOverHTTPS.mjs @@ -51,10 +51,27 @@ class DNSOverHTTPS extends Operation { value: [ "A", "AAAA", - "TXT", - "MX", + "ANAME", + "CERT", + "CNAME", "DNSKEY", - "NS" + "HTTPS", + "IPSECKEY", + "LOC", + "MX", + "NS", + "OPENPGPKEY", + "PTR", + "RRSIG", + "SIG", + "SOA", + "SPF", + "SRV", + "SSHFP", + "TA", + "TXT", + "URI", + "ANY" ] }, { diff --git a/src/core/operations/GenerateAllHashes.mjs b/src/core/operations/GenerateAllHashes.mjs index 0b4560cc..2a4a2b1a 100644 --- a/src/core/operations/GenerateAllHashes.mjs +++ b/src/core/operations/GenerateAllHashes.mjs @@ -1,5 +1,6 @@ /** * @author n1474335 [n1474335@gmail.com] + * @author john19696 [john19696@protonmail.com] * @copyright Crown Copyright 2016 * @license Apache-2.0 */ @@ -33,6 +34,9 @@ import BLAKE2b from "./BLAKE2b.mjs"; import BLAKE2s from "./BLAKE2s.mjs"; import Streebog from "./Streebog.mjs"; import GOSTHash from "./GOSTHash.mjs"; +import LMHash from "./LMHash.mjs"; +import NTHash from "./NTHash.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Generate all hashes operation @@ -51,7 +55,75 @@ class GenerateAllHashes extends Operation { this.infoURL = "https://wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions"; this.inputType = "ArrayBuffer"; this.outputType = "string"; - this.args = []; + this.args = [ + { + name: "Length (bits)", + type: "option", + value: [ + "All", "128", "160", "224", "256", "320", "384", "512" + ] + }, + { + name: "Include names", + type: "boolean", + value: true + }, + ]; + this.hashes = [ + {name: "MD2", algo: (new MD2()), inputType: "arrayBuffer", params: []}, + {name: "MD4", algo: (new MD4()), inputType: "arrayBuffer", params: []}, + {name: "MD5", algo: (new MD5()), inputType: "arrayBuffer", params: []}, + {name: "MD6", algo: (new MD6()), inputType: "str", params: []}, + {name: "SHA0", algo: (new SHA0()), inputType: "arrayBuffer", params: []}, + {name: "SHA1", algo: (new SHA1()), inputType: "arrayBuffer", params: []}, + {name: "SHA2 224", algo: (new SHA2()), inputType: "arrayBuffer", params: ["224"]}, + {name: "SHA2 256", algo: (new SHA2()), inputType: "arrayBuffer", params: ["256"]}, + {name: "SHA2 384", algo: (new SHA2()), inputType: "arrayBuffer", params: ["384"]}, + {name: "SHA2 512", algo: (new SHA2()), inputType: "arrayBuffer", params: ["512"]}, + {name: "SHA3 224", algo: (new SHA3()), inputType: "arrayBuffer", params: ["224"]}, + {name: "SHA3 256", algo: (new SHA3()), inputType: "arrayBuffer", params: ["256"]}, + {name: "SHA3 384", algo: (new SHA3()), inputType: "arrayBuffer", params: ["384"]}, + {name: "SHA3 512", algo: (new SHA3()), inputType: "arrayBuffer", params: ["512"]}, + {name: "Keccak 224", algo: (new Keccak()), inputType: "arrayBuffer", params: ["224"]}, + {name: "Keccak 256", algo: (new Keccak()), inputType: "arrayBuffer", params: ["256"]}, + {name: "Keccak 384", algo: (new Keccak()), inputType: "arrayBuffer", params: ["384"]}, + {name: "Keccak 512", algo: (new Keccak()), inputType: "arrayBuffer", params: ["512"]}, + {name: "Shake 128", algo: (new Shake()), inputType: "arrayBuffer", params: ["128", 256]}, + {name: "Shake 256", algo: (new Shake()), inputType: "arrayBuffer", params: ["256", 512]}, + {name: "RIPEMD-128", algo: (new RIPEMD()), inputType: "arrayBuffer", params: ["128"]}, + {name: "RIPEMD-160", algo: (new RIPEMD()), inputType: "arrayBuffer", params: ["160"]}, + {name: "RIPEMD-256", algo: (new RIPEMD()), inputType: "arrayBuffer", params: ["256"]}, + {name: "RIPEMD-320", algo: (new RIPEMD()), inputType: "arrayBuffer", params: ["320"]}, + {name: "HAS-160", algo: (new HAS160()), inputType: "arrayBuffer", params: []}, + {name: "Whirlpool-0", algo: (new Whirlpool()), inputType: "arrayBuffer", params: ["Whirlpool-0"]}, + {name: "Whirlpool-T", algo: (new Whirlpool()), inputType: "arrayBuffer", params: ["Whirlpool-T"]}, + {name: "Whirlpool", algo: (new Whirlpool()), inputType: "arrayBuffer", params: ["Whirlpool"]}, + {name: "BLAKE2b-128", algo: (new BLAKE2b), inputType: "arrayBuffer", params: ["128", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2b-160", algo: (new BLAKE2b), inputType: "arrayBuffer", params: ["160", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2b-256", algo: (new BLAKE2b), inputType: "arrayBuffer", params: ["256", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2b-384", algo: (new BLAKE2b), inputType: "arrayBuffer", params: ["384", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2b-512", algo: (new BLAKE2b), inputType: "arrayBuffer", params: ["512", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2s-128", algo: (new BLAKE2s), inputType: "arrayBuffer", params: ["128", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2s-160", algo: (new BLAKE2s), inputType: "arrayBuffer", params: ["160", "Hex", {string: "", option: "UTF8"}]}, + {name: "BLAKE2s-256", algo: (new BLAKE2s), inputType: "arrayBuffer", params: ["256", "Hex", {string: "", option: "UTF8"}]}, + {name: "Streebog-256", algo: (new Streebog), inputType: "arrayBuffer", params: ["256"]}, + {name: "Streebog-512", algo: (new Streebog), inputType: "arrayBuffer", params: ["512"]}, + {name: "GOST", algo: (new GOSTHash), inputType: "arrayBuffer", params: ["D-A"]}, + {name: "LM Hash", algo: (new LMHash), inputType: "str", params: []}, + {name: "NT Hash", algo: (new NTHash), inputType: "str", params: []}, + {name: "SSDEEP", algo: (new SSDEEP()), inputType: "str"}, + {name: "CTPH", algo: (new CTPH()), inputType: "str"} + ]; + this.checksums = [ + {name: "Fletcher-8", algo: (new Fletcher8Checksum), inputType: "byteArray", params: []}, + {name: "Fletcher-16", algo: (new Fletcher16Checksum), inputType: "byteArray", params: []}, + {name: "Fletcher-32", algo: (new Fletcher32Checksum), inputType: "byteArray", params: []}, + {name: "Fletcher-64", algo: (new Fletcher64Checksum), inputType: "byteArray", params: []}, + {name: "Adler-32", algo: (new Adler32Checksum), inputType: "byteArray", params: []}, + {name: "CRC-8", algo: (new CRC8Checksum), inputType: "arrayBuffer", params: ["CRC-8"]}, + {name: "CRC-16", algo: (new CRC16Checksum), inputType: "arrayBuffer", params: []}, + {name: "CRC-32", algo: (new CRC32Checksum), inputType: "arrayBuffer", params: []} + ]; } /** @@ -60,63 +132,74 @@ class GenerateAllHashes extends Operation { * @returns {string} */ run(input, args) { - const arrayBuffer = input, - str = Utils.arrayBufferToStr(arrayBuffer, false), - byteArray = new Uint8Array(arrayBuffer), - output = "MD2: " + (new MD2()).run(arrayBuffer, []) + - "\nMD4: " + (new MD4()).run(arrayBuffer, []) + - "\nMD5: " + (new MD5()).run(arrayBuffer, []) + - "\nMD6: " + (new MD6()).run(str, []) + - "\nSHA0: " + (new SHA0()).run(arrayBuffer, []) + - "\nSHA1: " + (new SHA1()).run(arrayBuffer, []) + - "\nSHA2 224: " + (new SHA2()).run(arrayBuffer, ["224"]) + - "\nSHA2 256: " + (new SHA2()).run(arrayBuffer, ["256"]) + - "\nSHA2 384: " + (new SHA2()).run(arrayBuffer, ["384"]) + - "\nSHA2 512: " + (new SHA2()).run(arrayBuffer, ["512"]) + - "\nSHA3 224: " + (new SHA3()).run(arrayBuffer, ["224"]) + - "\nSHA3 256: " + (new SHA3()).run(arrayBuffer, ["256"]) + - "\nSHA3 384: " + (new SHA3()).run(arrayBuffer, ["384"]) + - "\nSHA3 512: " + (new SHA3()).run(arrayBuffer, ["512"]) + - "\nKeccak 224: " + (new Keccak()).run(arrayBuffer, ["224"]) + - "\nKeccak 256: " + (new Keccak()).run(arrayBuffer, ["256"]) + - "\nKeccak 384: " + (new Keccak()).run(arrayBuffer, ["384"]) + - "\nKeccak 512: " + (new Keccak()).run(arrayBuffer, ["512"]) + - "\nShake 128: " + (new Shake()).run(arrayBuffer, ["128", 256]) + - "\nShake 256: " + (new Shake()).run(arrayBuffer, ["256", 512]) + - "\nRIPEMD-128: " + (new RIPEMD()).run(arrayBuffer, ["128"]) + - "\nRIPEMD-160: " + (new RIPEMD()).run(arrayBuffer, ["160"]) + - "\nRIPEMD-256: " + (new RIPEMD()).run(arrayBuffer, ["256"]) + - "\nRIPEMD-320: " + (new RIPEMD()).run(arrayBuffer, ["320"]) + - "\nHAS-160: " + (new HAS160()).run(arrayBuffer, []) + - "\nWhirlpool-0: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-0"]) + - "\nWhirlpool-T: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-T"]) + - "\nWhirlpool: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool"]) + - "\nBLAKE2b-128: " + (new BLAKE2b).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-160: " + (new BLAKE2b).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-256: " + (new BLAKE2b).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-384: " + (new BLAKE2b).run(arrayBuffer, ["384", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-512: " + (new BLAKE2b).run(arrayBuffer, ["512", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-128: " + (new BLAKE2s).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-160: " + (new BLAKE2s).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-256: " + (new BLAKE2s).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + - "\nStreebog-256: " + (new Streebog).run(arrayBuffer, ["256"]) + - "\nStreebog-512: " + (new Streebog).run(arrayBuffer, ["512"]) + - "\nGOST: " + (new GOSTHash).run(arrayBuffer, ["D-A"]) + - "\nSSDEEP: " + (new SSDEEP()).run(str) + - "\nCTPH: " + (new CTPH()).run(str) + - "\n\nChecksums:" + - "\nFletcher-8: " + (new Fletcher8Checksum).run(byteArray, []) + - "\nFletcher-16: " + (new Fletcher16Checksum).run(byteArray, []) + - "\nFletcher-32: " + (new Fletcher32Checksum).run(byteArray, []) + - "\nFletcher-64: " + (new Fletcher64Checksum).run(byteArray, []) + - "\nAdler-32: " + (new Adler32Checksum).run(byteArray, []) + - "\nCRC-8: " + (new CRC8Checksum).run(arrayBuffer, ["CRC-8"]) + - "\nCRC-16: " + (new CRC16Checksum).run(arrayBuffer, []) + - "\nCRC-32: " + (new CRC32Checksum).run(arrayBuffer, []); + const [length, includeNames] = args; + this.inputArrayBuffer = input; + this.inputStr = Utils.arrayBufferToStr(input, false); + this.inputByteArray = new Uint8Array(input); + + let digest, output = ""; + // iterate over each of the hashes + this.hashes.forEach(hash => { + digest = this.executeAlgo(hash.algo, hash.inputType, hash.params || []); + output += this.formatDigest(digest, length, includeNames, hash.name); + }); + + if (length === "All") { + output += "\nChecksums:\n"; + this.checksums.forEach(checksum => { + digest = this.executeAlgo(checksum.algo, checksum.inputType, checksum.params || []); + output += this.formatDigest(digest, length, includeNames, checksum.name); + }); + } return output; } + /** + * Executes a hash or checksum algorithm + * + * @param {Function} algo - The hash or checksum algorithm + * @param {string} inputType + * @param {Object[]} [params=[]] + * @returns {string} + */ + executeAlgo(algo, inputType, params=[]) { + let digest = null; + switch (inputType) { + case "arrayBuffer": + digest = algo.run(this.inputArrayBuffer, params); + break; + case "str": + digest = algo.run(this.inputStr, params); + break; + case "byteArray": + digest = algo.run(this.inputByteArray, params); + break; + default: + throw new OperationError("Unknown hash input type: " + inputType); + } + + return digest; + } + + /** + * Formats the digest depending on user-specified arguments + * @param {string} digest + * @param {string} length + * @param {boolean} includeNames + * @param {string} name + * @returns {string} + */ + formatDigest(digest, length, includeNames, name) { + if (length !== "All" && (digest.length * 4) !== parseInt(length, 10)) + return ""; + + if (!includeNames) + return digest + "\n"; + + return `${name}:${" ".repeat(13-name.length)}${digest}\n`; + } + } export default GenerateAllHashes; diff --git a/src/core/operations/JWTDecode.mjs b/src/core/operations/JWTDecode.mjs index 07f19477..b6356b5a 100644 --- a/src/core/operations/JWTDecode.mjs +++ b/src/core/operations/JWTDecode.mjs @@ -26,6 +26,13 @@ class JWTDecode extends Operation { this.inputType = "string"; this.outputType = "JSON"; this.args = []; + this.checks = [ + { + pattern: "^ey([A-Za-z0-9_-]+)\\.ey([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+)$", + flags: "", + args: [] + }, + ]; } /** diff --git a/src/core/operations/LMHash.mjs b/src/core/operations/LMHash.mjs new file mode 100644 index 00000000..2bedf0e8 --- /dev/null +++ b/src/core/operations/LMHash.mjs @@ -0,0 +1,41 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import {smbhash} from "ntlm"; + +/** + * LM Hash operation + */ +class LMHash extends Operation { + + /** + * LMHash constructor + */ + constructor() { + super(); + + this.name = "LM Hash"; + this.module = "Crypto"; + this.description = "An LM Hash, or LAN Manager Hash, is a deprecated way of storing passwords on old Microsoft operating systems. It is particularly weak and can be cracked in seconds on modern hardware using rainbow tables."; + this.infoURL = "https://wikipedia.org/wiki/LAN_Manager#Password_hashing_algorithm"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return smbhash.lmhash(input); + } + +} + +export default LMHash; diff --git a/src/core/operations/LZMACompress.mjs b/src/core/operations/LZMACompress.mjs new file mode 100644 index 00000000..5a252db2 --- /dev/null +++ b/src/core/operations/LZMACompress.mjs @@ -0,0 +1,64 @@ +/** + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +import { compress } from "@blu3r4y/lzma"; +import {isWorkerEnvironment} from "../Utils.mjs"; + +/** + * LZMA Compress operation + */ +class LZMACompress extends Operation { + + /** + * LZMACompress constructor + */ + constructor() { + super(); + + this.name = "LZMA Compress"; + this.module = "Compression"; + this.description = "Compresses data using the Lempel\u2013Ziv\u2013Markov chain algorithm. Compression mode determines the speed and effectiveness of the compression: 1 is fastest and less effective, 9 is slowest and most effective"; + this.infoURL = "https://wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.args = [ + { + name: "Compression Mode", + type: "option", + value: [ + "1", "2", "3", "4", "5", "6", "7", "8", "9" + ], + "defaultIndex": 6 + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {ArrayBuffer} + */ + async run(input, args) { + const mode = Number(args[0]); + return new Promise((resolve, reject) => { + compress(new Uint8Array(input), mode, (result, error) => { + if (error) { + reject(new OperationError(`Failed to compress input: ${error.message}`)); + } + // The compression returns as an Int8Array, but we can just get the unsigned data from the buffer + resolve(new Int8Array(result).buffer); + }, (percent) => { + if (isWorkerEnvironment()) self.sendStatusMessage(`Compressing input: ${(percent*100).toFixed(2)}%`); + }); + }); + } + +} + +export default LZMACompress; diff --git a/src/core/operations/LZMADecompress.mjs b/src/core/operations/LZMADecompress.mjs new file mode 100644 index 00000000..3bebb860 --- /dev/null +++ b/src/core/operations/LZMADecompress.mjs @@ -0,0 +1,57 @@ +/** + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {decompress} from "@blu3r4y/lzma"; +import Utils, {isWorkerEnvironment} from "../Utils.mjs"; + +/** + * LZMA Decompress operation + */ +class LZMADecompress extends Operation { + + /** + * LZMADecompress constructor + */ + constructor() { + super(); + + this.name = "LZMA Decompress"; + this.module = "Compression"; + this.description = "Decompresses data using the Lempel-Ziv-Markov chain Algorithm."; + this.infoURL = "https://wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Markov_chain_algorithm"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {ArrayBuffer} + */ + async run(input, args) { + return new Promise((resolve, reject) => { + decompress(new Uint8Array(input), (result, error) => { + if (error) { + reject(new OperationError(`Failed to decompress input: ${error.message}`)); + } + // The decompression returns either a String or an untyped unsigned int8 array, but we can just get the unsigned data from the buffer + + if (typeof result == "string") { + resolve(Utils.strToArrayBuffer(result)); + } else { + resolve(new Int8Array(result).buffer); + } + }, (percent) => { + if (isWorkerEnvironment()) self.sendStatusMessage(`Decompressing input: ${(percent*100).toFixed(2)}%`); + }); + }); + } + +} + +export default LZMADecompress; diff --git a/src/core/operations/NTHash.mjs b/src/core/operations/NTHash.mjs new file mode 100644 index 00000000..51318135 --- /dev/null +++ b/src/core/operations/NTHash.mjs @@ -0,0 +1,46 @@ +/** + * @author brun0ne [brunonblok@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; + +import cptable from "codepage"; +import {runHash} from "../lib/Hash.mjs"; + +/** + * NT Hash operation + */ +class NTHash extends Operation { + + /** + * NTHash constructor + */ + constructor() { + super(); + + this.name = "NT Hash"; + this.module = "Crypto"; + this.description = "An NT Hash, sometimes referred to as an NTLM hash, is a method of storing passwords on Windows systems. It works by running MD4 on UTF-16LE encoded input. NTLM hashes are considered weak because they can be brute-forced very easily with modern hardware."; + this.infoURL = "https://wikipedia.org/wiki/NT_LAN_Manager"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const format = 1200; // UTF-16LE + const encoded = cptable.utils.encode(format, input); + const hashed = runHash("md4", encoded); + + return hashed.toUpperCase(); + } +} + +export default NTHash; diff --git a/src/core/operations/ParseSSHHostKey.mjs b/src/core/operations/ParseSSHHostKey.mjs index 8a3bf5da..f1a1f58c 100644 --- a/src/core/operations/ParseSSHHostKey.mjs +++ b/src/core/operations/ParseSSHHostKey.mjs @@ -23,7 +23,7 @@ class ParseSSHHostKey extends Operation { this.name = "Parse SSH Host Key"; this.module = "Default"; - this.description = "Parses a SSH host key and extracts fields from it.
The key type can be:The key format can be either Hex or Base64."; + this.description = "Parses a SSH host key and extracts fields from it.
The key type can be:The key format can be either Hex or Base64."; this.infoURL = "https://wikipedia.org/wiki/Secure_Shell"; this.inputType = "string"; this.outputType = "string"; @@ -71,6 +71,8 @@ class ParseSSHHostKey extends Operation { } else if (keyType.startsWith("ecdsa-sha2")) { output += `\nCurve: ${Utils.byteArrayToChars(fromHex(fields[1]))}`; output += `\nPoint: 0x${fields.slice(2)}`; + } else if (keyType === "ssh-ed25519") { + output += `\nx: 0x${fields[1]}`; } else { output += "\nUnsupported key type."; output += `\nParameters: ${fields.slice(1)}`; diff --git a/src/core/operations/RawInflate.mjs b/src/core/operations/RawInflate.mjs index 4555f287..f5b57bde 100644 --- a/src/core/operations/RawInflate.mjs +++ b/src/core/operations/RawInflate.mjs @@ -7,7 +7,6 @@ import Operation from "../Operation.mjs"; import {INFLATE_BUFFER_TYPE} from "../lib/Zlib.mjs"; import rawinflate from "zlibjs/bin/rawinflate.min.js"; -import OperationError from "../errors/OperationError.mjs"; const Zlib = rawinflate.Zlib; @@ -83,25 +82,6 @@ class RawInflate extends Operation { }), result = new Uint8Array(inflate.decompress()); - // Raw Inflate sometimes messes up and returns nonsense like this: - // ]....]....]....]....]....]....]....]....]....]....]....]....]....]... - // e.g. Input data of [8b, 1d, dc, 44] - // Look for the first two square brackets: - if (result.length > 158 && result[0] === 93 && result[5] === 93) { - // If the first two square brackets are there, check that the others - // are also there. If they are, throw an error. If not, continue. - let valid = false; - for (let i = 0; i < 155; i += 5) { - if (result[i] !== 93) { - valid = true; - } - } - - if (!valid) { - throw new OperationError("Error: Unable to inflate data"); - } - } - // This seems to be the easiest way... return result.buffer; } diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index e654cc6d..d91f50ae 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -52,7 +52,17 @@ class YARARules extends Operation { name: "Show counts", type: "boolean", value: true - } + }, + { + name: "Show rule warnings", + type: "boolean", + value: true + }, + { + name: "Show console module messages", + type: "boolean", + value: true + }, ]; } @@ -64,7 +74,7 @@ class YARARules extends Operation { async run(input, args) { if (isWorkerEnvironment()) self.sendStatusMessage("Instantiating YARA..."); - const [rules, showStrings, showLengths, showMeta, showCounts] = args; + const [rules, showStrings, showLengths, showMeta, showCounts, showRuleWarns, showConsole] = args; return new Promise((resolve, reject) => { Yara().then(yara => { if (isWorkerEnvironment()) self.sendStatusMessage("Converting data for YARA."); @@ -83,11 +93,19 @@ class YARARules extends Operation { const compileError = resp.compileErrors.get(i); if (!compileError.warning) { reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); - } else { - matchString += `Warning on line ${compileError.lineNumber}: ${compileError.message}`; + } else if (showRuleWarns) { + matchString += `Warning on line ${compileError.lineNumber}: ${compileError.message}\n`; } } } + + if (showConsole) { + const consoleLogs = resp.consoleLogs; + for (let i = 0; i < consoleLogs.size(); i++) { + matchString += consoleLogs.get(i) + "\n"; + } + } + const matchedRules = resp.matchedRules; for (let i = 0; i < matchedRules.size(); i++) { const rule = matchedRules.get(i); @@ -100,11 +118,11 @@ class YARARules extends Operation { } meta = meta.slice(0, -2) + "]"; } - const countString = showCounts ? `${matches.size()} time${matches.size() > 1 ? "s" : ""}` : ""; + const countString = matches.size() === 0 ? "" : (showCounts ? ` (${matches.size()} time${matches.size() > 1 ? "s" : ""})` : ""); if (matches.size() === 0 || !(showStrings || showLengths)) { matchString += `Input matches rule "${rule.ruleName}"${meta}${countString.length > 0 ? ` ${countString}`: ""}.\n`; } else { - matchString += `Rule "${rule.ruleName}"${meta} matches (${countString}):\n`; + matchString += `Rule "${rule.ruleName}"${meta} matches${countString}:\n`; for (let j = 0; j < matches.size(); j++) { const match = matches.get(j); if (showStrings || showLengths) { diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index f4294cad..19e70970 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -45,6 +45,7 @@ import "./tests/DateTime.mjs"; import "./tests/ExtractEmailAddresses.mjs"; import "./tests/Fork.mjs"; import "./tests/FromDecimal.mjs"; +import "./tests/GenerateAllHashes.mjs"; import "./tests/Gzip.mjs"; import "./tests/Gunzip.mjs"; import "./tests/Hash.mjs"; @@ -119,9 +120,10 @@ import "./tests/SIGABA.mjs"; import "./tests/ELFInfo.mjs"; import "./tests/Subsection.mjs"; import "./tests/CaesarBoxCipher.mjs"; +import "./tests/UnescapeString.mjs"; import "./tests/LS47.mjs"; import "./tests/LZString.mjs"; - +import "./tests/NTLM.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; diff --git a/tests/operations/tests/Compress.mjs b/tests/operations/tests/Compress.mjs index a1e895bb..015277b1 100644 --- a/tests/operations/tests/Compress.mjs +++ b/tests/operations/tests/Compress.mjs @@ -23,4 +23,56 @@ TestRegister.addTests([ } ], }, + { + name: "LZMA compress & decompress", + input: "The cat sat on the mat.", + // Generated using command `echo -n "The cat sat on the mat." | lzma -z -6 | xxd -p` + expectedOutput: "The cat sat on the mat.", + recipeConfig: [ + { + "op": "LZMA Compress", + "args": ["6"] + }, + { + "op": "LZMA Decompress", + "args": [] + }, + ], + }, + { + name: "LZMA decompress: binary", + // Generated using command `echo "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10" | xxd -r -p | lzma -z -6 | xxd -p` + input: "5d00008000ffffffffffffffff00000052500a84f99bb28021a969d627e03e8a922effffbd160000", + expectedOutput: "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "LZMA Decompress", + "args": [] + }, + { + "op": "To Hex", + "args": ["Space", 0] + } + ], + }, + { + name: "LZMA decompress: string", + // Generated using command `echo -n "The cat sat on the mat." | lzma -z -6 | xxd -p` + input: "5d00008000ffffffffffffffff002a1a08a202b1a4b814b912c94c4152e1641907d3fd8cd903ffff4fec0000", + expectedOutput: "The cat sat on the mat.", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "LZMA Decompress", + "args": [] + } + ], + }, ]); diff --git a/tests/operations/tests/GenerateAllHashes.mjs b/tests/operations/tests/GenerateAllHashes.mjs new file mode 100644 index 00000000..28707bde --- /dev/null +++ b/tests/operations/tests/GenerateAllHashes.mjs @@ -0,0 +1,115 @@ +/** + * GenerateAllHashes tests. + * + * @author john19696 [john19696@protonmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Full generate all hashes", + input: "test", + expectedOutput: `MD2: dd34716876364a02d0195e2fb9ae2d1b +MD4: db346d691d7acc4dc2625db19f9e3f52 +MD5: 098f6bcd4621d373cade4e832627b4f6 +MD6: 93c8a7d0ff132f325138a82b2baa98c12a7c9ac982feb6c5b310a1ca713615bd +SHA0: f8d3b312442a67706057aeb45b983221afb4f035 +SHA1: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +SHA2 224: 90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809 +SHA2 256: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 +SHA2 384: 768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9 +SHA2 512: ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff +SHA3 224: 3797bf0afbbfca4a7bbba7602a2b552746876517a7f9b7ce2db0ae7b +SHA3 256: 36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80 +SHA3 384: e516dabb23b6e30026863543282780a3ae0dccf05551cf0295178d7ff0f1b41eecb9db3ff219007c4e097260d58621bd +SHA3 512: 9ece086e9bac491fac5c1d1046ca11d737b92a2b2ebd93f005d7b710110c0a678288166e7fbe796883a4f2e9b3ca9f484f521d0ce464345cc1aec96779149c14 +Keccak 224: 3be30a9ff64f34a5861116c5198987ad780165f8366e67aff4760b5e +Keccak 256: 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 +Keccak 384: 53d0ba137307d4c2f9b6674c83edbd58b70c0f4340133ed0adc6fba1d2478a6a03b7788229e775d2de8ae8c0759d0527 +Keccak 512: 1e2e9fc2002b002d75198b7503210c05a1baac4560916a3c6d93bcce3a50d7f00fd395bf1647b9abb8d1afcc9c76c289b0c9383ba386a956da4b38934417789e +Shake 128: d3b0aa9cd8b7255622cebc631e867d4093d6f6010191a53973c45fec9b07c774 +Shake 256: b54ff7255705a71ee2925e4a3e30e41aed489a579d5595e0df13e32e1e4dd202a7c7f68b31d6418d9845eb4d757adda6ab189e1bb340db818e5b3bc725d992fa +RIPEMD-128: f1abb5083c9ff8a9dbbca9cd2b11fead +RIPEMD-160: 5e52fee47e6b070565f74372468cdc699de89107 +RIPEMD-256: fe0289110d07daeee9d9500e14c57787d9083f6ba10e6bcb256f86bb4fe7b981 +RIPEMD-320: 3b0a2e841e589cf583634a5dd265d2b5d497c4cc44b241e34e0f62d03e98c1b9dc72970b9bc20eb5 +HAS-160: cb15e491eec6e769771d1f811315139c93071084 +Whirlpool-0: d50ff71342b521974bae166539871922669afcfc7181250ebbae015c317ebb797173a69e7a05afd11099a9f0918159cd5bc88434d3ca44513d7263caea9244fe +Whirlpool-T: e6b4aa087751b4428171777f1893ba585404c7e0171787720eba0d8bccd710dc2c42f874c572bfae4cedabf50f2c80bf923805d4e31c504b86ca3bc59265e7dd +Whirlpool: b913d5bbb8e461c2c5961cbe0edcdadfd29f068225ceb37da6defcf89849368f8c6c2eb6a4c4ac75775d032a0ecfdfe8550573062b653fe92fc7b8fb3b7be8d6 +BLAKE2b-128: 44a8995dd50b6657a037a7839304535b +BLAKE2b-160: a34fc3b6d2cce8beb3216c2bbb5e55739e8121ed +BLAKE2b-256: 928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202 +BLAKE2b-384: 8a84b8666c8fcfb69f2ec41f578d7c85fbdb504ea6510fb05b50fcbf7ed8153c77943bc2da73abb136834e1a0d4f22cb +BLAKE2b-512: a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a483aa9bc33b582f77d30a65e6f29a896c0411f38312e1d66e0bf16386c86a89bea572 +BLAKE2s-128: e9ddd9926b9dcb382e09be39ba403d2c +BLAKE2s-160: d6197dabec2bd6f4ff303b8e519e8f15d42a453d +BLAKE2s-256: f308fc02ce9172ad02a7d75800ecfc027109bc67987ea32aba9b8dcc7b10150e +Streebog-256: 12a50838191b5504f1e5f2fd078714cf6b592b9d29af99d0b10d8d02881c3857 +Streebog-512: 7200bf5dea560f0d7960d07fdc8874ad9f3b86ece2e45f5502ae2e176f2c928e0e581152281f5aee818318bed7cbe6aa69999589234723ceb33175598365b5c8 +GOST: ee67303696d205ddd2b2363e8e01b4b7199a80957d94d7678eaad3fc834c5a27 +LM Hash: 01FC5A6BE7BC6929AAD3B435B51404EE +NT Hash: 0CB6948805F797BF2A82807973B89537 +SSDEEP: 3:Hn:Hn +CTPH: A:E:E + +Checksums: +Fletcher-8: 3d +Fletcher-16: 5dc1 +Fletcher-32: 045901c0 +Fletcher-64: 00000459000001c0 +Adler-32: 045d01c1 +CRC-8: b9 +CRC-16: f82e +CRC-32: d87f7e0c +`, + recipeConfig: [ + { + "op": "Generate all hashes", + "args": ["All", true] + } + ] + }, + { + name: "Hashes with length 32", + input: "test", + expectedOutput: `MD2: dd34716876364a02d0195e2fb9ae2d1b +MD4: db346d691d7acc4dc2625db19f9e3f52 +MD5: 098f6bcd4621d373cade4e832627b4f6 +RIPEMD-128: f1abb5083c9ff8a9dbbca9cd2b11fead +BLAKE2b-128: 44a8995dd50b6657a037a7839304535b +BLAKE2s-128: e9ddd9926b9dcb382e09be39ba403d2c +LM Hash: 01FC5A6BE7BC6929AAD3B435B51404EE +NT Hash: 0CB6948805F797BF2A82807973B89537 +`, + recipeConfig: [ + { + "op": "Generate all hashes", + "args": ["128", true] + } + ] + }, + { + name: "Hashes without names", + input: "test", + expectedOutput: `93c8a7d0ff132f325138a82b2baa98c12a7c9ac982feb6c5b310a1ca713615bd +9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 +36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80 +9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 +d3b0aa9cd8b7255622cebc631e867d4093d6f6010191a53973c45fec9b07c774 +fe0289110d07daeee9d9500e14c57787d9083f6ba10e6bcb256f86bb4fe7b981 +928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202 +f308fc02ce9172ad02a7d75800ecfc027109bc67987ea32aba9b8dcc7b10150e +12a50838191b5504f1e5f2fd078714cf6b592b9d29af99d0b10d8d02881c3857 +ee67303696d205ddd2b2363e8e01b4b7199a80957d94d7678eaad3fc834c5a27 +`, + recipeConfig: [ + { + "op": "Generate all hashes", + "args": ["256", false] + } + ] + } +]); diff --git a/tests/operations/tests/NTLM.mjs b/tests/operations/tests/NTLM.mjs new file mode 100644 index 00000000..6dfa704c --- /dev/null +++ b/tests/operations/tests/NTLM.mjs @@ -0,0 +1,34 @@ +/** + * NTLM test. + * + * @author brun0ne [brunonblok@gmail.com] + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "NT Hash", + input: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*()_+.,?/", + expectedOutput: "C5FA1C40E55734A8E528DBFE21766D23", + recipeConfig: [ + { + op: "NT Hash", + args: [], + }, + ], + }, + { + name: "LM Hash", + input: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*()_+.,?/", + expectedOutput: "6D9DF16655336CA75A3C13DD18BA8156", + recipeConfig: [ + { + op: "LM Hash", + args: [], + }, + ], + }, + +]); diff --git a/tests/operations/tests/ParseSSHHostKey.mjs b/tests/operations/tests/ParseSSHHostKey.mjs index 00e13c6c..296c8bb2 100644 --- a/tests/operations/tests/ParseSSHHostKey.mjs +++ b/tests/operations/tests/ParseSSHHostKey.mjs @@ -49,6 +49,18 @@ Point: 0x046c59592006272250a15070142a6be36d1e45464313f930d985a6e6f0eba3cd39d0367 } ] }, + { + name: "SSH Host Key: Ed25519", + input: "AAAAC3NzaC1lZDI1NTE5AAAAIBOF6r99IkvqGu1kwZrHHIqjpTB5w79bpv67B/Aw3+WJ", + expectedOutput: `Key type: ssh-ed25519 +x: 0x1385eabf7d224bea1aed64c19ac71c8aa3a53079c3bf5ba6febb07f030dfe589`, + recipeConfig: [ + { + op: "Parse SSH Host Key", + args: ["Base64"] + } + ] + }, { name: "SSH Host Key: Extract key", input: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDiJZ/9W9Ix/Dk9b+K4E+RGCug1AtkGXaJ9vNIY0YHFHLpWsB8DAuh/cGEI9TLbL1gzR2wG+RJNQ2EAQVWe6ypkK63Jm4zw4re+vhEiszpnP889J0h5N9yzyTndesrl4d3cQtv861FcKDPxUJbRALdtl6gwOB7BCL8gsXJLLVLO4EesrbPXD454qpVt7CgJXEXByOFjcIm3XwkdOnXMPHHnMSD7EIN1SvQMD6PfIDrbDd6KQt5QXW/Rc/BsfX5cbUIV1QW5A/GbepXHHKmWRtLC2J/mH3hW2Zq/hITPEaJdG1CtIilQmJaZGXpfGIwFeb0Av9pSL926arZZ6vDi9ctF test@test", diff --git a/tests/operations/tests/UnescapeString.mjs b/tests/operations/tests/UnescapeString.mjs new file mode 100644 index 00000000..7d3f9fda --- /dev/null +++ b/tests/operations/tests/UnescapeString.mjs @@ -0,0 +1,55 @@ +/** + * UnescapeString tests. + * + * @copyright Crown Copyright 2022 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "UnescapeString: escape sequences", + input: "\\a\\b\\f\\n\\r\\t\\v\\'\\\"", + expectedOutput: String.fromCharCode(0x07, 0x08, 0x0c, 0x0a, 0x0d, 0x09, + 0x0b, 0x27, 0x22), + recipeConfig: [ + { + op: "Unescape string", + args: [], + }, + ], + }, + { + name: "UnescapeString: octals", + input: "\\0\\01\\012\\1\\12", + expectedOutput: String.fromCharCode(0, 1, 10, 1, 10), + recipeConfig: [ + { + op: "Unescape string", + args: [], + }, + ], + }, + { + name: "UnescapeString: hexadecimals", + input: "\\x00\\xAA\\xaa", + expectedOutput: String.fromCharCode(0, 170, 170), + recipeConfig: [ + { + op: "Unescape string", + args: [], + }, + ], + }, + { + name: "UnescapeString: unicode", + input: "\\u0061\\u{0062}", + expectedOutput: "ab", + recipeConfig: [ + { + op: "Unescape string", + args: [], + }, + ], + }, +]); diff --git a/tests/operations/tests/YARA.mjs b/tests/operations/tests/YARA.mjs index 267af2ef..d92c19aa 100644 --- a/tests/operations/tests/YARA.mjs +++ b/tests/operations/tests/YARA.mjs @@ -8,6 +8,22 @@ */ import TestRegister from "../../lib/TestRegister.mjs"; +const CONSOLE_COMPILE_WARNING_RULE = `import "console" +rule a +{ + strings: + $s=" " + condition: + $s and console.log("log rule a") +} +rule b +{ + strings: + $s=" " + condition: + $s and console.hex("log rule b: int8(0)=", int8(0)) +}`; + TestRegister.addTests([ { name: "YARA Match: simple foobar", @@ -20,5 +36,56 @@ TestRegister.addTests([ } ], }, + { + name: "YARA Match: hashing rules", + input: "Hello World!", + expectedOutput: "Input matches rule \"HelloWorldMD5\".\nInput matches rule \"HelloWorldSHA256\".\n", + recipeConfig: [ + { + "op": "YARA Rules", + "args": [ + `import "hash" + rule HelloWorldMD5 { + condition: + hash.md5(0,filesize) == "ed076287532e86365e841e92bfc50d8c" + } + + rule HelloWorldSHA256 { + condition: + hash.sha256(0,filesize) == "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069" + }`, + true, true, true, true, false, false + ], + } + ], + }, + { + name: "YARA Match: compile warnings", + input: "CyberChef Yara", + expectedOutput: "Warning on line 5: string \"$s\" may slow down scanning\n" + + "Warning on line 12: string \"$s\" may slow down scanning\n" + + "Input matches rule \"a\".\n" + + "Input matches rule \"b\".\n", + recipeConfig: [ + { + "op": "YARA Rules", + "args": [CONSOLE_COMPILE_WARNING_RULE, false, false, false, false, true, false], + } + ], + }, + { + name: "YARA Match: console messages", + input: "CyberChef Yara", + expectedOutput: "log rule a\n" + + "log rule b: int8(0)=0x43\n" + + "Input matches rule \"a\".\n" + + "Input matches rule \"b\".\n", + recipeConfig: [ + { + "op": "YARA Rules", + "args": [CONSOLE_COMPILE_WARNING_RULE, false, false, false, false, false, true], + } + ], + }, ]); diff --git a/webpack.config.js b/webpack.config.js index 9db5462d..50c4c731 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -109,7 +109,8 @@ module.exports = { "buffer": require.resolve("buffer/"), "crypto": require.resolve("crypto-browserify"), "stream": require.resolve("stream-browserify"), - "zlib": require.resolve("browserify-zlib") + "zlib": require.resolve("browserify-zlib"), + "process": false } }, module: {