Give Me Code Exec
In this section, we are going to continue our reverse engineering
session to discover how we can obtain code execution
under specific circumstances.
During our analysis in the previous section, we followed
the code path starting from the Compile method of the
WorkflowCompilerInternal namespace.
The code trace lead us to GenerateLocalAssembly
which in theory should allow us to compile
and subsequently load an arbitrary assembly.
Now we’ll analyze InternalCompileFromDomBatch
which follows after the GenerateLocalAssembly call.
Before following the call into InternalCompileFromDomBatch,
we notice that GenerateLocalAssembly returns
the newly compiled and loaded assembly inside the
LocalAssembly property of the parameters variable.
A reference to the assembly is subsequently stored in the
typeProvider variable.
When we follow the call into InternalCompileFromDomBatch
we are lead into the XomlCompilerHelper namespace.
After some argument validation and variable initialization,
we find the following foreach loop .
The loop iterates over all classes in the previously
compiled file as given by the reference stored in the
typeProvider variable.
For each iteration it checks for classes which inherit from
System.Workflow.ComponentModel. Activity and are not abstract.
Assuming at least one such class exists,
we go into the loop where the CreateInstance method is invoked
which instantiates an object of the given type:
When an object is instantiated from a class,
the defined constructor is executed.
Our hypothesis is that the Compile method of the
Compiler.WorkflowCompiler namespace is called with the
path of a file containing C# source code given as an argument
After several iterations of validation and parsing,
the provided .NET code is compiled into an assembly,
loaded into memory, and if it contains a non-abstract class
which inherits from the Activity type, an object is instantiated.
If we are able to provide our desired code as part of the
constructor for that class, we can obtain arbitrary
code execution and bypass AppLocker.
This concludes the second stage of reverse engineering.
We have located a theoretical path
that will lead to code execution.
Now we must discover
how to provide proper input to the constructor.