.Net Assembly Spoof Attack

To be honest I am not sure about the name of such attack, but in the nutshell it is attack where the original good code is replaced by bad one with the same interface but very bad implementation - may be Trojan DLL? Anyway...

My Australia based teammate Rocky posted sometime ago coolest screencast - Assembly Hijacking. He calls it hijacking. Go see it, very cool.

The final verdict was to sign the assemblies with SNK which is almost always a good idea to do. This should definitely prevent such attack he demonstrated. To " Evaluate Whether You Need Strong Names " check on the following in referenced article:

  • You need to add your assembly to the global assembly cache.
  • You want to prevent partial trust callers.
  • You want cryptographically strong evidence for security policy evaluation. <-- this one is for our case

Lately I stumbled on another post Assembly Load Contexts Subtleties that "focus on Assembly.Load and Assembly.LoadFrom" which discusses dynamic assembly and types invocation.

I thought to myself "What role SNK plays in this case?"

None

Dynamically loaded assembly is not checked for its evidence. That means that all applications that use reflection to load assemblies dynamically are vulnerable to such attack - regardless if there is SNK in place or not.

What to do to prevent such attack when using reflection?

1. Do sign with SNK your assemblies.

2. Use Full Assembly Names When You Dynamically Load Assemblies

The code you should find there will look like this:

public static StrongName GetStrongName(Assembly assembly)
{
if(assembly == null)
throw new ArgumentNullException("assembly");

AssemblyName assemblyName = assembly.GetName();
// get the public key blob
byte[] publicKey = assemblyName.GetPublicKey();
if(publicKey == null || publicKey.Length == 0)
throw new InvalidOperationException(String.Format("{0} is not strongly named", assembly));
StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);

// create the StrongName
return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
}

And here is the check itself:

//LOAD ASSEMBLY DYNAMICALLY
Assembly assembly = Assembly.LoadFrom("ReflectedDll.dll");

//GET STRONG NAME FOR THE LOADED ASSEMBLY
StrongName sn = GetStrongName(assembly);

StrongName myStrongName = null;

//GET CURRENT APPDOMAIN STRONG NAME
IEnumerator enumerator = (IEnumerator)AppDomain.CurrentDomain.Evidence.GetEnumerator();
enumerator.Reset();
while (enumerator.MoveNext())
{
if (enumerator.Current.GetType().Equals(typeof(StrongName)))
myStrongName = (StrongName)enumerator.Current;
}

if (!sn.PublicKey.Equals(myStrongName.PublicKey))
{
throw new ApplicationException("SPOOFED!!");
}

//OK, STRONG NAME IS COOL, LETS RUN IT FURTHER

 

In the world of provider design pattern (abstract factory – GoF definition) where assemblies get loaded dynamically one should pay closer attention to this.

Enjoy