SecurityException when running sandbox code in C#

Viewed 132

I am following the example code from this page to create a sandbox application that launches Windows executable files with limited permissions: https://docs.microsoft.com/en-us/previous-versions/dotnet/framework/code-access-security/how-to-run-partially-trusted-code-in-a-sandbox?redirectedfrom=MSDN

I have changed the example code so that instead of running a specific assembly in a specific path (and including that assembly as a dependency of the sandbox program), I run an executable selected by the user. The code is:

AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = Path.GetFullPath(filepath);
        string sandboxContainingFilepath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Substring(6);
        PermissionSet permSet = new PermissionSet(PermissionState.None);
        permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
        permSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess, sandboxContainingFilepath));
        StrongName fullTrustAssembly = typeof(AppLoader).Assembly.Evidence.GetHostEvidence<StrongName>();
        AppDomain newDomain = AppDomain.CreateDomain("AppLoader", null, adSetup, permSet, fullTrustAssembly);
        ObjectHandle handle = Activator.CreateInstanceFrom(
            newDomain, typeof(AppLoader).Assembly.ManifestModule.FullyQualifiedName,
            typeof(AppLoader).FullName
            );
        AppLoader newDomainInstance = (AppLoader)handle.Unwrap();
        string filename = new DirectoryInfo(filepath).Name;
        newDomainInstance.ExecuteUntrustedCodeFromPath(Path.GetFullPath(filepath));

...

private void ExecuteUntrustedCodeFromPath(string filepath)
    {
        ProcessStartInfo startInfo = new ProcessStartInfo(filepath);
        startInfo.CreateNoWindow = false;
        startInfo.UseShellExecute = false;
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;

        try
        {
            using (Process exeProcess = Process.Start(startInfo))
            {
                exeProcess.WaitForExit();
            }
        }
        catch (Exception ex)
        {
            new PermissionSet(PermissionState.Unrestricted).Assert();
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());
            CodeAccessPermission.RevertAssert();
            Console.ReadLine();
        }
    }

The second block replaces the method ExecuteUntrustedCode() in the example. When using that method, I find that if the untrusted assembly reads from a file it doesn't have permission for, a SecurityException is thrown, and otherwise it is able to run with no exception. This is my goal for my altered version of the code.

However, when running a selected executable in this way, I get the following exception:

    SecurityException caught:
System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=...' failed.
   at System.StubHelpers.StubHelpers.DemandPermission(IntPtr pNMD)
   at Microsoft.Win32.NativeMethods.CreateProcess(String lpApplicationName, StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, Boolean bInheritHandles, Int32 dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation)
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at Trustworthy_ACW_1.AppLoader.ExecuteUntrustedCodeFromPath(String filepath) in ...\AppLoader.cs:line 79
The action that failed was:
Demand
The type of the first permission that failed was:
System.Security.Permissions.SecurityPermission
The first permission that failed was:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."
version="1"
Flags="UnmanagedCode"/>

The demand was for:
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."
version="1"
Flags="UnmanagedCode"/>

The granted set of the failing assembly was:
<PermissionSet class="System.Security.PermissionSet"
version="1">
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."
version="1"
Read="...\bin\Debug"
Write="...\bin\Debug"
Append="...\bin\Debug"
PathDiscovery="...\bin\Debug"/>
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."
version="1"
Flags="Execution"/>
</PermissionSet>

In case it makes a difference, I did notice that the URL of the failed assembly is my sandbox application, not the target executable. I can add the UnmanagedCode permission, but if I do that, no exception is thrown if the target executable reads from a file outside of the /bin folders I gave it permission for. I have confirmed that it is reading from this file by having it print the output. What do I need to do to make my sandbox application discriminate between allowed and disallowed filepaths to read/write for the executable at the selected filepath?

0 Answers
Related