Search code examples
c#sandboxappdomain

Loading Assembly in sandbox Appdomain - SecurityException


I would like to invoke method from runtime created assembly. It is partially trusted code, so I want to create a sandbox application domain for it.

I create the Assembly with Roslyn, the result is a byte[]. I can load it and invoke from it in the default Appdomain, it works fine. The problem is (I guess) with the sandbox.

I used this tutorial.

Creating the sandbox Appdomain:

private AppDomain createAppdomain(string location)
{
     AppDomain currentAppdomain = AppDomain.CurrentDomain;

     // Create the permission set to be granted to the untrusted application
     Evidence ev = new Evidence();
     ev.AddHostEvidence(new Zone(SecurityZone.Internet));
     PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);
     var platform = Assembly.GetExecutingAssembly();
     internetPS.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(platform.Location)));

     // Sign the assembly that contains the hosting class (named Sandboxer in this example) that calls the untrusted code
     // .NET Framework assemblies such as mscorlib and System.dll do not have to be added to the full-trust list
     // because they are loaded as fully trusted from the global assembly cache.
     StrongName[] fullTrustAssembly = new StrongName[1];
     fullTrustAssembly[0] = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();

     // Initialize the AppDomainSetup parameter of the CreateDomain method
     //  The ApplicationBase property is an important setting,
     // and should be different from the ApplicationBase property for the AppDomain of the hosting application.
     // If the ApplicationBase settings are the same,
     // the partial-trust application can get the hosting application to load (as fully trusted) an exception it defines, thus exploiting it.
     AppDomainSetup adSetup = new AppDomainSetup();
     adSetup.ApplicationBase = Path.GetFullPath(location);


     // Call the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to create the application domain
     // http://msdn.microsoft.com/en-us/library/ms130766(v=vs.110).aspx
     AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, internetPS, fullTrustAssembly);

     return newDomain;
}

Creating the sandbox (Sandboxer type is MarshalByRefObject):

string physicalPath = HttpContext.Current.Request.PhysicalApplicationPath + @"App_Data\";
 AppDomain Sandbox = createAppdomain(physicalPath);

// http://msdn.microsoft.com/en-us/library/dd413384(v=vs.110).aspx
ObjectHandle handle = Activator.CreateInstanceFrom(
     Sandbox,
     typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
     typeof(Sandboxer).FullName,
     true,
     BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance,
     null,
    // byte[] rawAssembly
     new object[] { rawAssembly },
     null,
     null);
Sandboxer newDomainInstance = (Sandboxer)handle.Unwrap();   

string s = newDomainInstance.callMethod();

Loading the Assembly, invoking the method:

private string callMethod()
{   
     // No Exception thrown yet, but some problems with the Evidence:
     // Evidence    'asm.Evidence' threw an exception of type 'System.Security.SecurityException'   System.Security.Policy.Evidence {System.Security.SecurityException}
     //"Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed."
     Assembly asm = Assembly.Load(rawAssembly);

     Type MyClass = asm.GetType(myClassName);

     // In this line I get the Exception:
     // System.Security.SecurityException
     // "Request failed."
     object obj = Activator.CreateInstance(MyClass);

     MethodInfo mi = MyClass.GetMethod(myMethodName);
     mi.Invoke(obj, null);

     // some code

     return s;
}

StackTrace:

"at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\r\n   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type) ..."

What did I miss? (Sorry for my English.)

EDIT: Tried to add this line to the class to grant full permission:

[PermissionSet(SecurityAction.Assert, Unrestricted = true)]

After this object obj = Activator.CreateInstance(MyClass); worked fine. I need sandboxing, so this is not a solution.


Solution

  • Your problem is your loaded Sandboxer assembly while fully trusted is being called with an untrusted stack. Therefore you are getting a security exception somewhere in the activation (not 100% sure why based on your code). The solution is exactly as you found, to add the assert to the function. This would allow this function to execute with full trust (the assert will block the stack walk) but the loaded assembly is partially trusted so will still be sandboxed.

    If you do this you need to ensure that the sandboxed code cannot interact with the method and therefore could abuse it. You can do this by marking the function SecurityCritical so that only fully trusted code can interact with the function.