Activation Contexts — A Love Story

Philip Tsukerman
Cybereason
Published in
7 min readDec 2, 2019

TL;DR — Microsoft Windows loads a version of the Microsoft.Windows.SystemCompatible assembly manifest into every process. By tampering with it, you can inject DLL side-loading opportunities into every process and perform COM hijacking without touching the registry. The only hitch? If the manifest is replaced by another version, it might kill your persistence by surprise.

Not all research projects are created equal, and there are many that never deliver on the promise of useful information. Luckily, the confrontations with wrong assumptions, moments of feeling like an idiot, and various rabbit holes along the way are still going to teach you quite a lot, so not all is in vain. This post is about one such project, which is why it will be mostly focused on my process and not the perhaps anticlimactic conclusions.

Microsoft.Windows.Actctx is an interesting COM object that provides some rather intriguing capabilities for attackers that have, for the most part, been covered by Casey Smith. As an example, it can give you the ability to create COM objects without going through the registry. When I first started to mess around with it, I first wondered, “What else can it do?” and “Are there any other objects which could provide a similar capability?”. To answer, I did what I always do with COM objects, which is trying to find if there are any other objects or interfaces implemented by the same executable. I fired up the ever reliable OleViewDotNet, searched for the ProgID, and encountered this result:

I was really expecting more stuff here.

Feeling rather confused and betrayed by this, I searched the registry directly, which again gave me nothing. I tried to debug the creation of the object while setting a breakpoint on library loads, and was finally greeted by the library implementing the object — sxsoa.dll. A quick reverse engineering effort led me to the conclusion that there probably aren’t any secret extra capabilities in the dll, but now I was left with an even more interesting question …How is Windows able to find and create this object?

The first step to answering this question was to reverse the CLSIDFromProgID function, which translates the friendly ProgID name of the ActCtx object to a CLSID. Originally, I assumed that there would be a rather simple function that looks stuff up in the registry, and maybe has some hardcoded/otherwise registered external object lists. It turned out to be a bit more complicated than that, as there were a couple of indirect calls to class methods and interfaces for which I did not have a definition.

The functionality isn’t immediately apparent without recovering the various interface definitions.

There were actually some symbols for the CComCatalog class returned from the GetComCatalogObject, which gave me some interesting clues about the different ways to “register” a COM object and might be a fun target for further exploratory research.

struct __cppobj CComCatalog : IComCatalog, IComCatalogSCM, IComCatalogInternalImpl, IComCatalog2, IComCatalog2Internal, IWinRTCatalogInternal, IComCatalogSettings, IComCatalogTreatAsClass, IPackagedComProxyStubCatalogInternal, IPackagedComClassCatalogInternal, ITypeLibCatalogInternal{int m_cRef;IComCatalogInternal *m_pCatalogRegNative;IComCatalogInternal *m_pCatalogCOMBaseInCLB;IComCatalogInternal *m_pCatalogRegX86Wow;IComCatalogInternal *m_pCatalogRegArm32Wow;IComCatalogInternal *m_pCatalogCLB;IComCatalogInternal *m_pCatalogSxS;IComCatalogInternal *m_pCatalogBuiltInActivationStore;IComCatalogInternal *m_pCatalogPerUserActivationStore;IComCatalogInternal *m_pCatalogInboxAppsStore;Microsoft::WRL::ComPtr<IComCatalogInternal> m_pCatalogPrivateHive;Microsoft::WRL::ComPtr<IComCatalogInternal> m_pCatalogPackagedComAllPackages;Microsoft::WRL::ComPtr<IComCatalogInternal> m_pCatalogPackagedComCurrentPackage;};

My next step was to debug CLSIDFromProgID when creating the ActCtx object, where I discovered that the CLSID itself is supplied by the CComSxSClassInfo::GetConfiguredClsid function. This is when things finally started to click. SxS, which is short for Side-by-Side Assemblies, is the mechanism by which ActCtx can create COM objects without registration, and is a feature of Windows that is supposed to provide applications with the ability to use specific versions of libraries without fear of versioning conflicts that come from a different, incompatible version of a library that is suddenly registered, overriding the one needed by the application. Basically, you parse an XML-formatted manifest (just like the ones supplied to ActCtx) into a structure called an Activation Context, and the current Activation Context determines the various objects and libraries that need to be redirected to a specific implementation. ActCtx simply creates a new Activation Context using the supplied manifest, and then makes it the current context when creating a new object with the CreateObject method. A very well written post on the subject can be found here.

Setting a manifest for ActCtx and using it to create a new object,

Returning to our main question, I didn’t immediately suspect SxS as a source of our ActCtx registration. There was absolutely no manifest provided with my test harness, so there shouldn’t be any redirections at all!

Luckily, I was able to find this awesome utility, to parse and output Activation Contexts, and was immediately confronted by a curious command line option:

So apparently, apart from the dynamic, per-process current Activation Context, there exists something called a “System Default Activation Context”, which defines redirections globally. This is pretty weird. I have not yet thought of a reason to use this instead of just putting the same information in the registry. Nonetheless, running the tool gives us the following output, and validates my assumptions:

Finally, some info about ActCtx!

At least part of the mystery has been solved. Processes are able to discover and create ActCtx because it’s information is always embedded in the System Default Activation Context (let’s just agree to call it the SDAC from now on), even if the application doesn’t use a manifest. This poses an interesting opportunity, as finding and manipulating the source of this system-wide context could possibly allow us to inject redirections into pretty much every process on the machine, which could become an extremely interesting persistence mechanism!

Activation Contexts in the PEB, including our elusive SystemDefaultActivationContextData field.

The most logical place to look for the origin of the SDAC is during process creation, so the most straightforward approach is to make a process spawn a child and debug them both during creation, trying to pinpoint the phase in which the SDAC appears. The NtCreateUserProcess syscall is a nice first suspect, as it’s where the kernel actually creates and sets up the process and its relevant structures. Alas, stepping over it and checking the state of the child shows us that this is not the right answer:

No Activation Contexts here, yet…

At least now we know that this happens somewhere in the user-mode part of process creation, which is why our next step is to look at the CreateProcessInternal function, identify interesting calls (mostly by name, at first), and then step over them to see if this fills the SDAC field of the child process. This method eventually led me to discover that the SDAC field is populated after a call to ntdll!CsrClientCallServer.

The CsrClientCallServer function implements the ALPC interface between Win32 processes and the CSRSS process. This means that the creation and mapping of the SDAC to the child process is done by CSRSS at the request of the parent. Instead of trying to reverse engineer the whole path between processes, adding yet another rabbit hole to the ever expanding scope of this project, I decided to simply look at csrss.exe statically, and search for some suspicious strings and function names. This quickly led me (through a bunch of other imported dlls) to the sxssrv.dll library, which fortunately contained the descriptively named BaseSrvSxsDoSystemDefaultActivationContext function.

Look, an assembly name!

BaseSrvSxsDoSystemDefaultActivationContext checks if there already exists a section containing the SDAC in a global variable, and if it doesn’t, creates a new activation context from a hardcoded assembly name, which it then saves and maps to the child process. Afterwards, it manually updates the PEB of the child with the pointer to the SDAC, which can now be used by the child. That’s it, we finally know the exact origin of the SDAC, and can now start tampering with it. :)

Mapping the SDAC and updating the PEB.

Searching for the hardcoded assembly name (a specific version of Microsoft.Windows.SystemCompatible) in the WinSxS folder yields a manifest file, which looks like this:

The ActCtx “registration” is actually inside that IsolationAutomation manifest,

The file itself is only accessible to TrustedInstaller, but using FILE_FLAG_BACKUP_SEMANTICS will allow us to modify it. Sadly, I didn’t find a way to tamper with this without admin privileges.

We can now treat this just like any manifest file. This means that you can supply additional dependencies, and even inject your own COM/DLL redirections into this manifest. Modifying this file can give you opportunities to side-load libraries into pretty much every process on the machine, and to implement COM hijacking (via COM redirection) without ever touching the registry!

Implementing these hijacks is left as an exercise to the reader. :)

There’s a serious shortcoming to this method, and it’s the fact that the hardcoded assembly file changes between updates, which means you are at risk of suddenly losing your sneaky persistence after a while. This disappointingly makes the whole thing much less useful for actual long term engagements.

While the eventual conclusions from the project aren’t exactly the *most* useful thing, and various mistakes (such as ignoring the existence of SxS in the beginning, and forgetting about csrss and sxssrv.dll) made the whole process much longer than it should have been, this was still pretty fun to do. Flailing around like this taught me a whole bunch of new things about Windows, which might be very useful for future research. :)

Have you tried messing around with activation contexts? Comment on this post with your thoughts.

FIN

--

--