AppLocker Policy Enumeration in C
Abstract
Application whitelisting and blacklisting is an interesting topic because depending on how it has been configured this can drastically increase the difficulty of an attacker to gain initial code execution. With Windows XP and Windows Server 2013, Microsoft released Software Restriction Policy (SRP), which was a great idea but a massive pain to configure with little to no flexibility. This is where AppLocker is coming into play, this is the successor of SRP. AppLocker has been introduced in Windows 10, originally only for Enterprise and Education versions.
AppLocker offers a lot of flexibility because the allow/deny rules are set to a SID and therefore can be applied to any security principal (i.e. user, groups). AppLocker has also multiple rule types, which are as follows:
- Executable rules, for executable files (e.g.
C:\Windows\System32\cmd.exe
); - Windows Installer rules, for installation files (e.g.
C:\Users\Public\myinstaller.msi
); - Script rules, for Windows Script Host (WSH) scripts (e.g.
C:\Windows\System32\winrm.vbs
); and - Packaged app rules, for new Microsoft Store installation packages (e.g.
C:\Users\Public\myapp.appx
)
Besides, having different rule types, the restriction (i.e. allow or deny) can be set based on the program's path, publisher or hash. Finally, AppLocker can be configured via Group Policies (GPO), which is incredibly helpful for maintaining and updating rules across an estate.
Most of the examples, if not all the examples online are using the Get-AppLockerPolicy
PowerShell Cmdlet but more recently @Flangvik and @Jean_Maes_1994 released a tooled named SharpAppLocker to list AppLocker policies. Despite being an interesting implementation in C#, they are using the following two Assembly Types:
AppLockerPolicy
; andPolicyManager
As a result, the following four Assemblies are loaded into the default application domain:
Microsoft.Security.ApplicationId.PolicyManagement.PolicyEngineApi.Interop
;Microsoft.Security.ApplicationId.PolicyManagement.PolicyManager
;Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel
; andMicrosoft.Security.ApplicationId.PolicyManagement.XmlHelper
In terms of operational security, there is no need to load and use theses Assemblies when they are only wrappers around a COM interface, as we will see later. I submitted a pull request to their project in order to use the COM interface instead of the aforementioned Assembly Types.
Ultimately, the reason why I'm writing this blog post is because I think it is interesting to provide an example of how it is possible to use a COM interface when the definition of the interface is not provided by Microsoft. You can find the source code of the application in my Windows System Programming Experiments (WSPE) repository on GitHub: List AppLocker Policies
Identifying the Culprit
According to the Microsoft documentation, the Get-AppLockerPolicy
is the PowerShell Cmdlets that can be used to list all the policies. Under the hood this Cmdlet is loading and using Types from a .NET Framework Assembly. To get the Assembly we can execute the following PowerShell Cmdlet:
Get-Command Get-AppLockerPolicy | Select-Object Dll
In my case this will return the path of the Assembly, in the form of a DLL, from the Global Assembly Cache (GAC): C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.Security.ApplicationId.PolicyManagement.Cmdlets\v4.0_10.0.0.0__31bf3856ad364e35\Microsoft.Security.ApplicationId.PolicyManagement.Cmdlets.dll
.
.NET applications are known to be easy to reverse engineer, at least when they are not obfuscated. The reason behind that is that .NET application are compiled into Microsoft Intermediate Language (MSIL) instead of machine code. In this case I used the dnSpy to reverse engineer the Assembly.
As shown, the PowerShell cmdlet use the AppLockerPolicy
and PolicyManager
Types. In this case we are only interested by the PolicyManager
Type and especially the GetLocalPolicy
, GetDomainPolicy
and GetEffectivePolicy
static methods. This Type is defined within the Microsoft.Security.ApplicationId.PolicyManagement.PolicyManager
Assembly.
As shown, the static methods are instantiating an interface named IAppIdPolicyHandler
via a class factory named AppIdPolicyHandlerClass
.
Without too much surprises, this is a COM interface. At this point this means that instead of using the Get-AppLockerPolicy
or the AppLockerPolicy
and PolicyManager
Types to get the AppLocker policies we can directly use the COM interface. With dnSpy we can get the Global Unique Identifier (GUID) of the interface and class factory, which are as follows:
- F1ED7D4C-F863-4DE6-A1CA-7253EFDEE1F3 - AppIdPolicyHandlerClass
- B6FEA19E-32DD-4367-B5B7-2F5DA140E87D - IAppIdPolicyHandler
At first I've tried to implement IAppIdPolicyHandler
COM interface with the data I gathered from the OleViewDotnet tool from James Forshaw. However, that tool was returning the high level C# implementation of the interface.
After further research and a chat with odzhan he pointed me to the OleWoo
tool, which was required in order to get the proper low level definition of the interface. To find the DLL in which the COM interface is define, we just need to search for the InterServer32
key in the Windows Registry. Note that in this case we are using the GUID of the class factory of the interface: {F1ED7D4C-F863-4DE6-A1CA-7253EFDEE1F3}
.
After analysing the DLL with OleWoo
we can find that the Interface inherits from IDispatch
which inherits from IUnknown
. We can also find the different methods that are exposed: SetPolicy
, GetPolicy
, GetEffectivePolicy
, IsFileAllowed
and IsPackageAllowed
.
At this point we have all we need to define the interface in C.
Defining the COM Interface
First thing to know is that we will implement a C++-style class in C, which means that we will need to implement two structures. The first structure will define all the methods of the interface, methods from the the inherited interfaces included. The second structure will define the virtual method table (i.e. vtable), which is nothing more than a pointer to the first structure with all the methods.
The first interface will be defined as follows:
typedef struct AppIdPolicyHandlerVtbl {
BEGIN_INTERFACE
/**
* @brief QueryInterface method from IUnknown
*/
HRESULT(STDMETHODCALLTYPE* QueryInterface) (
_In_ IAppIdPolicyHandler* This,
_In_ REFIID riid,
_Out_ PVOID* ppvObject
);
/**
* @brief AddRef from IUnknown
*/
ULONG(STDMETHODCALLTYPE* AddRef)(
_In_ IAppIdPolicyHandler* This
);
/**
* @brief Release from IUnknown
*/
ULONG(STDMETHODCALLTYPE* Release)(
_In_ IAppIdPolicyHandler* This
);
/**
* @brief GetTypeInfoCount from IDispatch
*/
HRESULT(STDMETHODCALLTYPE* GetTypeInfoCount)(
_In_ IAppIdPolicyHandler* This,
_Out_ PUINT pctinfo
);
/**
* @brief GetTypeInfo from IDispatch
*/
HRESULT(STDMETHODCALLTYPE* GetTypeInfo)(
_In_ IAppIdPolicyHandler* This,
_In_ UINT itinfo,
_In_ ULONG lcid,
_Out_ LPVOID* pptinfo
);
/**
* @brief GetIDsOfNames from IDispatch
*/
HRESULT(STDMETHODCALLTYPE* GetIDsOfNames)(
_In_ IAppIdPolicyHandler* This,
_In_ LPIID riid,
_In_ LPOLESTR* rgszNames,
_In_ UINT cNames,
_In_ LCID lcid,
_Out_ DISPID* rgdispid
);
/**
* @brief Invoke from IDispatch
*/
HRESULT(STDMETHODCALLTYPE* Invoke)(
_In_ IAppIdPolicyHandler* This,
_In_ DISPID dispidMember,
_In_ LPIID riid,
_In_ LCID lcid,
_In_ WORD wFlags,
_In_ DISPPARAMS* pdispparams,
_In_ LPVARIANT pvarResult,
_Out_ LPEXCEPINFO pexcepinfo,
_Out_ PUINT puArgErr
);
/**
* @brief SetPolicy from IAppIdPolicyHandler
*/
HRESULT(STDMETHODCALLTYPE* SetPolicy)(
_In_ IAppIdPolicyHandler* This,
_In_ BSTR bstrLdapPath,
_In_ BSTR bstrXmlPolicy
);
/**
* @brief GetPolicy from IAppIdPolicyHandler
*/
HRESULT(STDMETHODCALLTYPE *GetPolicy)(
_In_ IAppIdPolicyHandler* This,
_In_ BSTR bstrLdapPath,
_Out_ LPBSTR pbstrXmlPolicy
);
/**
* @brief GetEffectivePolicy from IAppIdPolicyHandler
*/
HRESULT(STDMETHODCALLTYPE *GetEffectivePolicy)(
_In_ IAppIdPolicyHandler* This,
_Out_ LPBSTR pbstrXmlPolicies
);
/**
* @brief IsFileAllowed from IAppIdPolicyHandler
*/
HRESULT(STDMETHODCALLTYPE *IsFileAllowed)(
_In_ IAppIdPolicyHandler* This,
_In_ BSTR bstrXmlPolicy,
_In_ BSTR bstrFilePath,
_In_ BSTR bstrUserSid,
_Out_ LPGUID pguidResponsibleRuleId,
_Out_ PLONG pbStatus
);
/**
* @brief IsPackageAllowed from IAppIdPolicyHandler
*/
HRESULT(STDMETHODCALLTYPE *IsPackageAllowed)(
_In_ IAppIdPolicyHandler* This,
_In_ BSTR bstrXmlPolicy,
_In_ BSTR bstrPublisherName,
_In_ BSTR bstrPackageName,
_In_ ULONG ullPackageVersion,
_In_ BSTR bstrUserSid,
_Out_ LPGUID pguidResponsibleRuleId,
_Out_ PLONG pbStatus
);
END_INTERFACE
} AppIdPolicyHandlerVtbl;
The second method will be defined as follows:
typedef struct IAppIdPolicyHandler {
CONST_VTBL struct AppIdPolicyHandlerVtbl* lpVtbl;
};
Finally, you may have noticed that in the first structure AppIdPolicyHandlerVtbl
, I added a pointer to the second structure IAppIdPolicyHandler
as first parameter. The reason is pretty simple, this is what C++ is "using" under the hood when you are using the this
keyword, which can be used to access class properties in case you have any.
Listing the AppLocker Policies
The class factory GUID and the interface GUID can be defined as follows:
/**
* @brief GUID of the IAppIdPolicyHandler COM interface: B6FEA19E-32DD-4367-B5B7-2F5DA140E87D
*/
CONST IID IID_IAppIdPolicyHandler = { 0xB6FEA19E, 0x32DD, 0x4367, {0xB5, 0xB7, 0x2F, 0x5D, 0xA1, 0x40, 0xE8, 0x7D} };
/**
* @brief GUID of the IAppIdPolicyHandler class factory: F1ED7D4C-F863-4DE6-A1CA-7253EFDEE1F3
*/
CONST IID CLSID_AppIdPolicyHandlerClass = { 0xF1ED7D4C, 0xF863, 0x4DE6, {0xA1, 0xCA, 0x72, 0x53, 0xEF, 0xDE, 0xE1, 0xF3} };
The CoInitialize
and CoCreateInstance
Windows APIs can be used to respectively initialise the COM library in the current running thread and then initialise the interface. Example with error handling omitted:
HRESULT result = CoInitialize(NULL);
IAppIdPolicyHandler* pIAppIdPolicyHandler = NULL;
result = CoCreateInstance(&CLSID_AppIdPolicyHandlerClass, NULL, CLSCTX_INPROC_SERVER, &IID_IAppIdPolicyHandler, &pIAppIdPolicyHandler);
Finally, either GetPolicy
or GetEffectivePolicy
can be invoked to get the AppLocker policies. Example with error handling omitted:
BSTR bstrPolicies = NULL;
result = pIAppIdPolicyHandler->lpVtbl->GetEffectivePolicy(pIAppIdPolicyHandler, &bstrPolicies);
printf("Policies: \n\n%S\n", bstrPolicies);
Example of the program that you can find in my GitHub repository: