Merge branch 'master' into master

This commit is contained in:
Jed Laundry 2024-02-11 13:31:58 +13:00 committed by GitHub
commit a942fe92fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 4481 additions and 11709 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ npm-debug.log
travis.log
build
.vscode
.idea
.*.swp
src/core/config/modules/*
src/core/config/OperationConfig.json

View File

@ -13,6 +13,17 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [10.7.0] - 2024-02-09
- Added 'File Tree' operation [@sw5678] | [#1667]
- Added 'RISON' operation [@sg5506844] | [#1555]
- Added 'MurmurHash3' operation [@AliceGrey] | [#1694]
### [10.6.0] - 2024-02-03
- Updated 'Forensics Wiki' URLs to new domain [@a3957273] | [#1703]
- Added 'LZNT1 Decompress' operation [@0xThiebaut] | [#1675]
- Updated 'Regex Expression' UUID matcher [@cnotin] | [#1678]
- Removed duplicate 'hover' message within baking info [@KevinSJ] | [#1541]
### [10.5.0] - 2023-07-14
- Added GOST Encrypt, Decrypt, Sign, Verify, Key Wrap, and Key Unwrap operations [@n1474335] | [#592]
@ -372,8 +383,8 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.7.0]: https://github.com/gchq/CyberChef/releases/tag/v10.7.0
[10.6.0]: https://github.com/gchq/CyberChef/releases/tag/v10.6.0
[10.5.0]: https://github.com/gchq/CyberChef/releases/tag/v10.5.0
[10.4.0]: https://github.com/gchq/CyberChef/releases/tag/v10.4.0
[10.3.0]: https://github.com/gchq/CyberChef/releases/tag/v10.3.0
@ -528,6 +539,14 @@ All major and minor version changes will be documented in this file. Details of
[@joostrijneveld]: https://github.com/joostrijneveld
[@Xenonym]: https://github.com/Xenonym
[@gchq77703]: https://github.com/gchq77703
[@a3957273]: https://github.com/a3957273
[@0xThiebaut]: https://github.com/0xThiebaut
[@cnotin]: https://github.com/cnotin
[@KevinSJ]: https://github.com/KevinSJ
[@sw5678]: https://github.com/sw5678
[@sg5506844]: https://github.com/sg5506844
[@AliceGrey]: https://github.com/AliceGrey
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
@ -646,4 +665,10 @@ All major and minor version changes will be documented in this file. Details of
[#661]: https://github.com/gchq/CyberChef/pull/661
[#493]: https://github.com/gchq/CyberChef/pull/493
[#592]: https://github.com/gchq/CyberChef/issues/592
[#1703]: https://github.com/gchq/CyberChef/issues/1703
[#1675]: https://github.com/gchq/CyberChef/issues/1675
[#1678]: https://github.com/gchq/CyberChef/issues/1678
[#1541]: https://github.com/gchq/CyberChef/issues/1541
[#1667]: https://github.com/gchq/CyberChef/issues/1667
[#1555]: https://github.com/gchq/CyberChef/issues/1555
[#1694]: https://github.com/gchq/CyberChef/issues/1694

View File

@ -89,14 +89,14 @@ CyberChef is built to support
## Node.js support
CyberChef is built to fully support Node.js `v16`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki)
CyberChef is built to fully support Node.js `v16`. For more information, see the ["Node API" wiki page](https://github.com/gchq/CyberChef/wiki/Node-API)
## Contributing
Contributing a new operation to CyberChef is super easy! The quickstart script will walk you through the process. If you can write basic JavaScript, you can write a CyberChef operation.
An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki).
An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the ["Contributing" wiki page](https://github.com/gchq/CyberChef/wiki/Contributing).
- Push your changes to your fork.
- Submit a pull request. If you are doing this for the first time, you will be prompted to sign the [GCHQ Contributor Licence Agreement](https://cla-assistant.io/gchq/CyberChef) via the CLA assistant on the pull request. This will also ask whether you are happy for GCHQ to contact you about a token of thanks for your contribution, or about job opportunities at GCHQ.

15048
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "10.5.2",
"version": "10.7.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@ -39,55 +39,55 @@
"node >= 16"
],
"devDependencies": {
"@babel/core": "^7.21.0",
"@babel/eslint-parser": "^7.19.1",
"@babel/plugin-syntax-import-assertions": "^7.20.0",
"@babel/plugin-transform-runtime": "^7.21.0",
"@babel/preset-env": "^7.20.2",
"@babel/runtime": "^7.21.0",
"@codemirror/commands": "^6.2.1",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.2.3",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.9.2",
"autoprefixer": "^10.4.13",
"babel-loader": "^9.1.2",
"@babel/core": "^7.23.9",
"@babel/eslint-parser": "^7.23.10",
"@babel/plugin-syntax-import-assertions": "^7.23.3",
"@babel/plugin-transform-runtime": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/runtime": "^7.23.9",
"@codemirror/commands": "^6.3.3",
"@codemirror/language": "^6.10.1",
"@codemirror/search": "^6.5.5",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.1",
"autoprefixer": "^10.4.17",
"babel-loader": "^9.1.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0",
"chromedriver": "^114.0.2",
"chromedriver": "^121.0.0",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.29.0",
"css-loader": "6.7.3",
"eslint": "^8.35.0",
"copy-webpack-plugin": "^12.0.2",
"core-js": "^3.35.1",
"css-loader": "6.10.0",
"eslint": "^8.56.0",
"grunt": "^1.6.1",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^3.0.0",
"grunt-contrib-clean": "~2.0.1",
"grunt-contrib-connect": "^3.0.0",
"grunt-contrib-connect": "^4.0.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^24.0.1",
"grunt-eslint": "^24.3.0",
"grunt-exec": "~3.0.0",
"grunt-webpack": "^5.0.0",
"grunt-zip": "^0.20.0",
"html-webpack-plugin": "^5.5.0",
"imports-loader": "^4.0.1",
"mini-css-extract-plugin": "2.7.3",
"grunt-webpack": "^6.0.0",
"grunt-zip": "^1.0.0",
"html-webpack-plugin": "^5.6.0",
"imports-loader": "^5.0.0",
"mini-css-extract-plugin": "2.8.0",
"modify-source-webpack-plugin": "^3.0.0",
"nightwatch": "^2.6.16",
"postcss": "^8.4.21",
"postcss-css-variables": "^0.18.0",
"postcss-import": "^15.1.0",
"postcss-loader": "^7.0.2",
"nightwatch": "^3.4.0",
"postcss": "^8.4.33",
"postcss-css-variables": "^0.19.0",
"postcss-import": "^16.0.0",
"postcss-loader": "^8.1.0",
"prompt": "^1.3.0",
"sitemap": "^7.1.1",
"terser": "^5.16.6",
"webpack": "^5.76.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-dev-server": "4.11.1",
"terser": "^5.27.0",
"webpack": "^5.90.1",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-dev-server": "4.15.1",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
},
@ -100,7 +100,7 @@
"arrive": "^2.4.1",
"avsc": "^5.7.7",
"bcryptjs": "^2.4.3",
"bignumber.js": "^9.1.1",
"bignumber.js": "^9.1.2",
"blakejs": "^1.2.1",
"bootstrap": "4.6.2",
"bootstrap-colorpicker": "^3.4.0",
@ -108,45 +108,45 @@
"browserify-zlib": "^0.2.0",
"bson": "^4.7.2",
"buffer": "^6.0.3",
"cbor": "8.1.0",
"cbor": "9.0.2",
"chi-squared": "^1.1.0",
"codepage": "^1.15.0",
"crypto-api": "^0.8.5",
"crypto-browserify": "^3.12.0",
"crypto-js": "^4.1.1",
"crypto-js": "^4.2.0",
"ctph.js": "0.0.5",
"d3": "7.8.2",
"d3": "7.8.5",
"d3-hexbin": "^0.2.2",
"diff": "^5.1.0",
"es6-promisify": "^7.0.0",
"escodegen": "^2.0.0",
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"file-saver": "^2.0.5",
"flat": "^5.0.2",
"flat": "^6.0.1",
"geodesy": "1.1.3",
"highlight.js": "^11.7.0",
"highlight.js": "^11.9.0",
"jimp": "^0.16.13",
"jquery": "3.6.4",
"jquery": "3.7.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.8.0",
"js-sha3": "^0.9.3",
"jsesc": "^3.0.2",
"json5": "^2.2.3",
"jsonpath-plus": "^7.2.0",
"jsonpath-plus": "^8.0.0",
"jsonwebtoken": "8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^10.6.1",
"jsrsasign": "^11.1.0",
"kbpgp": "2.1.15",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.2.1",
"lodash": "^4.17.21",
"loglevel": "^1.8.1",
"loglevel": "^1.9.1",
"loglevel-message-prefix": "^3.0.0",
"lz-string": "^1.5.0",
"lz4js": "^0.2.0",
"markdown-it": "^13.0.1",
"moment": "^2.29.4",
"moment-timezone": "^0.5.41",
"markdown-it": "^14.0.0",
"moment": "^2.30.1",
"moment-timezone": "^0.5.44",
"ngeohash": "^0.6.3",
"node-forge": "^1.3.1",
"node-md6": "^0.1.0",
@ -158,22 +158,23 @@
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
"protobufjs": "^7.2.2",
"protobufjs": "^7.2.6",
"qr-image": "^3.2.0",
"reflect-metadata": "^0.1.13",
"reflect-metadata": "^0.2.1",
"rison": "^0.1.1",
"scryptsy": "^2.1.0",
"snackbarjs": "^1.1.0",
"sortablejs": "^1.15.0",
"sortablejs": "^1.15.2",
"split.js": "^1.6.5",
"ssdeep.js": "0.0.3",
"stream-browserify": "^3.0.0",
"tesseract.js": "3.0.3",
"ua-parser-js": "^1.0.34",
"tesseract.js": "5.0.4",
"ua-parser-js": "^1.0.37",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"xmldom": "^0.6.0",
"xpath": "0.0.32",
"xpath": "0.0.34",
"xregexp": "^5.1.1",
"zlibjs": "^0.3.1"
},

View File

@ -892,6 +892,23 @@ class Utils {
}
/**
* Converts a string to it's title case equivalent.
*
* @param {string} str
* @returns string
*
* @example
* // return "A Tiny String"
* Utils.toTitleCase("a tIny String");
*/
static toTitleCase(str) {
return str.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
/**
* Encodes a URI fragment (#) or query (?) using a minimal amount of percent-encoding.
*

View File

@ -29,6 +29,8 @@
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base92",
"From Base92",
"To Base85",
"From Base85",
"To Base",
@ -67,7 +69,9 @@
"JSON to CSV",
"Avro to JSON",
"CBOR Encode",
"CBOR Decode"
"CBOR Decode",
"Rison Encode",
"Rison Decode"
]
},
{
@ -294,7 +298,8 @@
"Escape string",
"Unescape string",
"Pseudo-Random Number Generator",
"Sleep"
"Sleep",
"File Tree"
]
},
{
@ -351,7 +356,8 @@
"LZMA Decompress",
"LZMA Compress",
"LZ4 Decompress",
"LZ4 Compress"
"LZ4 Compress",
"LZNT1 Decompress"
]
},
{
@ -368,6 +374,7 @@
"SHA2",
"SHA3",
"SM3",
"MurmurHash3",
"Keccak",
"Shake",
"RIPEMD",

44
src/core/lib/Base92.mjs Normal file
View File

@ -0,0 +1,44 @@
/**
* Base92 resources.
*
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* Base92 alphabet char
*
* @param {number} val
* @returns {number}
*/
export function base92Chr(val) {
if (val < 0 || val >= 91) {
throw new OperationError("Invalid value");
}
if (val === 0)
return "!".charCodeAt(0);
else if (val <= 61)
return "#".charCodeAt(0) + val - 1;
else
return "a".charCodeAt(0) + val - 62;
}
/**
* Base92 alphabet ord
*
* @param {string} val
* @returns {number}
*/
export function base92Ord(val) {
if (val === "!")
return 0;
else if ("#" <= val && val <= "_")
return val.charCodeAt(0) - "#".charCodeAt(0) + 1;
else if ("a" <= val && val <= "}")
return val.charCodeAt(0) - "a".charCodeAt(0) + 62;
throw new OperationError(`${val} is not a base92 character`);
}

88
src/core/lib/LZNT1.mjs Normal file
View File

@ -0,0 +1,88 @@
/**
*
* LZNT1 Decompress.
*
* @author 0xThiebaut [thiebaut.dev]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*
* https://github.com/Velocidex/go-ntfs/blob/master/parser%2Flznt1.go
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
const COMPRESSED_MASK = 1 << 15,
SIZE_MASK = (1 << 12) - 1;
/**
* @param {number} offset
* @returns {number}
*/
function getDisplacement(offset) {
let result = 0;
while (offset >= 0x10) {
offset >>= 1;
result += 1;
}
return result;
}
/**
* @param {byteArray} compressed
* @returns {byteArray}
*/
export function decompress(compressed) {
const decompressed = Array();
let coffset = 0;
while (coffset + 2 <= compressed.length) {
const doffset = decompressed.length;
const blockHeader = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
coffset += 2;
const size = blockHeader & SIZE_MASK;
const blockEnd = coffset + size + 1;
if (size === 0) {
break;
} else if (compressed.length < coffset + size) {
throw new OperationError("Malformed LZNT1 stream: Block too small! Has the stream been truncated?");
}
if ((blockHeader & COMPRESSED_MASK) !== 0) {
while (coffset < blockEnd) {
let header = compressed[coffset++];
for (let i = 0; i < 8 && coffset < blockEnd; i++) {
if ((header & 1) === 0) {
decompressed.push(compressed[coffset++]);
} else {
const pointer = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), "little");
coffset += 2;
const displacement = getDisplacement(decompressed.length - doffset - 1);
const symbolOffset = (pointer >> (12 - displacement)) + 1;
const symbolLength = (pointer & (0xFFF >> displacement)) + 2;
const shiftOffset = decompressed.length - symbolOffset;
for (let shiftDelta = 0; shiftDelta < symbolLength + 1; shiftDelta++) {
const shift = shiftOffset + shiftDelta;
if (shift < 0 || decompressed.length <= shift) {
throw new OperationError("Malformed LZNT1 stream: Invalid shift!");
}
decompressed.push(decompressed[shift]);
}
}
header >>= 1;
}
}
} else {
decompressed.push(...compressed.slice(coffset, coffset + size + 1));
coffset += size + 1;
}
}
return decompressed;
}

View File

@ -21,7 +21,7 @@ class CTPH extends Operation {
this.name = "CTPH";
this.module = "Crypto";
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
this.infoURL = "https://forensics.wiki/context_triggered_piecewise_hashing/";
this.inputType = "string";
this.outputType = "string";
this.args = [];

View File

@ -24,7 +24,7 @@ class CompareCTPHHashes extends Operation {
this.name = "Compare CTPH hashes";
this.module = "Crypto";
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
this.infoURL = "https://forensics.wiki/context_triggered_piecewise_hashing/";
this.inputType = "string";
this.outputType = "Number";
this.args = [

View File

@ -24,7 +24,7 @@ class CompareSSDEEPHashes extends Operation {
this.name = "Compare SSDEEP hashes";
this.module = "Crypto";
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
this.infoURL = "https://forensics.wiki/ssdeep/";
this.inputType = "string";
this.outputType = "Number";
this.args = [

View File

@ -39,7 +39,7 @@ class ExtractFiles extends Operation {
${supportedExts.join("</li><li>")}
</li>
</ul>Minimum File Size can be used to prune small false positives.`;
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
this.infoURL = "https://forensics.wiki/file_carving";
this.inputType = "ArrayBuffer";
this.outputType = "List<File>";
this.presentType = "html";

View File

@ -66,7 +66,7 @@ class ExtractIPAddresses extends Operation {
run(input, args) {
const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args,
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})";
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})(([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}";
let ips = "";
if (includeIpv4 && includeIpv6) {

View File

@ -0,0 +1,93 @@
/**
* @author sw5678
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs";
/**
* Unique operation
*/
class FileTree extends Operation {
/**
* Unique constructor
*/
constructor() {
super();
this.name = "File Tree";
this.module = "Default";
this.description = "Creates file tree from list of file paths (similar to the tree command in Linux)";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "File Path Delimiter",
type: "binaryString",
value: "/"
},
{
name: "Delimiter",
type: "option",
value: INPUT_DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
// Set up arrow and pipe for nice output display
const ARROW = "|---";
const PIPE = "| ";
// Get args from input
const fileDelim = args[0];
const entryDelim = Utils.charRep(args[1]);
// Store path to print
const completedList = [];
const printList = [];
// Loop through all entries
const filePaths = input.split(entryDelim).unique().sort();
for (let i = 0; i < filePaths.length; i++) {
// Split by file delimiter
let path = filePaths[i].split(fileDelim);
if (path[0] === "") {
path = path.slice(1, path.length);
}
for (let j = 0; j < path.length; j++) {
let printLine;
let key;
if (j === 0) {
printLine = path[j];
key = path[j];
} else {
printLine = PIPE.repeat(j-1) + ARROW + path[j];
key = path.slice(0, j+1).join("/");
}
// Check to see we have already added that path
if (!completedList.includes(key)) {
completedList.push(key);
printList.push(printLine);
}
}
}
return printList.join("\n");
}
}
export default FileTree;

View File

@ -0,0 +1,55 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import { base92Ord } from "../lib/Base92.mjs";
import Operation from "../Operation.mjs";
/**
* From Base92 operation
*/
class FromBase92 extends Operation {
/**
* FromBase92 constructor
*/
constructor() {
super();
this.name = "From Base92";
this.module = "Default";
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const res = [];
let bitString = "";
for (let i = 0; i < input.length; i += 2) {
if (i + 1 !== input.length) {
const x = base92Ord(input[i]) * 91 + base92Ord(input[i + 1]);
bitString += x.toString(2).padStart(13, "0");
} else {
const x = base92Ord(input[i]);
bitString += x.toString(2).padStart(6, "0");
}
while (bitString.length >= 8) {
res.push(parseInt(bitString.slice(0, 8), 2));
bitString = bitString.slice(8);
}
}
return res;
}
}
export default FromBase92;

View File

@ -0,0 +1,41 @@
/**
* @author 0xThiebaut [thiebaut.dev]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {decompress} from "../lib/LZNT1.mjs";
/**
* LZNT1 Decompress operation
*/
class LZNT1Decompress extends Operation {
/**
* LZNT1 Decompress constructor
*/
constructor() {
super();
this.name = "LZNT1 Decompress";
this.module = "Compression";
this.description = "Decompresses data using the LZNT1 algorithm.<br><br>Similar to the Windows API <code>RtlDecompressBuffer</code>.";
this.infoURL = "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/5655f4a3-6ba4-489b-959f-e1f407c52f15";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
return decompress(input);
}
}
export default LZNT1Decompress;

View File

@ -0,0 +1,143 @@
/**
* Based on murmurhash-js (https://github.com/garycourt/murmurhash-js)
* @author Gary Court
* @license MIT
*
* @author AliceGrey [alice@grey.systems]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* MurmurHash3 operation
*/
class MurmurHash3 extends Operation {
/**
* MurmurHash3 constructor
*/
constructor() {
super();
this.name = "MurmurHash3";
this.module = "Default";
this.description = "Generates a MurmurHash v3 for a string input and an optional seed input";
this.infoURL = "https://wikipedia.org/wiki/MurmurHash";
this.inputType = "string";
this.outputType = "number";
this.args = [
{
name: "Seed",
type: "number",
value: 0
},
{
name: "Convert to Signed",
type: "boolean",
value: false
}
];
}
/**
* Calculates the MurmurHash3 hash of the input.
* Based on Gary Court's JS MurmurHash implementation
* @see http://github.com/garycourt/murmurhash-js
* @author AliceGrey [alice@grey.systems]
* @param {string} input ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
mmh3(input, seed) {
let h1b;
let k1;
const remainder = input.length & 3; // input.length % 4
const bytes = input.length - remainder;
let h1 = seed;
const c1 = 0xcc9e2d51;
const c2 = 0x1b873593;
let i = 0;
while (i < bytes) {
k1 =
((input.charCodeAt(i) & 0xff)) |
((input.charCodeAt(++i) & 0xff) << 8) |
((input.charCodeAt(++i) & 0xff) << 16) |
((input.charCodeAt(++i) & 0xff) << 24);
++i;
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
}
k1 = 0;
if (remainder === 3) {
k1 ^= (input.charCodeAt(i + 2) & 0xff) << 16;
}
if (remainder === 3 || remainder === 2) {
k1 ^= (input.charCodeAt(i + 1) & 0xff) << 8;
}
if (remainder === 3 || remainder === 2 || remainder === 1) {
k1 ^= (input.charCodeAt(i) & 0xff);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
}
h1 ^= input.length;
h1 ^= h1 >>> 16;
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
}
/**
* Converts an unsigned 32-bit integer to a signed 32-bit integer
* @author AliceGrey [alice@grey.systems]
* @param {value} 32-bit unsigned integer
* @return {number} 32-bit signed integer
*/
unsignedToSigned(value) {
if (value & 0x80000000) {
return -0x100000000 + value;
} else {
return value;
}
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
if (args && args.length >= 1) {
const seed = args[0];
const hash = this.mmh3(input, seed);
if (args.length > 1 && args[1]) {
return this.unsignedToSigned(hash);
}
return hash;
}
return this.mmh3(input);
}
}
export default MurmurHash3;

View File

@ -20,7 +20,7 @@ class ParseASN1HexString extends Operation {
this.name = "Parse ASN.1 hex string";
this.module = "PublicKey";
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data and presents the resulting tree.";
this.description = "Abstract Syntax Notation One (ASN.1) is a standard and notation that describes rules and structures for representing, encoding, transmitting, and decoding data in telecommunications and computer networking.<br><br>This operation parses arbitrary ASN.1 data (encoded as an hex string: use the 'To Hex' operation if necessary) and presents the resulting tree.";
this.infoURL = "https://wikipedia.org/wiki/Abstract_Syntax_Notation_One";
this.inputType = "string";
this.outputType = "string";

View File

@ -83,6 +83,10 @@ class RegularExpression extends Operation {
name: "Strings",
value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
},
{
name: "UUID (any version)",
value: "[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}"
},
],
"target": 1
},

View File

@ -0,0 +1,60 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import rison from "rison";
/**
* Rison Decode operation
*/
class RisonDecode extends Operation {
/**
* RisonDecode constructor
*/
constructor() {
super();
this.name = "Rison Decode";
this.module = "Default";
this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
this.infoURL = "https://github.com/Nanonid/rison";
this.inputType = "string";
this.outputType = "Object";
this.args = [
{
name: "Decode Option",
type: "editableOption",
value: [
{ name: "Decode", value: "Decode", },
{ name: "Decode Object", value: "Decode Object", },
{ name: "Decode Array", value: "Decode Array", },
]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Object}
*/
run(input, args) {
const [decodeOption] = args;
switch (decodeOption) {
case "Decode":
return rison.decode(input);
case "Decode Object":
return rison.decode_object(input);
case "Decode Array":
return rison.decode_array(input);
}
throw new OperationError("Invalid Decode option");
}
}
export default RisonDecode;

View File

@ -0,0 +1,63 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import rison from "rison";
/**
* Rison Encode operation
*/
class RisonEncode extends Operation {
/**
* RisonEncode constructor
*/
constructor() {
super();
this.name = "Rison Encode";
this.module = "Default";
this.description = "Rison, a data serialization format optimized for compactness in URIs. Rison is a slight variation of JSON that looks vastly superior after URI encoding. Rison still expresses exactly the same set of data structures as JSON, so data can be translated back and forth without loss or guesswork.";
this.infoURL = "https://github.com/Nanonid/rison";
this.inputType = "Object";
this.outputType = "string";
this.args = [
{
name: "Encode Option",
type: "editableOption",
value: [
{ name: "Encode", value: "Encode", },
{ name: "Encode Object", value: "Encode Object", },
{ name: "Encode Array", value: "Encode Array", },
{ name: "Encode URI", value: "Encode URI", }
]
},
];
}
/**
* @param {Object} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [encodeOption] = args;
switch (encodeOption) {
case "Encode":
return rison.encode(input);
case "Encode Object":
return rison.encode_object(input);
case "Encode Array":
return rison.encode_array(input);
case "Encode URI":
return rison.encode_uri(input);
}
throw new OperationError("Invalid encode option");
}
}
export default RisonEncode;

View File

@ -21,7 +21,7 @@ class SSDEEP extends Operation {
this.name = "SSDEEP";
this.module = "Crypto";
this.description = "SSDEEP is a program for computing context triggered piecewise hashes (CTPH). Also called fuzzy hashes, CTPH can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>SSDEEP hashes are now widely used for simple identification purposes (e.g. the 'Basic Properties' section in VirusTotal). Although 'better' fuzzy hashes are available, SSDEEP is still one of the primary choices because of its speed and being a de facto standard.<br><br>This operation is fundamentally the same as the CTPH operation, however their outputs differ in format.";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
this.infoURL = "https://forensics.wiki/ssdeep";
this.inputType = "string";
this.outputType = "string";
this.args = [];

View File

@ -0,0 +1,67 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import { base92Chr } from "../lib/Base92.mjs";
import Operation from "../Operation.mjs";
/**
* To Base92 operation
*/
class ToBase92 extends Operation {
/**
* ToBase92 constructor
*/
constructor() {
super();
this.name = "To Base92";
this.module = "Default";
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const res = [];
let bitString = "";
while (input.length > 0) {
while (bitString.length < 13 && input.length > 0) {
bitString += input[0].charCodeAt(0).toString(2).padStart(8, "0");
input = input.slice(1);
}
if (bitString.length < 13)
break;
const i = parseInt(bitString.slice(0, 13), 2);
res.push(base92Chr(Math.floor(i / 91)));
res.push(base92Chr(i % 91));
bitString = bitString.slice(13);
}
if (bitString.length > 0) {
if (bitString.length < 7) {
bitString = bitString.padEnd(6, "0");
res.push(base92Chr(parseInt(bitString, 2)));
} else {
bitString = bitString.padEnd(13, "0");
const i = parseInt(bitString.slice(0, 13), 2);
res.push(base92Chr(Math.floor(i / 91)));
res.push(base92Chr(i % 91));
}
}
return res;
}
}
export default ToBase92;

View File

@ -157,9 +157,9 @@ function titleFromWikiLink(urlStr) {
pageTitle = "";
switch (urlObj.host) {
case "forensicswiki.xyz":
case "forensics.wiki":
wikiName = "Forensics Wiki";
pageTitle = urlObj.query.substr(6).replace(/_/g, " "); // Chop off 'title='
pageTitle = Utils.toTitleCase(urlObj.path.replace(/\//g, "").replace(/_/g, " "));
break;
case "wikipedia.org":
wikiName = "Wikipedia";

View File

@ -62,7 +62,8 @@
"Training branch predictor...",
"Timing cache hits...",
"Speculatively executing recipes...",
"Adding LLM hallucinations..."
"Adding LLM hallucinations...",
"Decompressing malware..."
];
// Shuffle array using Durstenfeld algorithm

View File

@ -275,7 +275,6 @@ class StatusBarPanel {
bakingTime.textContent = this.timing.duration(this.tabNumGetter());
const info = this.timing.printStages(this.tabNumGetter()).replace(/\n/g, "<br>");
bakingTimeInfo.setAttribute("title", info);
bakingTimeInfo.setAttribute("data-original-title", info);
} else {
bakingTimeInfo.style.display = "none";

View File

@ -635,6 +635,10 @@ WWFkYSBZYWRh\r
assert.strictEqual(chef.keccak("Flea Market").toString(), "c2a06880b19e453ee5440e8bd4c2024bedc15a6630096aa3f609acfd2b8f15f27cd293e1cc73933e81432269129ce954a6138889ce87831179d55dcff1cc7587");
}),
it("LZNT1 Decompress", () => {
assert.strictEqual(chef.LZNT1Decompress("\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot").toString(), "compressedtestdatacompressedalot");
}),
it("MD6", () => {
assert.strictEqual(chef.MD6("Head Over Heels", {key: "arty"}).toString(), "d8f7fe4931fbaa37316f76283d5f615f50ddd54afdc794b61da522556aee99ad");
}),

View File

@ -25,6 +25,7 @@ import "./tests/Base58.mjs";
import "./tests/Base64.mjs";
import "./tests/Base62.mjs";
import "./tests/Base85.mjs";
import "./tests/Base92.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/ByteRepr.mjs";
import "./tests/CartesianProduct.mjs";
@ -62,9 +63,11 @@ import "./tests/JSONtoCSV.mjs";
import "./tests/JWTDecode.mjs";
import "./tests/JWTSign.mjs";
import "./tests/JWTVerify.mjs";
import "./tests/LZNT1Decompress.mjs";
import "./tests/MS.mjs";
import "./tests/Magic.mjs";
import "./tests/MorseCode.mjs";
import "./tests/MurmurHash3.mjs";
import "./tests/NetBIOS.mjs";
import "./tests/NormaliseUnicode.mjs";
import "./tests/OTP.mjs";
@ -113,6 +116,7 @@ import "./tests/Unicode.mjs";
import "./tests/RSA.mjs";
import "./tests/CBOREncode.mjs";
import "./tests/CBORDecode.mjs";
import "./tests/RisonEncodeDecode.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA3SFingerprint.mjs";
import "./tests/HASSH.mjs";

View File

@ -0,0 +1,89 @@
/**
* Base92 tests.
*
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "To Base92: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 1",
input: "AB",
expectedOutput: "8y2",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 2",
input: "Hello!!",
expectedOutput: ";K_$aOTo&",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 3",
input: "base-92",
expectedOutput: "DX2?V<Y(*",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "From Base92: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
{
name: "From Base92: Spec decoding example 1",
input: "G'_DW[B",
expectedOutput: "ietf!",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
{
name: "From Base92: Invalid character",
input: "~",
expectedOutput: "~ is not a base92 character",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
]);

View File

@ -0,0 +1,21 @@
/**
* @author sw5678
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
"name": "Swap Case: basic example",
"input": "/test_dir1/test_file1.txt\n/test_dir1/test_file2.txt\n/test_dir2/test_file1.txt",
"expectedOutput": "test_dir1\n|---test_file1.txt\n|---test_file2.txt\ntest_dir2\n|---test_file1.txt",
"recipeConfig": [
{
"op": "File Tree",
"args": [
],
},
],
}
]);

View File

@ -0,0 +1,22 @@
/**
* LZNT1 Decompress tests.
*
* @author 0xThiebaut [thiebaut.dev]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "LZNT1 Decompress",
input: "\x1a\xb0\x00compress\x00edtestda\x04ta\x07\x88alot",
expectedOutput: "compressedtestdatacompressedalot",
recipeConfig: [
{
op: "LZNT1 Decompress",
args: []
}
],
}
]);

View File

@ -0,0 +1,77 @@
/**
* MurmurHash3 tests
* @author AliceGrey [alice@grey.systems]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "To MurmurHash3: nothing",
input: "",
expectedOutput: "0",
recipeConfig: [
{
op: "MurmurHash3",
args: [0],
},
],
},
{
name: "To MurmurHash3: 1",
input: "1",
expectedOutput: "2484513939",
recipeConfig: [
{
op: "MurmurHash3",
args: [0],
},
],
},
{
name: "To MurmurHash3: Hello World!",
input: "Hello World!",
expectedOutput: "3691591037",
recipeConfig: [
{
op: "MurmurHash3",
args: [0],
},
],
},
{
name: "To MurmurHash3: Hello World! with seed",
input: "Hello World!",
expectedOutput: "1148600031",
recipeConfig: [
{
op: "MurmurHash3",
args: [1337],
},
],
},
{
name: "To MurmurHash3: foo",
input: "foo",
expectedOutput: "4138058784",
recipeConfig: [
{
op: "MurmurHash3",
args: [0],
},
],
},
{
name: "To MurmurHash3: foo signed",
input: "foo",
expectedOutput: "-156908512",
recipeConfig: [
{
op: "MurmurHash3",
args: [0, true],
},
],
}
]);

View File

@ -0,0 +1,66 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Rison Encode: Encoding example 1",
input: JSON.stringify({ any: "json", yes: true }),
expectedOutput: "(any:json,yes:!t)",
recipeConfig: [
{
op: "Rison Encode",
args: ["Encode"]
}
]
},
{
name: "Rison Encode: Encoding example 2",
input: JSON.stringify({ supportsObjects: true, ints: 435 }),
expectedOutput: "ints:435,supportsObjects:!t",
recipeConfig: [
{
op: "Rison Encode",
args: ["Encode Object"]
}
]
},
{
name: "Rison Encode: Encoding example 3",
input: JSON.stringify(["A", "B", { supportsObjects: true }]),
expectedOutput: "A,B,(supportsObjects:!t)",
recipeConfig: [
{
op: "Rison Encode",
args: ["Encode Array"]
}
]
},
{
name: "Rison Encode: Object for an array",
input: JSON.stringify({ supportsObjects: true, ints: 435 }),
expectedOutput: "Rison Encode - rison.encode_array expects an array argument",
expectedError: "Rison Encode - rison.encode_array expects an array argument",
recipeConfig: [
{
op: "Rison Encode",
args: ["Encode Array"]
}
]
},
{
name: "Rison Decode: Decoding example 1",
input: "(any:json,yes:!t)",
expectedOutput: JSON.stringify({ any: "json", yes: true }, null, 4),
recipeConfig: [
{
op: "Rison Decode",
args: ["Decode"]
}
]
}
]);