Activation Contexts — A Love Story

Philip Tsukerman
Philip Tsukerman
Published in
7 min readOct 18, 2019

TL;DR — Windows loads a version of the Microsoft.Windows.SystemCompatible assembly manifest into every process. Tampering with it lets you inject DLL side-loading opportunities into every process, and to perform COM hijacking without touching the registry. Unfortunately, the manifest could be replaced by another version, possibly killing your persistence by surprise.

Not all research projects are born equal, and there are many that never deliver on the promise of something useful at the end of your journey. The various rabbit-holes, confrontations with wrong assumptions, and other moments of feeling like an idiot 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 quite an interesting COM object, which provides some rather interesting capabilities for attackers, which were mostly described by Casey Smith. It can provide you, among other things, with an ability to create COM objects without going through the registry. My first questions upon messing around with it for a bit were “What else can it do?” and “Are there any other objects which could provide a similar capability?”. To answer these, I did what I always do with COM objects, which is trying to find if there are any other objects or interfaces which are implemented by the same executable. To do this I simply 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 tried to search the registry directly, which again gave me nothing. Finally, 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 lingering 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. My first assumption was that I’m going to see a rather simple function, which 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 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 may give you 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;};

The next step was to debug CLSIDFromProgID when creating the ActCtx object, and to discover that the CLSID itself is supplied by the CComSxSClassInfo::GetConfiguredClsid function, which finally made things start 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 which is supposed to provide applications with the ability to use specific versions of libraries, without fear of versioning conflicts which might arise because a different, incompatible version of a library has been 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 which 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 source of our ActCtx registration, because 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, because I have not yet thought of a reason to use this instead of just putting the same information in the registry. Running the tool gives us the following output, and validates my assumptions:

Finally, some info about ActCtx!

A part of the mystery has been solved. Processes are able to discover and create ActCtx because information about it 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 enable us to inject redirections into pretty much every process on the machine, which might 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 would be during process creation, so the most straightforward approach would be 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 isn’t 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 the next step is to look at the CreateProcessInternal function, identify interesting calls (mostly by name, at first), and 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 (as seen in the image below), 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. This means that 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, meaning that you are at the 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 whole 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 :)

FIN

--

--