From b590aa6e6932d7e7d4928d125d1577a5b5093ac4 Mon Sep 17 00:00:00 2001 From: Neoreo Date: Thu, 30 Jul 2026 16:14:06 +0200 Subject: [PATCH] Add PowerShell Encode/Decode operation --- src/core/config/Categories.json | 1 + .../operations/PowerShellEncodeDecode.mjs | 71 +++++++++++++++++++ .../tests/PowerShellEncodeDecode.mjs | 60 ++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/core/operations/PowerShellEncodeDecode.mjs create mode 100644 tests/operations/tests/PowerShellEncodeDecode.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 6ad3adb7..87cab479 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -64,6 +64,7 @@ "Change IP format", "Encode text", "Decode text", + "PowerShell -e Encode/Decode", "Text Encoding Brute Force", "Swap endianness", "To MessagePack", diff --git a/src/core/operations/PowerShellEncodeDecode.mjs b/src/core/operations/PowerShellEncodeDecode.mjs new file mode 100644 index 00000000..58e64534 --- /dev/null +++ b/src/core/operations/PowerShellEncodeDecode.mjs @@ -0,0 +1,71 @@ +/** + * @author neoreo + * @copyright Crown Copyright 2026 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import cptable from "codepage"; +import {toBase64, fromBase64} from "../lib/Base64.mjs"; + +// PowerShell -EncodedCommand uses UTF-16LE (code page 1200) +const UTF16LE = 1200; + +/** + * PowerShell -e Encode/Decode operation + */ +class PowerShellEncodeDecode extends Operation { + + /** + * PowerShellEncodeDecode constructor + */ + constructor() { + super(); + + this.name = "PowerShell -e Encode/Decode"; + this.module = "Encodings"; + this.description = [ + "Encodes or decodes a PowerShell -EncodedCommand (-e) payload in a single operation.", + "

", + "PowerShell's -EncodedCommand parameter expects the command encoded as UTF-16LE and then Base64'd. ", + "This operation combines both steps so you don't have to chain 'Encode text' and 'To Base64' (or their decode equivalents) yourself.", + "

", + "Encode: whoami becomes dwBoAG8AYQBtAGkA, runnable as powershell -e dwBoAG8AYQBtAGkA.", + "
", + "Decode: dwBoAG8AYQBtAGkA becomes whoami." + ].join("\n"); + this.infoURL = "https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe#-encodedcommand-base64encodedcommand"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Mode", + type: "option", + value: ["Encode", "Decode"] + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [mode] = args; + + if (mode === "Encode") { + const encoded = cptable.utils.encode(UTF16LE, input); + return toBase64(new Uint8Array(encoded).buffer); + } else if (mode === "Decode") { + const bytes = fromBase64(input, "A-Za-z0-9+/=", "byteArray"); + return cptable.utils.decode(UTF16LE, new Uint8Array(bytes)); + } else { + throw new OperationError("Invalid mode"); + } + } + +} + +export default PowerShellEncodeDecode; diff --git a/tests/operations/tests/PowerShellEncodeDecode.mjs b/tests/operations/tests/PowerShellEncodeDecode.mjs new file mode 100644 index 00000000..de2a05dd --- /dev/null +++ b/tests/operations/tests/PowerShellEncodeDecode.mjs @@ -0,0 +1,60 @@ +/** + * PowerShell -e Encode/Decode tests. + * + * @author neoreo + * + * @copyright Crown Copyright 2026 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "PowerShell -e Encode/Decode: encode nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "PowerShell -e Encode/Decode", + args: ["Encode"], + }, + ], + }, + { + name: "PowerShell -e Encode/Decode: encode whoami", + input: "whoami", + expectedOutput: "dwBoAG8AYQBtAGkA", + recipeConfig: [ + { + op: "PowerShell -e Encode/Decode", + args: ["Encode"], + }, + ], + }, + { + name: "PowerShell -e Encode/Decode: decode whoami", + input: "dwBoAG8AYQBtAGkA", + expectedOutput: "whoami", + recipeConfig: [ + { + op: "PowerShell -e Encode/Decode", + args: ["Decode"], + }, + ], + }, + { + name: "PowerShell -e Encode/Decode: round trip", + input: "Get-Process | Where-Object {$_.CPU -gt 10}", + expectedOutput: "Get-Process | Where-Object {$_.CPU -gt 10}", + recipeConfig: [ + { + op: "PowerShell -e Encode/Decode", + args: ["Encode"], + }, + { + op: "PowerShell -e Encode/Decode", + args: ["Decode"], + }, + ], + }, +]);