memshell-party-cli 0.4.0 → 0.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memshell-party-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Command-line client and MCP server for MemShellParty (party.mem.mk) — generate Java memory shells and probes from the terminal.",
5
5
  "keywords": [
6
6
  "memshell",
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
+ "resources",
20
21
  "skills",
21
22
  "README.md",
22
23
  "README.en.md",
@@ -224,9 +224,11 @@ paths are on the machine running the MCP server.
224
224
 
225
225
  `mimic` is a demo protocol that shapes shell traffic after the target site's own pages:
226
226
  requests are browser-style form POSTs; responses are real site pages with the ciphertext
227
- hidden in a per-shell-unique JS variable (`var Re<md5(pass+key)[0:5]>_config="..."`).
228
- **You write the site profile — the CLI does not crawl.** That is the point: you can judge
229
- which page makes believable cover; a regex crawler cannot.
227
+ hidden in a per-shell-unique marker (a JS variable or an HTML comment). The wire codec is
228
+ selectable per profile (`cipher` section: AES/ECB, AES/CBC with random IV, or XOR; base64 /
229
+ base64url / hex; optional key-derived junk tail). **You write the site profile — the CLI
230
+ does not crawl.** That is the point: you can judge which page makes believable cover; a
231
+ regex crawler cannot.
230
232
 
231
233
  ### Workflow
232
234
 
@@ -268,6 +270,20 @@ which page makes believable cover; a regex crawler cannot.
268
270
  `{{hex:N}}` (N hex), `{{b64:N}}` (N base64url), `{{uuid}}`, `{{ts}}` (unix seconds),
269
271
  `{{int:A:B}}`. Never ship the default `pass=<cipher>` single-field body — a lone
270
272
  `pass=` parameter is a textbook webshell signature.
273
+ - `cipher` (optional): the wire codec. Fields (all optional, defaults = the legacy
274
+ format `aes-ecb` + `base64` + `js-var` + no tail):
275
+ - `algorithm`: `"aes-ecb"` (fixed blocks — same plaintext ⇒ same ciphertext),
276
+ `"aes-cbc"` (random IV per message — same command encrypts differently every
277
+ time, the strongest default), `"xor"` (no JCE needed).
278
+ - `encoding`: `"base64"` | `"base64url"` | `"hex"`.
279
+ - `padTail`: `true` appends key-derived-length (0-15) random alnum garbage after
280
+ the encoded ciphertext — Behinder's `aes_with_magic` trick against length
281
+ signatures. Costs nothing, turn it on.
282
+ - `marker`: `"js-var"` (`var Re<md5(pass+key)[0:5]>_config="..."`) or
283
+ `"html-comment"` (`<!--Re..._config:...-->`) — pick whichever looks more at
284
+ home in the cover page.
285
+ ⚠️ The cipher is **baked into the filter at build time**. Editing `cipher` in the
286
+ profile afterwards desynchronizes client and server — rebuild + re-inject.
271
287
  4. **Use it:**
272
288
 
273
289
  ```bash
@@ -337,49 +353,55 @@ documents dominate; no SPA).
337
353
 
338
354
  ### What mimic handles vs. what you decide
339
355
 
340
- - Automatic: AES/ECB (key = md5(key)[0:16]), per-shell derived response delimiters,
341
- form-body encoding, browser-consistent request headers, credential round-trip on `connect`.
356
+ - Automatic: the selected codec (key = md5(key)[0:16] for every algorithm), per-shell
357
+ derived response delimiters, form-body encoding, browser-consistent request headers,
358
+ credential round-trip on `connect`.
342
359
  - Yours: the template page, the path vocabulary, the request pacing. Filter/listener-type
343
360
  memory shells answer on any path, which is what makes `--dynamic-path` legitimate — for a
344
361
  fixed-URL shell just omit the flag.
345
362
  - Limits: exec only (no upload/download); request timing/method mix is fixed, so pace your
346
363
  commands like a patient human.
347
364
 
348
- ### Server side (Java filter memshell)
349
-
350
- The reference server is a servlet Filter, not a JSP — the repo keeps it in
351
- `scratch/mimic-java/`:
352
-
353
- - `mimic/MimicFilter.java` — the protocol server: mapped to `/*` at chain position 0, it
354
- answers any form POST whose carrier (one of the profile's secretFields, baked in by the
355
- generator — form field, URL parameter or header) decrypts, and passes everything else
356
- through. Two rules keep it invisible: **detection uses `getParameter()`/`getHeader()` only
357
- and never reads the body stream** (a second filter shell behind it keeps working), and
358
- **an undecryptable value means chain-through, not a cover page** — so a legit form that
359
- happens to use the same field name (e.g. the console's own login POST with `verCode`) is
360
- never hijacked.
361
- - `mimic/MimicInject.java` — the registration injector (adapted from MemShellParty's
362
- TongWeb injector): its `<clinit>` embeds the filter's class bytes (gzip+base64), defines
363
- them in the webapp classloader, and registers the filter into every running context.
364
- - `../gen-mimic-filter.mjs` — the generator: reads the profile from
365
- `~/.memparty/profiles/`, embeds **all** its templates (the filter picks one at random per
366
- response — that's the rotation), and emits `MimicFilter<V>.class` + `MimicInject<V>.class`
367
- (`javac --release 8`). Swap templates, re-run, re-inject. **Pass a fresh version suffix
368
- on every re-injection** (`node gen-mimic-filter.mjs V3`): a running JVM can't re-define a
369
- class, and the injector skips registration when the filter name already exists.
370
-
371
- Deployment needs any channel that can `defineClass` + instantiate in the target JVM (a
372
- deserialization RCE, an agent loader, another shell that evaluates scripts, …). The filter
373
- is memory-resident and dies on restart — same as any memory shell.
374
-
375
- Two deployment truths learned on a real TongWeb console:
376
-
377
- - **Dynamic path is conditional on the target app's own filters.** The console's
378
- loginfilter answers `/console/*` itself (except `/console/service`), so `--dynamic-path`
379
- fails there even with the filter on `/*` — probe before relying on it, fall back to the
380
- fixed working path.
381
- - The cover page must be one the target actually serves; a beautiful but foreign template
382
- is a fingerprint.
365
+ ### Server side — `memparty custom build`
366
+
367
+ The server half is a plain `javax.servlet.Filter` generated FROM the profile and compiled
368
+ locally — one command does the whole chain:
369
+
370
+ ```bash
371
+ memparty custom build --profile acme --server Tomcat
372
+ ```
373
+
374
+ What happens inside:
375
+
376
+ 1. renders `MimicFilter<rand>.java` from the profile (templates, carrier fields, **the
377
+ `cipher` section is baked in here**) + random `pass`/`key` (or `--pass/--key`);
378
+ 2. compiles it with the local JDK (`javac` on PATH, servlet API jar bundled — no other
379
+ dependency);
380
+ 3. submits the class to the MemShellParty backend as `shellTool=Custom`, which wraps it
381
+ with the injector for `--server` (any server from `memparty config tools` — the filter
382
+ itself has zero middleware specifics) and packs it with `--packer`
383
+ (default `DefaultBase64`);
384
+ 4. writes an output dir (default `<profile>-build/`): the `.java`/`.class`, one
385
+ `payload-*.txt` per pack variant, and **`manifest.json`** — the single source of truth
386
+ for the follow-up (credentials, cipher, class names, ready-made connect command).
387
+
388
+ Then: inject the payload through your foothold (deserialization RCE `defineClass`, an
389
+ existing shell that evaluates scripts, an agent loader…), and connect with the printed
390
+ command — a successful `connect` auto-saves the target, afterwards
391
+ `memparty exec <host>/mimic --cmd "id"` needs no flags.
392
+
393
+ Notes that matter:
394
+
395
+ - **Random class name every build** (`--out` keeps builds apart): a running JVM can't
396
+ re-define a class, so re-injection NEEDS the fresh name — never reuse one.
397
+ - The printed `--pass/--key` are random per build unless you pin them; they're in
398
+ `manifest.json`, not in your shell history if you use `--json`.
399
+ - The filter probes carriers with `getParameter()`/`getHeader()` only (never reads the
400
+ body stream), passes undecryptable values through `chain.doFilter`, and answers errors
401
+ with the plain cover page — it coexists with the site's real forms and other shells.
402
+ - Filter-type deployment truth learned on a real console: **dynamic path is conditional
403
+ on the target app's own filters** — probe a random path first, fall back to the fixed
404
+ working path when the app answers everything itself.
383
405
 
384
406
  ### Reality check before relying on it
385
407