Programatically Modifying .NET Assemblies

Continuing with my WCF vulnerability research I was recently taking a look at “Microsoft.Exchange.Directory.TopologyService.exe” which is part of Exchange server and exposes a WCF endpoint over a NetTcpBinding.

Client code can usually be found in the install path of a service and is generally a lot easier to use vs. building a client from scratch. After analyzing the service for a bit I was able to find client code in one of the DLLs. There was a problem though, which is that the class I wanted to use, “TopologyServiceClient”, was marked with the access modifier “internal”. This means that the class can only be accessed from within the assembly where it is defined. In other words, I could not import the assembly into my own project and use the internal classes.

To work around this I first tried copy pasting the code that I needed into my project…due the many layers of abstraction this wasn’t going to work. Another thing I tried was decompiling the assembly, changing the access modifiers, and then rebuilding…also wasn’t going to work. One more idea was to use the assembly editor in dnSpy to make the changes, but because of the nested classes with various levels of scoping it wasn’t enough to just change the access modifier for one class or even one method, and changing them all by hand would take way too long. Determined to use these classes, I eventually stumbled on dnlib, from the maker of dnSpy. dnlib is described simply as a ” .NET module/assembly reader/writer library”. Using a shotgun approach, I was able to use it to loop through all the classes and methods, change the access modifier from “internal” or “private” to “public” and save the modified assembly without rebuilding it, essentially patching it.

After doing this I was able to import the modified assembly, create the client object and interact with the service. Regretfully, the service requires admin credz in order to connect. Even worse, I believe that if non-admin credz could be used, none of the behavior would be exploitable anyway (not 100% sure though).

Regardless, I wanted to share the code and also use this as a reminder for myself, as I have a feeling it will be useful again in the future and I didn’t see any examples online of people doing this.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using dnlib.DotNet;
using dnlib.DotNet.Emit;

namespace CecilTest {
    class Program {
        static void Main(string[] args) {

            string libDir = @"C:\lib";
            string destDir = @"C:\modded-lib\";
            //var files = Directory.EnumerateFiles(libDir, "*.dll");
            var files = Directory.EnumerateFiles(libDir, "Microsoft.Exchange.Data.Directory.dll");
            foreach (string file in files) {
                Console.WriteLine(string.Format("[+] Analyzing {0}", file));
                using (ModuleDefMD mod = ModuleDefMD.Load(file)) {
                    foreach (var type in mod.GetTypes()) {
                        if (type.IsNotPublic && !type.Attributes.HasFlag(TypeAttributes.Abstract)) {
                            type.Attributes |= TypeAttributes.Public;
                            foreach (var method in type.Methods) {
                                if (type.FullName.ToLower().Contains("topologyserviceclient")) {
                                    if (method.Attributes.HasFlag(MethodAttributes.Private)) {
                                        method.Attributes ^= MethodAttributes.Private;
                                        method.Attributes |= MethodAttributes.Public;
                                    }
                                    if (method.Attributes.HasFlag(MethodAttributes.PrivateScope)) {
                                        method.Attributes ^= MethodAttributes.PrivateScope;
                                    }
                                }
                                method.Attributes |= MethodAttributes.Public;
                            }
                            foreach (var field in type.Fields) {
                                //field.Attributes |= FieldAttributes.Public;
                            }
                            foreach (var nest in type.NestedTypes) {
                                //nest.Attributes |= TypeAttributes.Public;
                            }
                            
                        }
                    }
                    string destFile = destDir + Path.GetFileName(file);
                        mod.Write(destFile);
                        Console.WriteLine(string.Format("[+] Outputting to: {0}", destFile));
                }
            }
        }
    }
}

And here is what the modified class looks like when decompiled: