Use of APTCA

 

The .Net Framework provides a number of security features that enables in secure applications, and one of the features is evidence based that works on the top of operating system security. The evidence based security gathers evidences regarding the assembly and determine whether the code has permissions to execute. Any assembly that gets installed in Global Assembly Cache (GAC) is considered by .Net Framework to have Full Trust i.e. the code has unrestricted access to all protected resources and these assemblies can only be called by other Full Trust assemblies. However the partial trust assemblies cannot call the Full Trust assemblies therefore .NET Framework 1.1 introduced an assembly-level custom attribute AllowPartiallyTrustedCallers. The Full Trust assemblies marked with AllowPartiallyTrustedCallersAttribute can be called by partially trusted assemblies and the implicit demand is not called. The improper usage of this attribute can introduce vulnerabilities that can lead to luring attack. The solution presented in this document discusses the proper usage of AllowPartiallyTrustedCallersAttribute.

 

Trust Levels

In managed world an Assembly can belong to one of the following trust models:

 

1. Full Trust
Any assembly that has been strong named and installed in GAC is Full Trust Assembly, unless someone has lock down the security policy by changing the grant set of local machine to something less than full trust. This permission set gives code unrestricted access to all protected resources. This can be a very dangerous permission set; it allows full access to your computer's resources such as the file system or network access, potentially operating outside the control of the security system. If you are modifying security policy, be sure to set policy such that only assemblies you fully trust could possibly get this permission set.

2. Partial Trust

Code that does not get full trust will have partial trust. Partial trust code will have to call framework code, and hence an assembly with full trust will be called by an assembly with partial trust.

 

Why Full Trust is required?

Full Trust is required:

· To call Unmanaged Code

· To create COM objects

· To use OLEdb or ODBC

 

A partial trust assembly cannot perform the above mentioned operations and most core .Net assemblies don’t have the AllowPartiallyTrustedCallersAttribute(APTCA) applied, hence limiting partial trust assemblies.

 

The Problem

A class library needs to perform some privileged operations (example: reading contents of some system file) and the assembly needs to be shared among various applications in the organization. To make the assembly be shared among various applications we need to strong name our assembly and host it in GAC. Now this library can only be called by strong named assemblies or assemblies with full trust. To make the assembly accessible by other partially trusted assemblies we need to add APTCA to the assembly.

The Luring Attack

The result of adding APTCA any un-trusted / partially trusted code can call the trusted code. The trusted code then executes the privileged code on the behalf of the un-trusted code. This is known as Luring Attack where an attacker can take advantage of use of APTCA to lure a Fully Trusted assembly to execute un-trusted code on its behalf like calling the privileged operation on the victim machine.

 

Code Sample: The Luring Attack

// The Trusted Code

using System;

using System.Security;

using System.Security.Permissions;

using System.Reflection;

// This assembly executes with full trust and

// allows partially trusted callers and would reside in GAC

// Applying APTCA to assembly

[assembly:AllowPartiallyTrustedCallers]

namespace TrustedCodeWithImproperAPTCA

{

public class PerformPrivilgedOperations

{

public static void PrivilgedOperations()

{

// Some Call to Privileged Code

}

}

}

_______________________________________________________________________________________________

// The Un-trusted / Attacker Code

using System;

using TrustedCodeWithImproperAPTCA;

// If this test is run from the local computer, it gets full trust by

// default. So Remove full trust.

[assembly:System.Security.Permissions.PermissionSetAttribute(

System.Security.Permissions.SecurityAction.RequestRefuse, Name="FullTrust")]

namespace TestSecLibrary

{

class TestApctaMethodRule

{

public static void Main()

{

// Indirectly calls PrivilgedOperations.

PerformPrivilgedOperations.PrivilgedOperations();

}

}

}

Solution design

APTCA only removes the implicit Link demands; any demands explicitly placed in your assembly will still be enforced. This means that even once an assembly has been marked with APTCA, it could still have some types in it that are not usable from partial trust by use of Explicit Demands.

 

Using the above stated fact we can use the APTCA properly with Explicit Demands. Thus we can mark the Full trust assemblies with APTCA and also have explicit demands to prevent the Luring attack. A better design would be not to have APTCA directly applied on the Trusted Code, but introduce a Façade assembly. The Façade assembly / code will have the full trust as the trusted code with APTCA applied to it, and will have explicit demand calls, thus making it non-vulnerable for the luring attack. This allows the Trusted Code to take advantage of Link Demands as well as protection from luring attack with help on explicit demands through Façade.

 

Code Sample: The proper use of APTCA

// The Trusted Code.

using System;

namespace TrustedCode

{

public class PerformPrivilgedOperations

{

public static void PrivilgedOperations()

{

// Some Call to Privileged Code.

}

}

}

_______________________________________________________________________________________________

// The Façade Code (with APTCA applied)

using System;

using System.Security;

using System.Security.Permissions;

using System.Reflection;

using TrustedCodeWithImproperAPTCA;

// Applying APTCA to assembly

[assembly:AllowPartiallyTrustedCallers]

namespace FacadeWithAPTCACallingTrustedCode

{

public class FacadeCallingTrustedCode

{

public static void CallingTrustedCode()

{

// This security check fails if the caller

// does not have full trust.

NamedPermissionSet pset= new NamedPermissionSet("FullTrust");

// This try-catch block shows the caller's permissions.

// Correct code would either not catch the exception,

// or would rethrow it.

try

{

pset.Demand();

}

catch (SecurityException e)

{

Console.WriteLine("Demand for full trust:{0}", e.Message);

}

// Calls PrivilgedOperations()

PerformPrivilgedOperations.PrivilgedOperations();

}

}

}

// The Un-trusted / Partial Code

using System;

using FacadeWithAPTCACallingTrustedCode;

// If this test is run from the local computer, it gets full trust by default.

// Remove full trust.

[assembly:System.Security.Permissions.PermissionSetAttribute(

System.Security.Permissions.SecurityAction.RequestRefuse, Name="FullTrust")]

namespace TestSecLibrary

{

class TestApctaMethodRule

{

public static void Main()

{

FacadeCallingTrustedCode.CallingTrustedCode();

}

}

}

References

Introduction to Code Access Security - https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconintroductiontocodeaccesssecurity.asp

Code Access Security Basics - https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcodeaccesssecuritybasics.asp

Using Libraries from Partially Trusted Code - https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconUsingLibrariesFromPartiallyTrustedCode.asp

APTCA methods should only call APTCA methods - https://msdn2.microsoft.com/en-us/library/ms182297.aspx