Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"Change IP format",
"Encode text",
"Decode text",
"PowerShell -e Encode/Decode",
"Text Encoding Brute Force",
"Swap endianness",
"To MessagePack",
Expand Down
67 changes: 67 additions & 0 deletions src/core/operations/PowerShellEncodeDecode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @author neoreo
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import cptable from "codepage";
import {toBase64, fromBase64} from "../lib/Base64.mjs";

// PowerShell -EncodedCommand uses UTF-16LE (code page 1200)
const UTF_16LE = 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 <code>-EncodedCommand</code> (<code>-e</code>) payload in a single operation.",
"<br><br>",
"PowerShell's <code>-EncodedCommand</code> 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.",
"<br><br>",
"<b>Encode:</b> <code>whoami</code> becomes <code>dwBoAG8AYQBtAGkA</code>, runnable as <code>powershell -e dwBoAG8AYQBtAGkA</code>.",
"<br>",
"<b>Decode:</b> <code>dwBoAG8AYQBtAGkA</code> becomes <code>whoami</code>."
].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(UTF_16LE, input);
return toBase64(new Uint8Array(encoded).buffer);
}
const bytes = fromBase64(input, "A-Za-z0-9+/=", "byteArray");
return cptable.utils.decode(UTF_16LE, new Uint8Array(bytes));
}

}

export default PowerShellEncodeDecode;
2 changes: 1 addition & 1 deletion tests/node/tests/nodeApi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ TestRegister.addApiTests([

it("chef.help: returns multiple results", () => {
const result = chef.help("base 64");
assert.strictEqual(result.length, 14);
assert.strictEqual(result.length, 15);
}),

it("chef.help: looks in description for matches too", () => {
Expand Down
86 changes: 86 additions & 0 deletions tests/operations/tests/PowerShellEncodeDecode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* 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"],
},
],
},
{
name: "PowerShell -e Encode/Decode: round trip with non-ASCII characters",
input: "Write-Host \"café ☕\"",
expectedOutput: "Write-Host \"café ☕\"",
recipeConfig: [
{
op: "PowerShell -e Encode/Decode",
args: ["Encode"],
},
{
op: "PowerShell -e Encode/Decode",
args: ["Decode"],
},
],
},
{
name: "PowerShell -e Encode/Decode: decode ignores characters outside the Base64 alphabet",
input: "!!!@@@###",
expectedOutput: "",
recipeConfig: [
{
op: "PowerShell -e Encode/Decode",
args: ["Decode"],
},
],
},
]);