InternalsVisibleTo attribute and security vulnerability

Posted by Sergey Litvinov on Stack Overflow See other posts from Stack Overflow or by Sergey Litvinov
Published on 2013-06-30T22:05:24Z Indexed on 2013/06/30 22:21 UTC
Read the original article Hit count: 1348

I found one issue with InternalsVisibleTo attribute usage. The idea of InternalsVisibleTo attribute to allow some other assemblies to use internal classes\methods of this assembly. To make it work you need sign your assemblies. So, if other assemblies isn't specified in main assembly and if they have incorrect public key, then they can't use Internal members.

But the issue in Reflection Emit type generation. For example, we have CorpLibrary1 assembly and it has such class:


    public class TestApi
    {
        internal virtual void DoSomething()
        {
            Console.WriteLine("Base DoSomething");
        }

        public void DoApiTest()
        {
            // some internal logic
            // ...
            // call internal method
            DoSomething();
        }
    }

This assembly is marked with such attribute to allow another CorpLibrary2 to make inheritor for that TestAPI and override behaviour of DoSomething method.

[assembly: InternalsVisibleTo("CorpLibrary2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100434D9C5E1F9055BF7970B0C106AAA447271ECE0F8FC56F6AF3A906353F0B848A8346DC13C42A6530B4ED2E6CB8A1E56278E664E61C0D633A6F58643A7B8448CB0B15E31218FB8FE17F63906D3BF7E20B9D1A9F7B1C8CD11877C0AF079D454C21F24D5A85A8765395E5CC5252F0BE85CFEB65896EC69FCC75201E09795AAA07D0")]

The issue is that I'm able to override this internal DoSomething method and break class logic. My steps to do it:

  1. Generate new assembly in runtime via AssemblyBuilder
  2. Get AssemblyName from CorpLibrary1 and copy PublikKey to new assembly
  3. Generate new assembly that will inherit TestApi class
  4. As PublicKey and name of generated assembly is the same as in InternalsVisibleTo, then we can generate new DoSomething method that will override internal method in TestAPI assembly

Then we have another assembly that isn't related to this CorpLibrary1 and can't use internal members. We have such test code in it:

class Program
    {
        static void Main(string[] args)
        {
            var builder = new FakeBuilder(InjectBadCode, "DoSomething", true);
            TestApi fakeType = builder.CreateFake();

            fakeType.DoApiTest();
            // it will display:
            //   Inject bad code
            //   Base DoSomething


            Console.ReadLine();
        }

        public static void InjectBadCode()
        {
            Console.WriteLine("Inject bad code");
        }
    }

And this FakeBuilder class has such code:

        /// 
    /// Builder that will generate inheritor for specified assembly and will overload specified internal virtual method 
    /// 
    /// Target type
    public class FakeBuilder
    {
        private readonly Action _callback;

        private readonly Type _targetType;

        private readonly string _targetMethodName;

        private readonly string _slotName;

        private readonly bool _callBaseMethod;

        public FakeBuilder(Action callback, string targetMethodName, bool callBaseMethod)
        {
            int randomId = new Random((int)DateTime.Now.Ticks).Next();
            _slotName = string.Format("FakeSlot_{0}", randomId);
            _callback = callback;
            _targetType = typeof(TFakeType);
            _targetMethodName = targetMethodName;
            _callBaseMethod = callBaseMethod;
        }

        public TFakeType CreateFake()
        {
            // as CorpLibrary1 can't use code from unreferences assemblies, we need to store this Action somewhere. 
            // And Thread is not bad place for that. It's not the best place as it won't work in multithread application, but it's just a sample
            LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot(_slotName);
            Thread.SetData(slot, _callback);

            // then we generate new assembly with the same nameand public key as target assembly trusts by InternalsVisibleTo attribute
            var newTypeName = _targetType.Name + "Fake";
            var targetAssembly = Assembly.GetAssembly(_targetType);

            AssemblyName an = new AssemblyName();
            an.Name = GetFakeAssemblyName(targetAssembly);

            // copying public key to new generated assembly
            var assemblyName = targetAssembly.GetName();
            an.SetPublicKey(assemblyName.GetPublicKey());
            an.SetPublicKeyToken(assemblyName.GetPublicKeyToken());

            AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyBuilder.GetName().Name, true);

            // create inheritor for specified type
            TypeBuilder typeBuilder = moduleBuilder.DefineType(newTypeName, TypeAttributes.Public | TypeAttributes.Class, _targetType);

            // LambdaExpression.CompileToMethod can be used only with static methods, so we need to create another method that will call our Inject method
            // we can do the same via ILGenerator, but expression trees are more easy to use
            MethodInfo methodInfo = CreateMethodInfo(moduleBuilder);

            MethodBuilder methodBuilder = typeBuilder.DefineMethod(_targetMethodName, MethodAttributes.Public | MethodAttributes.Virtual);

            ILGenerator ilGenerator = methodBuilder.GetILGenerator();

            // call our static method that will call inject method
            ilGenerator.EmitCall(OpCodes.Call, methodInfo, null);

            // in case if we need, then we put call to base method
            if (_callBaseMethod)
            {
                var baseMethodInfo = _targetType.GetMethod(_targetMethodName, BindingFlags.NonPublic | BindingFlags.Instance);

                // place this to stack
                ilGenerator.Emit(OpCodes.Ldarg_0);

                // call the base method
                ilGenerator.EmitCall(OpCodes.Call, baseMethodInfo, new Type[0]);

                // return
                ilGenerator.Emit(OpCodes.Ret);
            }

            // generate type, create it and return to caller
            Type cheatType = typeBuilder.CreateType();
            object type = Activator.CreateInstance(cheatType);

            return (TFakeType)type;
        }

        /// 
        /// Get name of assembly from InternalsVisibleTo AssemblyName
        /// 
        private static string GetFakeAssemblyName(Assembly assembly)
        {
            var internalsVisibleAttr = assembly.GetCustomAttributes(typeof(InternalsVisibleToAttribute), true).FirstOrDefault() as InternalsVisibleToAttribute;

            if (internalsVisibleAttr == null)
            {
                throw new InvalidOperationException("Assembly hasn't InternalVisibleTo attribute");
            }

            var ind = internalsVisibleAttr.AssemblyName.IndexOf(",");
            var name = internalsVisibleAttr.AssemblyName.Substring(0, ind);

            return name;
        }

        /// 
        /// Generate such code:
        /// ((Action)Thread.GetData(Thread.GetNamedDataSlot(_slotName))).Invoke();
        /// 
        private LambdaExpression MakeStaticExpressionMethod()
        {
            var allocateMethod = typeof(Thread).GetMethod("GetNamedDataSlot", BindingFlags.Static | BindingFlags.Public);
            var getDataMethod = typeof(Thread).GetMethod("GetData", BindingFlags.Static | BindingFlags.Public);

            var call = Expression.Call(allocateMethod, Expression.Constant(_slotName));
            var getCall = Expression.Call(getDataMethod, call);
            var convCall = Expression.Convert(getCall, typeof(Action));

            var invokExpr = Expression.Invoke(convCall);

            var lambda = Expression.Lambda(invokExpr);

            return lambda;
        }


        /// 
        /// Generate static class with one static function that will execute Action from Thread NamedDataSlot
        /// 
        private MethodInfo CreateMethodInfo(ModuleBuilder moduleBuilder)
        {
            var methodName = "_StaticTestMethod_" + _slotName;
            var className = "_StaticClass_" + _slotName;

            TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class);
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, MethodAttributes.Static | MethodAttributes.Public);
            LambdaExpression expression = MakeStaticExpressionMethod();

            expression.CompileToMethod(methodBuilder);

            var type = typeBuilder.CreateType();

            return type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
        }
    }

remarks about sample: as we need to execute code from another assembly, CorpLibrary1 hasn't access to it, so we need to store this delegate somewhere. Just for testing I stored it in Thread NamedDataSlot. It won't work in multithreaded applications, but it's just a sample.

I know that we use Reflection to get private\internal members of any class, but within reflection we can't override them. But this issue is allows anyone to override internal class\method if that assembly has InternalsVisibleTo attribute.

I tested it on .Net 3.5\4 and it works for both of them.

How does it possible to just copy PublicKey without private key and use it in runtime?

The whole sample can be found there - https://github.com/sergey-litvinov/Tests_InternalsVisibleTo

UPDATE1: That test code in Program and FakeBuilder classes hasn't access to key.sn file and that library isn't signed, so it hasn't public key at all. It just copying it from CorpLibrary1 by using Reflection.Emit

© Stack Overflow or respective owner

Related posts about c#

Related posts about .NET