
Bug in the CLR: how to drag an object into the sandbox without marshalling and call Callback
Good afternoon! I hope that I already won the achievement “recognized the author by title” on Habr’s;) Today, however, we will talk about a fresh, not yet closed vulnerability in .Net, which one person brought to my mind (who will throw him an invite?), Who wrote me by mail:
At first I did not understand it, but then a code example was born that throws any object of the type located in the SharedDomain into the sandbox and allows you to use its methods WITHOUT marshalling.
On the one hand, it’s rather difficult to call a hole, because the host should prepare the ground for this. And not in the most ordinary way, it should be noted. But on the other hand ... Yes, this is a
bug .
The first thing we need is the routine EntityPtr.ToPointer () and * .ToInstance ()
methods from DotNetEx . Their combination makes the object cast to an incompatible type. Those. to the type that it is not:
Naturally, if you call any method on such a "string", nothing will happen: an Exception will be thrown (except for virtual methods from object - their positions in the table of virtual methods will coincide with those overridden in our type and the call will happen correctly)
However, since the string is already a serialized object , then when marshalling it is transmitted by reference, without copying. This means that we can throw it into a method whose code is executed in the sandbox and cast it back inside the type there.
I'll start right away with the delicious:
It displays a welcome screen.
So that will work:
What is impossible:
What affects:

Have you tried using IL code to cast objects to string types and pass them to other domains?
At first I did not understand it, but then a code example was born that throws any object of the type located in the SharedDomain into the sandbox and allows you to use its methods WITHOUT marshalling.
On the one hand, it’s rather difficult to call a hole, because the host should prepare the ground for this. And not in the most ordinary way, it should be noted. But on the other hand ... Yes, this is a

The first thing we need is the routine EntityPtr.ToPointer () and * .ToInstance ()

string str = EntityPtr.ToInstance(EntityPtr.ToPointer(new List()));
Naturally, if you call any method on such a "string", nothing will happen: an Exception will be thrown (except for virtual methods from object - their positions in the table of virtual methods will coincide with those overridden in our type and the call will happen correctly)
However, since the string is already a serialized object , then when marshalling it is transmitted by reference, without copying. This means that we can throw it into a method whose code is executed in the sandbox and cast it back inside the type there.
I'll start right away with the delicious:
private void methodInsideAppDomain(string str)
{
object tmp = str;
var act = (Action)tmp;
act();
}
public static void Go(string startingIntPtr)
{
// make appdomain
var permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var dom = AppDomain.CreateDomain("PseudoIsolated", null, new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
}, permissions);
// create object instance
var asmname = typeof (AppDomainRunner).Assembly.FullName;
var typename = typeof (AppDomainRunner).FullName;
var instance = (AppDomainRunner)dom.CreateInstanceAndUnwrap(asmname, typename);
// enumerate objects from outside area to our appdomain area
instance.methodInsideAppDomain(startingIntPtr);
}
}
class Program
{
static void Main(string[] args)
{
Expression expression = () => Console.WriteLine("Surprise, motherf*ckers!");
AppDomainRunner.Go(EntityPtr.ToInstance(EntityPtr.ToPointer(expression.Compile())));
Console.ReadKey();
}
}
It displays a welcome screen.
So that will work:
- Pulling objects of any type with casting inside the sandbox - into a type from the Shared Domain.
- If you call the virtual methods of the base class, which is located in the Shared Domain and which you redefined on your own, your
- If you pass as an Action - a compiled Expression - will also be called. But at the same time, it will also be called in the sandbox, since the domain that goes with marshalling will not be switched.
What is impossible:
- Cast to a sandbox type, since the type and everything connected with it are loaded a second time in the sandbox and cannot be converted to the same type: physically, type descriptors in different domains will have different addresses, which means an InvalidCastException.
- To give ordinary delegates to your code as an Action. Delegates either check the domain, or the call within the domain goes near jmp, and the domains have different code selectors ... In general, the call drops. Need to do Expression.
- Even if you don’t need to cast the type (we give List <CustomType>, we get list.First (). DoSomething ()), still nothing works. It is necessary to work through the basic type
What affects:
- As a matter of fact, since this should fine-tune the host, and not do it in the sandbox, the value of the vulnerability drops
- Do not call external code to switch to the hosting AppDomain. A delegate who is thrown in will be called under the same rights. Although it is possible to make the RunAsHost (() => ...) method through a chain, but more on that later =)
- You can organize a quick call to host methods from the sandbox quite safely, without marshalling. Sometimes this is critical. For example, to forward quotes
