diff --git a/package-lock.json b/package-lock.json
index b374df4bc9..70919d14c3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -70,6 +70,7 @@
"lz-string": "^1.5.0",
"lz4js": "^0.2.0",
"markdown-it": "^14.1.0",
+ "minusonejs": "^0.4.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"ngeohash": "^0.6.3",
@@ -1842,6 +1843,11 @@
"lzma.js": "bin/lzma.js"
}
},
+ "node_modules/@bytecodealliance/preview2-shim": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.4.tgz",
+ "integrity": "sha512-vRfxQ6ob5wCgXlpVNoeIUHrqJ2+JTcnQASNMpoi3OVPRYA2oUfhMJdWFP0u5JVL9FlC4YKe+QKqerr345B3T4A=="
+ },
"node_modules/@codemirror/commands": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.0.tgz",
@@ -13384,6 +13390,14 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/minusonejs": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/minusonejs/-/minusonejs-0.4.2.tgz",
+ "integrity": "sha512-Uzk5xynwdLwL7c2l1/gooiDR94yI6IlIx2T91lwON8AUsEj3hjvRk/jaQWy71Fychde9bDYN+0buaqvgYfBTtw==",
+ "dependencies": {
+ "@bytecodealliance/preview2-shim": "^0.17.4"
+ }
+ },
"node_modules/mocha": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz",
diff --git a/package.json b/package.json
index 9191ab6f03..133c2f0e1e 100644
--- a/package.json
+++ b/package.json
@@ -156,6 +156,7 @@
"lz-string": "^1.5.0",
"lz4js": "^0.2.0",
"markdown-it": "^14.1.0",
+ "minusonejs": "^0.4.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",
"ngeohash": "^0.6.3",
diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 434c8bb619..48a989d4eb 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -493,6 +493,7 @@
{
"name": "Forensics",
"ops": [
+ "Deobfuscate",
"Detect File Type",
"Scan for Embedded Files",
"Extract Files",
diff --git a/src/core/operations/Deobfuscate.mjs b/src/core/operations/Deobfuscate.mjs
new file mode 100644
index 0000000000..4520e693bc
--- /dev/null
+++ b/src/core/operations/Deobfuscate.mjs
@@ -0,0 +1,54 @@
+/**
+ * @author dolphinau [me@dawl.fr]
+ * @copyright Crown Copyright 2025
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+
+import { deobfuscate, getLanguages } from "minusonejs";
+
+/**
+ * Deobfuscate operation
+ */
+class Deobfuscate extends Operation {
+ /**
+ * Deobfuscate constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Deobfuscate";
+ this.module = "Obfuscation";
+ this.description =
+ "Deobfuscate input with the minusone engine.
Supported languages:
- Powershell";
+ this.infoURL = "https://github.com/airbus-cert/minusone"; // Usually a Wikipedia link. Remember to remove localisation (i.e. https://wikipedia.org/etc rather than https://en.wikipedia.org/etc)
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Language",
+ type: "option",
+ value: getLanguages(),
+ },
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ if (!input) return "";
+
+ const [Language] = args;
+ const [res, err] = deobfuscate(input, Language);
+
+ if (err) throw new OperationError(`Deobfuscate error: ${err}`);
+ return res;
+ }
+}
+
+export default Deobfuscate;
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index f147e9e7c7..d2afa82e7d 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -60,6 +60,7 @@ import "./tests/Crypt.mjs";
import "./tests/CSV.mjs";
import "./tests/DateTime.mjs";
import "./tests/DefangIP.mjs";
+import "./tests/Deobfuscate.mjs";
import "./tests/DropNthBytes.mjs";
import "./tests/ECDSA.mjs";
import "./tests/ELFInfo.mjs";
diff --git a/tests/operations/tests/Deobfuscate.mjs b/tests/operations/tests/Deobfuscate.mjs
new file mode 100644
index 0000000000..91cc513882
--- /dev/null
+++ b/tests/operations/tests/Deobfuscate.mjs
@@ -0,0 +1,33 @@
+/**
+ * Deobfuscate tests
+ *
+ * @author dolphinau [me@dawl.fr]
+ * @copyright Crown Copyright 2019
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Deobfuscate: Powershell empty deobfuscation",
+ input: "",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "Deobfuscate",
+ args: ["Powershell"],
+ },
+ ],
+ },
+ {
+ name: "Deobfuscate: Powershell deobfuscation",
+ input: "${Pop-pKkAp}=1;${Clear-OK3Emf}=4;${Push-Jh8ps}=9;${Format-qqM9C}=16;${Redo-kSQuo}=86;${Format-LyC}=51;${Pop-ASPJ}=74;${Join-pIuV}=112;${Hide-Rhpet}=100;${Copy-TWaj}=71;${Set-yYE}=85;${Exit-shq}=116;${Skip-5qa}=83;${Push-bAik}=57;${Split-f7hDr6}=122;${Open-YGi}=65;${Open-LPQk}=61;${Select-YUyq}=84;${Move-sS6mJ}=87;${Search-wa0}=108;${Join-YJq}=117;${Hide-iQ5}=88;${Select-iV0F7}=78;${Select-cI9j}=80;${Open-Hec}=98;${Reset-4QePz}=109;${Format-4e7UHy}=103;${Lock-UyaF}=97;${Select-ZGdxB}=77;${Move-FtkTLt}=104;${Push-VUUQsE}=73;${Add-LHgggw}=99;${Reset-sc3}=81;${Format-AlmdYS}=50;${Resize-mYqZ}=121;${Reset-hp9}=66;${Reset-qC3Yd}=48;${Find-6QywvV}=120;${Select-v7sja}=110;${Step-7WvUL}=82;$DJ2=[System.Text.Encoding];$1Ro=[System.Convert];${Step-xE2}=-join'8FTU'[-${Pop-pKkAp}..-${Clear-OK3Emf}];${Unlock-Zdbkvh}=-join'gnirtSteG'[-${Pop-pKkAp}..-${Push-Jh8ps}];${Close-yjy}=-join'gnirtS46esaBmorF'[-${Pop-pKkAp}..-${Format-qqM9C}];. ($DJ2::${Step-xE2}.${Unlock-Zdbkvh}($1Ro::${Close-yjy}(([char]${Redo-kSQuo}+[char]${Format-LyC}+[char]${Pop-ASPJ}+[char]${Join-pIuV}+[char]${Hide-Rhpet}+[char]${Copy-TWaj}+[char]${Set-yYE}+[char]${Exit-shq}+[char]${Skip-5qa}+[char]${Copy-TWaj}+[char]${Push-bAik}+[char]${Split-f7hDr6}+[char]${Hide-Rhpet}+[char]${Open-YGi}+[char]${Open-LPQk}+[char]${Open-LPQk})))) ($DJ2::${Step-xE2}.${Unlock-Zdbkvh}($1Ro::${Close-yjy}(([char]${Select-YUyq}+[char]${Move-sS6mJ}+[char]${Search-wa0}+[char]${Join-YJq}+[char]${Hide-Rhpet}+[char]${Hide-iQ5}+[char]${Select-iV0F7}+[char]${Select-cI9j}+[char]${Open-Hec}+[char]${Reset-4QePz}+[char]${Set-yYE}+[char]${Format-4e7UHy}+[char]${Lock-UyaF}+[char]${Hide-iQ5}+[char]${Select-ZGdxB}+[char]${Format-4e7UHy}+[char]${Hide-Rhpet}+[char]${Copy-TWaj}+[char]${Move-FtkTLt}+[char]${Search-wa0}+[char]${Push-VUUQsE}+[char]${Copy-TWaj}+[char]${Pop-ASPJ}+[char]${Search-wa0}+[char]${Add-LHgggw}+[char]${Format-LyC}+[char]${Reset-sc3}+[char]${Format-4e7UHy}+[char]${Add-LHgggw}+[char]${Format-AlmdYS}+[char]${Select-iV0F7}+[char]${Resize-mYqZ}+[char]${Lock-UyaF}+[char]${Hide-iQ5}+[char]${Reset-hp9}+[char]${Reset-qC3Yd}+[char]${Push-VUUQsE}+[char]${Copy-TWaj}+[char]${Find-6QywvV}+[char]${Join-pIuV}+[char]${Open-Hec}+[char]${Select-v7sja}+[char]${Step-7WvUL}+[char]${Search-wa0}+[char]${Add-LHgggw}+[char]${Format-4e7UHy}+[char]${Open-LPQk}+[char]${Open-LPQk}))))",
+ expectedOutput: '\n& "write-host" "MinusOne is the best script linter"',
+ recipeConfig: [
+ {
+ op: "Deobfuscate",
+ args: ["Powershell"],
+ },
+ ],
+ },
+]);