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

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;