Dela via


Assembly Provided Evidence

We all know that the CLR provides many types of evidence to assemblies and AppDomains by default, but one feature of the runtime that's much less known is that assemblies can actually provide evidence of their own.  This seems to be one of the best kept secrets in CLR security, the only mention of it that most of the people on the team today could find was in the .NET Framework Security book published for v1.0 of the framework.  (In a somewhat interesting note, nearly everybody on the security team knew that this was possible, but the number of people who actually knew how to embed the evidence was not nearly as high.  I'll let you guess at the number, but I'll give you a clue ... if it takes more than one bit to represent the number you're guessing then you've probably gone too high :-) )  Since that book is out of print, I've decided to write up the process here in case it interests anyone else.

Obviously evidence that's provided by an assembly is not as trustworthy as evidence that's provided by a host, however you can imagine situations in which assembly provided evidence could be used to make trust decisions for an assembly.  One example might be an assembly that ships with a signed XML file indicating the results of a security audit done by a trusted company:

 <securityAudit>
     <assembly strongName="TrustedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=173144ef9dd6c5fd">
         <auditDate>2/20/2007 11:39:03PM</auditDate>
         <auditResult>success</auditResult>
         <verificationUrl>https://audits.trustedcompany.com/auditResult.aspx?auditId=12345</verificationUrl>
     </assembly>
     <Signature xmns="https://www.w3.org/2000/09/xmldsig#">
         <!-- ... -->
         <X509Data>
             <!-- .... -->
         </X509Data>
 
         <!-- ... -->
     </Signature>
 </securityAudit>

Since this hypothetical XML blob contains the strong name of the assembly to which it refers and the X.509 certificate of the company that did the audit and produced the signature, we could choose to trust that assembly if we trusted the company that provided the blob.

So, how would I go about embedding this custom evidence into my assembly?  It's basically a three step process:

  1. Write the custom evidence using the BinaryFormatter into a file
  2. Compile your assembly into a netmodule
  3. Use the AL.exe tool to link the evidence into the assembly

Lets look at each step in detail.

1. Write the custom evidence using the BinaryFormatter into a file

The first step is to serialize the assembly evidence into a file that can be used by build tools to embed into the assembly.  To do this, you'll need to write a short program that dumps the evidence out.  The code looks something like this:

     public static void Main()
     {
         XmlElement auditSignedXml = GetAuditSignedXml();
         Evidence evidence = new Evidence();
         evidence.AddAssembly(auditSignedXml.OuterXml);
  
         using (FileStream evidenceFile = new FileStream("TrustedAssembly.evidence", FileMode.Create, FileAccess.Write))
         {
             BinaryFormatter formatter = new BinaryFormatter();
             formatter.Serialize(evidenceFile, evidence);
         }
  
         return;
     }

This code is fairly straightforward.  First we load up the XML that our security auditors gave us into an XmlElement.  We can't add the XmlElement directly into the evidence however because all evidence must be serializable and XmlElement is not.  However, String is, so we can just get the OuterXml and add it to the Assembly evidence collection.  It's important to AddAssembly rather than AddHost here, otherwise the CLR will ignore the evidence when it loads your assembly.

Once the Evidence collection is created, we use a FileStream and the BinaryFormatter to dump the evidence to a binary file on disk, in this case cleverly named TrustedAssembly.evidence, however any name will do.

2. Compile your assembly into a netmodule

The next step is to compile the code that would normally go into your assembly, however this time instead of building an .exe or .dll, you want to build a .netmodule.  In C# this is done by saying /target:module on the command line, and VB supports a similar switch.  I don't know of a way to modify the project properties in the Visual Studio UI to produce a netmodule, but you can manually edit your project file to achieve this:

 <Project DefaultTargets="Build" xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <!-- ... -->
     <OutputType>Module</OutputType>
     <!-- ... -->
   </PropertyGroup>
   <!-- ... -->
 </Project>

In a standard Visual Studio generated project, you'll just have to change the OutputType property to be a Module rather than an Exe or a Library.  This will cause the build to output <projectname>.netmodule as its result.

The final step is to use the AL.exe tool to link the binary evidence file and the .netmodule containing the code for your assembly into the final assembly.  AL takes the input evidence using it's /evidence command line switch, so an example of linking TrustedAssemblyEvidence.bin with TrustedAssembly.netmodule to produce TrustedAssembly.exe would be:

al.exe /evidence:TrustedAssembly.evidence  /out:TrustedAssembly.exe /main:Microsoft.Samples.TrustedAssembly.Main /target:exe /keyfile:TrustedAssembly.snk TrustedAssembly.netmodule

Here we tell al to link our evidence and our netmodule into an exe named TrustedAssembly.exe, setting the entry point to be Microosft.TrustedAssembly.Main and signing the assembly with TrustedAssembly.snk.

That's it, the resulting assembly now has evidence embedded within it.  If you load this assembly in the CLR, you can look at it's evidence and in addition to the standard evidence types you would find if you looked at the output of GetHostEnumerator, you would find the string containing this XML blob in the assembly enumerator.  A custom CLR host could use this evidence to decide that although this assembly was loaded from the Internet, it trusts the company that did the security audit, and therefore will grant the assembly extra permission.

Comments

  • Anonymous
    February 23, 2007
    The Evidence class supports being enumerated in three different ways: GetAssemblyEnumerator GetHostEnumerator

  • Anonymous
    March 20, 2007
    [Default] Evaluation Center Experience the New MSDN Evaluation Center Register to download software and

  • Anonymous
    April 10, 2007
    It took until .NET 2.0 till the verifier was even able to do a real validation (on .NET 1.1 and before you coudl bypass the verifier using some funny byte-array constructs, by design!), and the  class library is still trivially insecure. Why exactly do you intend to keep up that maculature of security?

  • Anonymous
    April 13, 2007
    That's just not true.  If the v1.x verifier was disabled by a strange sequence of IL opcodes, this would not be by design, and we would consider it a major bug and have it fixed. Similarly, if you have examples of how the class library is insecure, then you should post them to the MSDN feedback site or here, and we will fix the bugs. -Shawn

  • Anonymous
    April 17, 2007
    This is true, and in fact .NET 2.0 provides that fix. The problems was how structs were internally treaten as byte arrays, and based upopn the array indexes you never really knew which field of the struct you hid. A classical variant of pointer arithmetics, which were intentionally build into the "safe" verifiable subst (probably due to speed optimization). Heck, this is even documented! And the class library is, AFAICS, intended to be insecure, and references are not properly checked. F.e. when you open a File with the File(char[]) constructor, you'll maintain a reference to the filename. Check the File against a FileSecurityPermission, then try accessing the file, and at the same time modify the filename using the kept reference. If your timing is good, you'll hit the codepoint between verifying the filename and actually opening it. This works with about any non-immutable non-primitive type. It's already bad if trusted Java applets don't take such issues into account. But the class library including internal security-relevant code, running privileged threads, being insecure is the worst.

  • Anonymous
    April 18, 2007
    The class library is certainly not intended to be insecure.  I'm not sure which constructor you're talking about.  File is a static class, and FileStream does not have a char[] constructor.  The file names are strings, and strings are immutable, which means that the attack you describe is not possible on those constructors. -Shawn