Defeating Device Guard: A look into CVE-2017-0007

Over the past few months, I have had the pleasure to work side-by-side with Matt Graeber (@mattifestation) and Casey Smith (@subtee) in their previous job roles, researching Device Guard user mode code integrity (UMCI) bypasses. If you aren’t familiar with Device Guard, you can read more about it here: https://technet.microsoft.com/en-us/itpro/windows/keep-secure/device-guard-deployment-guide.  In short, Device Guard UMCI prevents unapproved binaries from executing, restricts the Windows Scripting Host, and it places PowerShell in Constrained Language mode, unless the scripts themselves are signed by a trusted signer. After spending some time evaluating how scripts are handled on Device Guard enabled systems, I ended up identifying a way to get any unapproved script to execute on a Device Guard enabled system. Upon reporting the issue to MSRC, this bug was eventually assigned CVE-2017-0007 (under MS17-012) and patched. This particular bug only impacts PowerShell and the Windows Scripting Host, and not compiled code.

This bug is my first CVE and my first time reversing a patch. This post is a write-up of not only the bug, but the patch reverse-engineering process that I took as well. Since this is my first time doing this, there are bound to be errors. If I stated something incorrectly, please let me know so I can learn 🙂

When executing a signed script, wintrust.dll handles validating the signature of that file. This was determined after looking at the exports. Ideally, if you take a Microsoft signed script and modify it, the integrity of the file has been compromised, and the signature should no longer be valid. Such validation is critical and fundamental to Device Guard, where its sole purpose is to prevent unsigned or untrusted code from running. CVE-2017-0007 circumvents this protection, allowing you to run any unsigned code you want by simply modifying a script that was previously signed by an approved signer. In this case, a Microsoft signed script was chosen since code signed by Microsoft needs to be able to run on Device Guard. For example, if we try to run an unsigned PowerShell script that executes restricted actions (e.g. instantiation of most COM objects), it will fail due to PowerShell being in Constrained Language mode. Any signed and trusted PowerShell code that is approved via the deployed Code Integrity Policy is permitted to run in “FullLanguage” mode, allowing it to execute with no restrictions. In this case, our code is not signed nor trusted, so it is placed in Constrained Language mode and fails to execute correctly.

Fortunately, Microsoft has scripts that are signed with their code signing certificate. You can validate that a script is indeed signed by Microsoft using sigcheck or the PowerShell cmdlet “Get-AuthenticodeSignature”. In this case, I grabbed a signed PowerShell script from the Windows SDK and renamed it to “MicrosoftSigned.ps1”:

When scripts like these are signed, they often contain an embedded authenticode signature within the body of the script. If you were to modify any contents of that file, the integrity of said file would be broken and the signature would no longer be valid. You can also simply copy the authenticode cert from a signed file and paste it into the body of an unsigned script:

As you can see, the original contents of the script were replaced with our own code, and sigcheck reports that “The digital signature of the object did not verify”, meaning the integrity of the file has been compromised and the code will be blocked from running, right?

As you can see, our code executed anyway, despite the invalidated digital signature. Microsoft assigned this bug CVE-2017-0007, classified under MS17-012. The underlying issue here is that the error code returned by the function that ensures the file’s integrity never gets validated, resulting in successful execution of the unsigned code.

So, what is the reason for the bug, and how was it fixed? Device Guard relies on wintrust.dll to handle some of the signature and integrity checks on signed files. Due to the nature of the bug, this was the first place I looked. Bindiffing wintrust.dll pre-patch (10.0.14393.0) and post-patch (10.0.14393.953) reveals a new chunk of code was added. While there was one other change to wintrust.dll, this was the only change that pertained to validating signed scripts. Due to this, is very likely to be the patch for the bug:

Looking closer, you will see that some code from “sub_18002D0F8” was removed:

Looking at the newly added block named “sub_18002D104”, you will see that it contains some code from “sub_18002D0F8” as well as some additions. These particular functions don’t have symbols, so we must refer to them as the defined names. Alternatively, you can also rename these functions in IDA to something more meaningful.

The text above is a bit small, but I will go into depth on what exactly was done. I won’t go into specifics on using bindiff, but if you want to learn more I recommend you check out the manual. Armed with the general location of the bug fix, I set out to identify exactly what was happening when our unsigned code was executed. Knowing that some code was removed from “sub_18002D0F8” and added to a new block named “sub_18002D104” made these two places a good starting point. First, I opened up the pre-patch version of wintrust.dll (10.0.14393.0) in IDA, and navigated to the sub that was modified in the patch (sub_18002D0F8). This function starts off by setting a few variables and then calls “SoftpubAuthenticode”

Looking at “SoftpubAuthenticode” reveals that it calls another function named “CheckValidSignature”:

It makes sense that “CheckValidSignature” would handle validating the signature/integrity of the file being executed. Looking at this function, we can get the location of the last instruction before it returns.

We can see the error code from “CheckValidSignature” in the eax register by setting a windbg breakpoint at the last instruction in the function, which is highlighted in yellow above.

In this case, the error code is “0x80096010”, which translates to “TRUST_E_BAD_DIGEST”, according to wintrust.h in the Windows SDK. This is why we see “The digital signature of the object did not verify.” when running sigcheck on a modified signed file. After “CheckValidSignature” returns (via retn), we arrive back at “SoftpubAuthenticode”.

“SoftPubAuthenticode” then goes on to call “SoftpubCallUI” and then returns back to “sub_18002D0F8”, all while keeping our error code “0x80096010” in the eax register. Now that we know what the error code is and where it is stored, we can take a close look at why our script was actually allowed to run, despite “CheckValidSignature” returning “TRUST_E_BAD_DIGEST”. At this point, we are resuming execution in sub_18002D0F8, immediately after the call to “SoftpubAuthenticode”.

Since our error code is stored in eax, it gets overwritten immediately after returning from SoftpubAuthenticode via “move rax, [r12]”.

Since the error code stating that our script’s digital signature isn’t valid doesn’t exist anymore, it never gets validated and the script is allowed to execute:

With an understanding of exactly what the bug is, we can go look at how Microsoft patched it. In order to do so, we need to install KB4013429. Looking at the new version of wintrust.dll (10.0.14393.953), we can explore “sub_18002D104”, which is the added block of code that was identified towards the beginning of the blog post. We know that the bug stemmed from the register holding our error code was being overwritten and not validated. We can see that the patch added a new call to “sub_18002D4BC” following the return from “SoftPubAuthenticode”.

You may also notice in the picture above that our error code gets placed in the ecx register, and the instructions to overwrite the rcx register is now dependent on a test instruction, followed by a “jump if zero” instruction. This means that our error code, now stored in “ecx” will only get overwritten if the jump isn’t followed. Looking at the newly introduced sub “sub_18002D4BC”, you will see this:

This function returns a BOOL (0 or 1), depending on the result of operations performed on our error code. This addition checks to see if the call to “SoftpubAuthenticode” succeeded (< 0x7FFFFFFF) or if the return code matches “0x800B0109”, which translates to “CERT_E_UNTRUSTEDROOT”. In this case, SoftpubAuthenticode returned 0x80096010 (TRUST_E_BAD_DIGEST) which does not match either of the described conditions, resulting in the function returning 1 (TRUE).

After setting “al” to “1” and returning back to the previous function, we can see how this bug was actually patched:

With “al” set to “1”, the function does another logical compare to see if “al” is zero or not. Since it isn’t, it sets the “r14b” register to “0” (since the ZF flag isn’t set from the previous “test” instruction). It then does a last logical compare to check if “r14b” is zero. Since it is, it follows the jump and skips over the portion of code that overwrites the “rcx” register (leaving ecx populated with our error code). The error code eventually gets validated and the script is placed in Constrained Language mode, causing failed execution.

Cheers,
Matt

“Fileless” UAC Bypass using sdclt.exe

Recently, I published a post on using App Paths with sdclt.exe to bypass UAC. You may remember that the App Path bypass required a file on disk. Since sdclt.exe is out there, I figured I would publish another bypass using that binary, only this one is fileless. I mentioned it in my previous post, but the Vault7 leak confirms that bypassing UAC is operationally interesting, even to nation states, as several UAC bypasses/notes were detailed in the dump. As far as public bypasses go, definitely check out the UACME project by @hfiref0x, which has a nice collection of public techniques.

In newer versions of Windows, Microsoft has shown that they are taking the bypasses seriously. This has motivated me to spend a little more time on UAC and the different methods around it.

As some of you may know, there are some Microsoft signed binaries that auto-elevate due to their manifest. You can read more about these binaries and their manifests here. While searching for more of these auto-elevating binaries by using the SysInternals tool “sigcheck“, I came across “sdclt.exe” and verified that it auto-elevates due to its manifest:

*Note: This only works on Windows 10. The manifest for sdclt.exe in Windows 7 has the requestedExecutionLevel set to “AsInvoker”, preventing auto-elevation when started from medium integrity.

As I mentioned in my last post, a common technique used to investigate loading behavior on Windows is to use SysInternals Process Monitor to analyze how a process behaves when executed. I often work some basic binary analysis into my investigative process in order to see what other opportunities exist.

One of the first things I tend to do when analyzing an auto-elevate binary is to look for any potential command line arguments. I use IDA for this, but you can use your preferred tool. When peering into sdclt.exe, I noticed a few arguments that stood out due to interesting keywords:

These were interesting as sdclt.exe is set to auto-elevate in its manifest anyway. Looking at sdclt.exe in IDA, it checks if the argument matches “/kickoffelev”. If it does, it sets the full path for “sdclt.exe”, adds “/KickOffJob” as a parameter and then calls SxShellExecuteWithElevate.

Following that path, SxShellExecuteWithElevate starts “%systemroot%\system32\sdclt.exe /kickoffjob” with the “Runas” verb. This is essentially programmatically executing the “RunAsAdministrator” option when you right-click a binary.

The next step is to run “sdclt.exe /Kickoffelev” with procmon running. After going through the output, we see the trusty “shell\<verb>\command” registry search path in the HKEY_CURRENT_USER hive.

The next step was to add those keys and see if our binary and parameters of choice would execute. Unfortunately, nothing executed after adding the keys and starting “sdclt.exe /kickoffelev”. Looking back in procmon, our keys are queried, but sdclt.exe is actually looking for an additional value within the “command” key: “IsolatedCommand”.

We can then add our payload and parameters in a string (REG_SZ) value within the “Command” key called “IsolatedCommand”:

This is the same bug (minus the IsolatedCommand portion) that was used in the eventvwr.exe “fileless” UAC bypass. You can read about the eventvwr.exe bypass and the specific registry keys used here. Notice that instead of “shell\open\command”, we now see “shell\runas\command”. This is because sdclt.exe was invoked (again) using the “RunAs” verb via SxShellExecuteWithElevate.

After adding our payload as the “IsolatedCommand” value, running “sdclt.exe /KickOffElev” will execute our payload (and any parameters) in a high-integrity context:

To demonstrate this technique, you can find a script here: https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Invoke-SDCLTBypass.ps1

The script takes a full path to your payload and any parameters. “C:\Windows\System32\cmd.exe /c notepad.exe” is a good one to validate. It will automatically add the keys, start “sdclt.exe /kickoffelev” and then cleanup.

This particular technique can be remediated or fixed by setting the UAC level to “Always Notify” or by removing the current user from the Local Administrators group. Further, if you would like to monitor for this attack, you could utilize methods/signatures to look for and alert on new registry entries in HKCU:\Software\Classes\exefile\shell\runas\command\isolatedCommand

Cheers,
Matt

Bypassing UAC using App Paths

Over the past several months, I’ve taken an interest in Microsoft’s User Account Control (UAC) feature in Windows. While Microsoft doesn’t define UAC as a security boundary, bypassing this protection is still something attackers frequently need to do. The recent Vault7 leak confirms that bypassing UAC is operationally interesting, even to nation states, as several UAC bypasses/notes were detailed in the dump. On the purposefully public side, check out the UACME project by @hfiref0x for a great collection of existing techniques.

Microsoft seems to have a renewed interest in UAC, finally fixing many of the issues highlighted by publicly disclosed bypasses. While these fixes are only in newer versions of Windows, the active response from Microsoft drives me to continue searching for UAC bypasses. I’ve previously blogged about two different bypass techniques, and this post will highlight an alternative method that also doesn’t rely on the IFileOperation/DLL hijacking approach. This technique works on Windows 10 build 15031, where the vast majority of public bypasses have been patched.

As some of you may know, there are some Microsoft signed binaries that auto-elevate due to their manifest. You can read more about these binaries and their manifests here. While searching for more of these auto-elevating binaries by using the SysInternals tool “sigcheck“, I came across “sdclt.exe” and verified that it auto-elevates due to its manifest:

*Note: This only works on Windows 10. The manifest for sdclt.exe in Windows 7 has the requestedExecutionLevel set to “AsInvoker”, preventing auto-elevation when started from medium integrity.

When observing the execution flow of sdclt.exe, it becomes apparent that this binary starts control.exe in order to open up a Control Panel item in high-integrity context:

I became curious how sdclt.exe obtains the path to control.exe. Looking again at the execution flow, sdclt.exe queries the App Path key for control.exe within the HKEY_CURRENT_USER hive.

Calls to HKEY_CURRENT_USER (or HKCU) from a high integrity process are particularly interesting. This often means that an elevated process is interacting with a registry location that a medium integrity process can tamper with. In this case, I saw that “sdclt.exe” was querying HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\control.exe. If you aren’t familiar with App Paths in Windows, you can read more about the topic here.

As it stands, sdclt.exe looks for the App Path of control.exe in the HKCU hive. Essentially, this binary is asking “what is the full path of control.exe?”. If that key isn’t found, it continues the typical Windows search order. Since it is searching in a place that can be modified, we can populate the key.

Guess what happens once sdclt.exe is started again? You guessed it. Sdclt.exe queried our newly created App Paths key for control.exe, resulting in cmd.exe getting returned.

Looking at Process Explorer (or whoami /groups), I was able to confirm that cmd.exe is indeed high integrity:

It is important to note that this technique does not allow for parameters, meaning it requires your payload to be placed on disk someplace. If you try to give the binary any parameters (e.g, C:\Windows\System32\cmd.exe /c calc.exe), it will interpret the entire string as the lpFile value to the ShellExecuteInfo structure, which is then passed over to ShellExecuteEx. Since that value doesn’t exist, it will not execute.

To demonstrate this technique, you can find a script here: https://raw.githubusercontent.com/enigma0x3/Misc-PowerShell-Stuff/master/Invoke-AppPathBypass.ps1

The script takes a full path to your payload. C:\Windows\System32\cmd.exe is a good one to validate. It will automatically add the keys, start sdclt.exe and then cleanup.

This particular technique can be remediated or fixed by setting the UAC level to “Always Notify” or by removing the current user from the Local Administrators group. Further, if you would like to monitor for this attack, you could utilize methods/signatures to look for and alert on new registry entries in HKCU\Microsoft\Windows\CurrentVersion\App Paths\Control.exe.

Cheers,
Matt

Lateral Movement via DCOM: Round 2

Most of you are probably aware that there are only so many ways to pivot, or conduct lateral movement to a Windows system. Some of those techniques include psexec, WMI, at, Scheduled Tasks, and WinRM (if enabled). Since there are only a handful of techniques, more mature defenders are likely able to prepare for and detect attackers using them. Due to this, I set out to find an alternate way of pivoting to a remote system.

This resulted in identifying the MMC20.Application COM object and its “ExecuteShellCommand” method, which you can read more about here. Thanks to the help of James Forshaw (@tiraniddo), we determined that the MMC20.Application object lacked explicit “LaunchPermissions”, resulting in the default permission set allowing Administrators access:

empty_launch_permissions

You can read more on that thread here. This got me thinking about other objects that have no explicit LaunchPermission set. Viewing these permissions can be achieved using @tiraniddo’s OleView .NET, which has excellent Python filters (among other things). In this instance, we can filter down to all objects that have no explicit Launch Permission. When doing so, two objects stood out to me: “ShellBrowserWindow” and “ShellWindows”:

interesting_objects

Another way to identify potential target objects is to look for the value “LaunchPermission” missing from keys in HKCR:\AppID\{guid}. An object with Launch Permissions set will look like below, with data representing the ACL for the object in Binary format:

launch_permissions_registry

Those with no explicit LaunchPermission set will be missing that specific registry entry.

The first object I explored was the “ShellWindows” instance. Since there is no ProgID associated with this object, we can use the Type.GetTypeFromCLSID .NET method paired with the Activator.CreateInstance method to instantiate the object via its AppID on a remote host. In order to do this, we need to get the AppID CLSID for the ShellWindows object, which can be accomplished using OleView .NET as well:

[Edit] Thanks to @tiraniddo for pointing it out, the instantiation portions should have read “CLSID” instead of “AppID”. This has been corrected below.

[Edit] Replaced screenshot of AppID wit CLSID

shellwindow_classid

As you can see below, the “Launch Permission” field is blank, meaning no explicit permissions are set.

screen-shot-2017-01-23-at-4-12-24-pm

 Now that we have the AppID CLSID, we can instantiate the object on a remote target:

remote_instantiation_shellwindows

With the object instantiated on the remote host, we can interface with it and invoke any methods we want. The returned handle to the object reveals several methods and properties, none of which we can interact with. In order to achieve actual interaction with the remote host, we need to access the WindowsShell.Item method, which will give us back an object that represents the Windows shell window:

item_instantiation

With a full handle on the Shell Window, we can now access all of the expected methods/properties that are exposed. After going through these methods, “Document.Application.ShellExecute” stood out. Be sure to follow the parameter requirements for the method, which are documented here.

shellwindows_command_execution

As you can see above, our command was executed on a remote host successfully.

Now that the “ShellWindows” object was tested and validated, I moved onto the “ShellBrowserWindow” object. One of the first things I noticed was that this particular object does not exist on Windows 7, making its use for lateral movement a bit more limited than the “ShellWindows” object, which I tested on Win7-Win10 successfully. Since the “ShellBrowserWindow” object was tested successfully on Windows 10-Server 2012R2, it should be noted as well.

I took the same enumeration steps on the “ShellBrowserWindow” object as I did with the “ShellWindows” object. Based on my enumeration of this object, it appears to effectively provide an interface into the Explorer window just as the previous object does. To instantiate this object, we need to get its AppID CLSID. Similar to above, we can use OleView .NET:

[Edit] Replaced screenshot of AppID wit CLSID

shellbrowser_classid

Again, take note of the blank Launch Permission field 🙂

screen-shot-2017-01-23-at-4-13-52-pm

With the AppID CLSID, we can repeat the steps taken on the previous object to instantiate the object and call the same method:

shellbrowserwindow_command_execution

As you can see, the command successfully executed on the remote target.

Since this object interfaces directly with the Windows shell, we don’t need to invoke the “ShellWindows.Item” method, as on the previous object.

While these two DCOM objects can be used to run shell commands on a remote host, there are plenty of other interesting methods that can be used to enumerate or tamper with a remote target. A few of these methods include:

  • Document.Application.ServiceStart()
  • Document.Application.ServiceStop()
  • Document.Application.IsServiceRunning()
  • Document.Application.ShutDownWindows()
  • Document.Application.GetSystemInformation()

Defenses

You may ask, what can I do to mitigate or detect these techniques? One option is to enable the Domain Firewall, as this prevents DCOM instantiation by default. While this mitigation works, there are methods for an attacker to tamper with the Windows firewall remotely (one being remotely stopping the service).

There is also the option of changing the default “LaunchPermissions” for all DCOM objects via dcomncfg.exe by right clicking on “My Computer”, selecting “Properties” and selecting “Edit Default” under “Launch and Activation Permissions”. You can then select the Administrators group and uncheck “Remote Launch” and “Remote Activation”:

dcom_default_lockdown

Attempted instantiation of an object results in “Access Denied”:

remote_instantiation_failure

You can also explicitly set the permissions on the suspect DCOM objects to remove RemoteActivate and RemoteLaunch permissions from the Local Administrators group. To do so, you will need to take ownership of the DCOM’s HKCR AppID key, change the permissions via the Component Services MMC snap-in and then change the ownership of the DCOM’s HKCR AppID key back to TrustedInstaller. For example, this is the process of locking down the “ShellWindows” object.

First, take ownership of HKCR:\AppID\{9BA05972-F6A8-11CF-A442-00A0C90A8F39}. The GUID will be the AppID of the DCOM object; finding this was discussed above. You can achieve this by going into regedit, right click on the key and select “permissions”. From there, you will find the “ownership” tab under “advanced”.

dcom_registry_ownership

As you can see above, the current owner is “TrustedInstaller”, meaning you can’t currently modify the contents of the key. To take ownership, click “Other Users or Groups” and add “Administrators” if it isn’t already there and click “Apply”:

apply_ownership

Now that you have ownership of the “ShellWindows” AppID key, you will need to make sure the Administrators group has “FullControl” over the AppID key of the DCOM object. Once done, open the “Component Services” MMC snap-in, browse to “ShellWindows”, right click on it and select “Properties”. To modify the Remote Activation and Launch permissions, you will need to go over to the “Security” tab. If you successfully took ownership of AppID key belonging to the DCOM object, the radio buttons for the security options should *not* be grayed out.

edit_default_dcom_perms

To modify the Launch and Activation permissions, click the “edit” button under the “Launch and Activation Permissions” section. Once done, select the Administrators group and uncheck “Remove Activation” and “Remote Launch”. Click “Ok” and then “Apply” to apply the changes.

remove_perms_dcom

Now that the Remote Activation and Launch permissions have been removed from the Administrators group, you will need to give ownership of the AppID key belonging to the DCOM object back to the TrustedInstaller account. To do so, go back to the HKCR:\AppID\{9BA05972-F6A8-11CF-A442-00A0C90A8F39} registry key and navigate back to the “Other Users and Groups” section under the owner tab. To add the TrustedInstaller account back, you will need to change the “Location” to the local host and enter “NT   SERVICE\TrustedInstaller” as the object name:

trustedinstaller_restore

Click “OK” and then “Apply” to change the owner back.

One important note: Since we added “Administrators” the “FullControl” permission to the AppID key belonging to the DCOM object, it is critical to remove that permission by unchecking the “FullControl” box for the Administrators group.  Since the updated DCOM permissions are stored as “LaunchPermission” under that key, an attacker can simply delete that value remotely, opening the DCOM object back up if not properly secured.

After making these changes, you should see that instantiation of that specific DCOM object is no longer allowed remotely:

remote_instantiation_failure

Keep in mind that while this mitigation does restrict the launch permissions of the given DCOM object, an attacker could theoretically remotely take ownership of the key and disable the mitigation since it is stored in the registry.

There is the option of disabling DCOM, which you can read about here. I have not tested to see if this breaks anything at scale, so proceed with caution.

As a reference, the three DCOM objects I have found that allows for remote code execution are as follows:

MMC20.Application (Tested Windows 7, Windows 10, Server 2012R2)
AppID: 7e0423cd-1119-0928-900c-e6d4a52a0715

ShellWindows  (Tested Windows 7, Windows 10, Server 2012R2)
AppID: 9BA05972-F6A8-11CF-A442-00A0C90A8F39

ShellBrowserWindow (Tested Windows 10, Server 2012R2)
AppID: C08AFD90-F2A1-11D1-8455-00A0C91F3880

It should also be noted that there may be other DCOM objects allowing for similar actions performed remotely. These are simply the ones I have found so far.

Full Disclosure: I encourage anyone who implements these mitigations to test them extensively before integrating at scale. As with any system configuration change, it is highly encouraged to extensively test it to ensure nothing breaks. I have not tested these mitigations at scale.

As for detection, there are a few things you can look for from a network level. When running the execution of this technique through Wireshark, you will likely see an influx of DCERPC traffic, followed by some indicators.

First, when the object is instantiated remotely, you may notice a “RemoteGetClassObject” request via ISystemActivator:

isystemactivator_getclassobject

Following that, you will likely see “GetTypeInfo” requests from IDispatch along with “RemQueryInterface” requests via IRemUnknown2:

idispatch_gettypeinfo

While this may, in most cases, look like normal DCOM/RPC traffic (to an extent), one large indicator of this technique being executed will be in a request via IDispatch of GetIDsofNames for “ShellExecute”:

method_enumeration

Immediately following that request, you will see a treasure trove of useful information via an “Invoke Request”, including *exactly* what was executed via the ShellExecute method:

method_params_over_wire

That will immediately be followed by the response code of the method execution (0 being success). This is what the actual execution of commands via this method looks like:

shellexecute_returncode

Cheers!
Matt N.

Lateral Movement using the MMC20.Application COM Object

For those of you who conduct pentests or red team assessments, you are probably aware that there are only so many ways to pivot, or conduct lateral movement to a Windows system. Some of those techniques include psexec, WMI, at, Scheduled Tasks, and WinRM (if enabled). Since there are only a handful of techniques, more mature defenders are likely able to prepare for and detect attackers using them. Due to this, I set out to find an alternate way of pivoting to a remote system.

Recently, I have been digging into COM (Component Object Model) internals. My interest in researching new lateral movement techniques led me to DCOM (Distributed Component Object Model), due to the ability to interact with the objects over the network. Microsoft has some good documentation on DCOM here and on COM here. You can find a solid list of DCOM applications using PowerShell, by running “Get-CimInstance Win32_DCOMApplication”.

While enumerating the different DCOM applications, I came across the MMC Application Class (MMC20.Application). This COM object allows you to script components of MMC snap-in operations. While enumerating the different methods and properties within this COM object, I noticed that there is a method named “ExecuteShellCommand” under Document.ActiveView.

executeshellcommad_method

You can read more on that method here. So far, we have a DCOM application that we can access over the network and can execute commands. The final piece is to leverage this DCOM application and the ExecuteShellCommand method to obtain code execution on a remote host.

Fortunately, as an admin, you can remotely interact with DCOM with PowerShell by using “[activator]::CreateInstance([type]::GetTypeFromProgID”. All you need to do is provide it a DCOM ProgID and an IP address. It will then provide you back an instance of that COM object remotely:remote_instantiation

It is then possible to invoke the “ExecuteShellCommand” method to start a process on the remote host:

start_remote_process

As you can see, calc.exe is running under Matt while the user “Jason” is logged in:

process_validation

By using this DCOM application and the associated method, it is possible to pivot to a remote host without using psexec, WMI, or other well-known techniques.

To further demonstrate this, we can use this technique to execute an agent, such as Cobalt Strike’s Beacon, on a remote host. Since this is a lateral movement technique, it requires administrative privileges on the remote host:

validate_admin_beacon

As you can see, the user “Matt” has local admin rights on “192.168.99.132”. You can then use the ExecuteShellCommand method of MMC20.Application to execute staging code on the remote host. For this example, a simple encoded PowerShell download cradle is specified. Be sure to pay attention to the requirements of “ExecuteShellCommand” as the program and its parameters are separated:

command1

The result of executing this through an agent results in obtaining access to the remote target:

execution

To detect/mitigate this, defenders can disable DCOM, block RPC traffic between workstations, and look for a child process spawning off of “mmc.exe”.

Edit: After some investigating and back & forth with James Forshaw, it appears that the Windows Firewall will block this technique by default. As an additional mitigation, ensure the windows firewall is enabled and “Microsoft Management Console” isn’t an enabled rule.

Cheers!
Matt N.

Bypassing Application Whitelisting By Using rcsi.exe

Over the past few weeks, I have had the pleasure to work side-by-side with Matt Graeber (@mattifestation) and Casey Smith (@subtee) researching Device Guard user mode code integrity (UMCI) bypasses. If you aren’t familiar with Device Guard, you can read more about it here: https://technet.microsoft.com/en-us/itpro/windows/keep-secure/device-guard-deployment-guide.  In short, Device Guard UMCI prevents unsigned binaries from executing, restricts the Windows Scripting Host, and it places PowerShell in Constrained Language mode.

After discovering an Application Whitelist bypass using dnx.exe, I decided to take a look at what other potential tools existed that allowed for execution of arbitrary C#, which led me to this blog post by Microsoft: https://blogs.msdn.microsoft.com/visualstudio/2011/10/19/introducing-the-microsoft-roslyn-ctp/

I then started looking for a Microsoft compiled version, which led me to the “Microsoft Roslyn CTP” download page. Unfortunately, this requires Visual Studio 2012 and the VS2012 SDK installed, but those are freely available.

It is stated in that blog that the Microsoft Roslyn CTP contains a new binary called “rcsi.exe”. This particular binary is one of the first attempts at integrating Roslyn on the backend (it is now nicely integrated into Visual Studio 15 Update 1, which interfaces with csi.exe). More information on Roslyn can be found here: https://github.com/dotnet/roslyn

Recently, my co-worker Casey Smith (@subtee) blogged about bypassing application whitelisting using csi.exe. The difference between these bypasses is csi.exe is interactive and rcsi.exe is not. Luckily for us, you can utilize the introduction of C# Scripting with rcsi.exe to execute unsigned code.

In a Device Guard scenario, rcsi.exe is allowed to execute as it is a Microsoft signed binary that can be installed along side of Visual Studio 2012. In order to execute rcsi.exe on a Device Guard system (assuming it isn’t already installed), you will need to gather rcsi.exe and its required dependencies (there are only 2 of them), and transport everything to your target (this is an exercise left up to the reader).

With everything required now on our target host, we can now start down the path of bypassing Device Guard’s UMCI. Since rcsi.exe allows for executing C# scripts, we can use it to execute arbitrary, unsigned C# code by passing the binary our own csx script.

For example, we can create a csx file and add whatever C# code we want. Keep in mind that it uses Roslyn, so you don’t need to add classes. To demonstrate the execution of unsigned code, we can keep things simple:

rcsi_code

Now that we have our C# script created,  we can execute our C# using rcsi.exe by simply passing our csx to it. This is done on a PC running Device Guard:

rcsi_execution

As you can see above, our unsigned C# successfully executed and is running inside of rcsi.exe.

Fortunately, these “misplaced trust” bypasses can be mitigated via code integrity policy FilePublisher file rules. You can read up on creating these mitigation rules here:

http://www.exploit-monday.com/2016/09/using-device-guard-to-mitigate-against.html

You can find a comprehensive bypass mitigation policy here:

https://github.com/mattifestation/DeviceGuardBypassMitigationRules

If you want to know more about “Misplaced Trust” bypasses, you can find @subtee’s awesome Bluehat presentation slides here: https://github.com/subTee/BlueHat2016

Cheers!
Matt Nelson

Bypassing Application Whitelisting By Using dnx.exe

Over the past few weeks, I have had the pleasure to work side-by-side with Matt Graeber (@mattifestation) and Casey Smith (@subtee) researching Device Guard user mode code integrity (UMCI) bypasses. If you aren’t familiar with Device Guard, you can read more about it here: https://technet.microsoft.com/en-us/itpro/windows/keep-secure/device-guard-deployment-guide.  

In short, Device Guard UMCI prevents unsigned binaries from executing, restricts the Windows Scripting Host, and it places PowerShell in Constrained Language mode.

Recently, @mattifestation blogged about a typical Device Guard scenario and using the Microsoft Signed debuggers WinDbg/CDB as shellcode runners.

Soon after, @subtee released a post on using CSI.exe to run unsigned C# code on a Device Guard system.

Taking their lead, I decided to install the Visual Studio Enterprise trial and poke around to see what binaries existed. After much digging, I stumbled across dnx.exe, which is the Microsoft .NET Execution environment. If you are curious, you can read more on dnx.exe here:

https://blogs.msdn.microsoft.com/sujitdmello/2015/04/23/step-by-step-installation-instructions-for-getting-dnx-on-your-windows-machine/

In a Device Guard scenario, dnx.exe is allowed to execute as it is a Microsoft signed binary packaged with Visual Studio Enterprise. In order to execute dnx.exe on a Device Guard system (assuming it isn’t already installed), you will need to gather dnx.exe and its required dependencies, and somehow transport everything to your target (this is an exercise left up to the reader).

With everything required now on our target host, we can now start down the path of bypassing Device Guard’s UMCI. Since dnx.exe allows for executing code in dynamic scenarios, we can use it to execute arbitrary, unsigned C# code. Fortunately, there is a solid example of this on Microsoft’s blog above.

For example, we can create a C# file called “Program.cs” and add whatever C# code we want. To demonstrate the execution of unsigned code, we can keep things simple:

csharp_code

To satisfy the requirements of dnx.exe, a Project.json file is required, which specifies some of the requirements when executing the code. For this PoC, the example “Project.json” file can be used from Microsoft’s blog here. As stated in their post, we can execute our C# by placing “Program.cs” and “Project.json” in a folder called “ConsoleApp” (this can obviously be renamed/modified).

Now that we have our files, we can execute our C# using dnx.exe by going into the “ConsoleApp” folder and invoking dnx.exe on it. This is done on a PC running Device Guard:

dnx_bypass

As you can see above, our unsigned C# successfully executed and is running inside of dnx.exe.

Fortunately, these “misplaced trust” bypasses can be mitigated via code integrity policy FilePublisher file rules. You can read up on creating these mitigation rules here:

http://www.exploit-monday.com/2016/09/using-device-guard-to-mitigate-against.html

You can find a comprehensive bypass mitigation policy here:

https://github.com/mattifestation/DeviceGuardBypassMitigationRules

Cheers!
Matt Nelson

“Fileless” UAC Bypass Using eventvwr.exe and Registry Hijacking

After digging into Windows 10 and discovering a rather interesting method for bypassing user account control, I decided to spend a little more time investigating other potential techniques for getting around UAC. Currently, there are a couple of public UAC bypass techniques, most of which require a privileged file copy using the IFileOperation COM object or WUSA extraction (Windows 7) to take advantage of a DLL hijack in a protected system location. All of these techniques require dropping a file to disk (for example, placing a DLL on disk to perform a DLL hijack). You can take a look at some of these public techniques here (by @hfiref0x). The technique covered in this post differs from the other public methods and provides a useful new technique that does not rely on a privileged file copy, code injection, or placing a traditional file on disk (such as a DLL). This technique has been tested on Windows 7 and Windows 10, but is expected to work on all versions of Windows that implement UAC.

As I mentioned in my last post on bypassing UAC using Disk Cleanup, a common technique used to investigate loading behavior on Windows is to use SysInternals Process Monitor to analyze how a process behaves when executed. While digging into the Windows Event Log with ProcMon opened, I noticed that eventvwr.exe was executing some registry queries against the HKEY_CURRENT_USER hive as a high integrity process.

Before diving in too far, it is important to understand what the HKEY_CLASSES_ROOT (HKCR) and HKEY_CURRENT_USER (HKCU) registry hives are and how they interact. The HKCR hive is simply a combination of HKLM:\Software\Classes and HKCU:\Software\Classes. You can read more about HKCR and why the HKLM and HKCU hives are merged here. Since these hives are merged, you can often hijack keys in HKCR:\ by creating them in HKCU:\Software\Classes. Since this relationship exists between these 2 hives, any elevated process that interacts with both HKCU and HKCR in succession are particularly interesting since you are able to tamper with values in HKCU. As a normal user, you have write access to keys in HKCU; if an elevated process interacts with keys you are able to manipulate, you can potentially interfere with actions a high-integrity process is attempting to perform.

 

Now, as some of you may know, there are some Microsoft signed binaries that auto-elevate due to their manifest. You can read more about these binaries and the manifests here. By using the SysInternals tool “sigcheck”, I verified that “eventvwr.exe” auto-elevates due to its manifest:

eventvwr_manifest

While digging deeper into the ProcMon output, I noticed that “eventvwr.exe” was interacting with HKCU\Software\Classes\mscfile\shell\open\command, which resulted in a “NAME NOT FOUND” result. Shortly after, eventvwr.exe was seen interacting with the key HKCR\mscfile\shell\open\command. Looking at HKCR\mscfile\shell\open\command, I noticed that the default value was set to call mmc.exe (Microsoft Management Console), the program responsible for loading Management Snap-Ins:

hkcr_mscfile_query

As mentioned above, calls to HKEY_CURRENT_USER (or HKCU) from a high integrity process are particularly interesting. This often means that an elevated process is somehow interacting with a registry location that a medium integrity process can tamper with. In this case, it was observed that “eventvwr.exe” was querying HKCU\Software\Classes\mscfile\shell\open\command before HKCR\mscfile\shell\open\command. Since the HKCU value returned with “NAME NOT FOUND”, the elevated process queried the HKCR location:

registry_queries

From the output, it appears that “eventvwr.exe”, as a high integrity process, queries both HKCU and HKCR registry hives to start mmc.exe. After mmc.exe starts, it opens eventvwr.msc, which is a Microsoft Saved Console file, causing the Event Viewer to be displayed. This makes sense due to the fact that the Microsoft Management Console (mmc.exe) loads Microsoft Saved Console files (.msc). You can read more about the Microsoft Management Console and the corresponding Microsoft Saved Console files here.
With this information, I decided to create the registry structure needed for “eventvwr.exe” to successfully query the HKCU location instead of the HKCR location. Since the (Default) value located in HKCR\mscfile\shell\open\command contained an executable, I decided to simply replace the executable with powershell.exe:

mscfile_key_hijack

When starting “eventvwr.exe”, I noticed that is successfully queried/opened HKCU\Software\Classes\mscfile\shell\open\command:

hijack_query

This action effectively replaced the expected “mmc.exe” value with our new value: “powershell.exe”. As the process continued, I observed that it ended up starting “powershell.exe” instead of “mmc.exe”:

powershell_load

Looking at Process Explorer, I was able to confirm that powershell.exe was indeed running as high integrity:

powershell_procexp

Due to the fact that I was able to hijack the process being started, it is possible to simply execute whatever malicious PowerShell script/command you wish. This means that code execution has been achieved in a high integrity process (bypassing UAC) without dropping a DLL or other file down to the file system. This significantly reduces the risk to the attacker because they aren’t placing a traditional file on the file system that can be caught by AV/HIPS or forensically identified later.

To demonstrate this attack, Matt Graeber (@mattifestation) and I constructed a PowerShell script that, when executed on a system, will create the required registry entry in the current user’s hive (HKCU\Software\Classes\mscfile\shell\open\command), set the default value to whatever you pass via the -Command parameter, run “eventvwr.exe” and then cleanup the registry entry.

You can find the script here: https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Invoke-EventVwrBypass.ps1

Within the script, we have provided an example command. This particular command uses PowerShell to write out “Is Elevated: True” to C:\UACBypassTest. This will demonstrate that the command has executed has a high integrity process due to the fact that “Is Elevated” equated to “True” and the text file it outputs is being written to a directory that a medium integrity process is not allowed to write to.

This technique differs from the other public techniques by having a few handy benefits:

  1. This technique does not require dropping a traditional file to the file system. Most (if not all) public UAC bypasses currently require dropping a file (typically a DLL) to the file system. Doing so increases the risk of the attacker getting caught. Since this technique doesn’t drop a traditional file, that extra risk to the attacker is mitigated.
  2. This technique does not require any process injection, meaning the attack won’t get flagged by security solutions that monitor for this type of behavior.
  3. There is no privileged file copy required. Most UAC bypasses require some sort of privileged file copy in order to get a malicious DLL into a secure location to setup a DLL hijack. Since it is possible to replace what executable “eventvwr.exe” starts to load the required Snap-in, it is possible to simply use an existing, trusted Microsoft binary to execute code in memory instead.

This particular technique can be remediated or fixed by setting the UAC level to “Always Notify” or by removing the current user from the Local Administrators group. Further, if you would like to monitor for this attack, you could utilize methods/signatures to look for and alert on new registry entries in HKCU\Software\Classes\.

Bypassing UAC on Windows 10 using Disk Cleanup

Matt Graeber (@mattifestation) and I recently dug into Windows 10, and discovered a rather interesting method of bypassing User Account Control (if you aren’t familiar with UAC you can read more about it here). Currently, there are a couple of public UAC bypass techniques, most of which require a privileged file copy using the IFileOperation COM object or WUSA extraction to take advantage of a DLL hijack. You can dig into some of the public bypasses here (by @hfiref0x). The technique covered in this post differs from the other methods and provides a useful alternative as it does not rely on a privileged file copy or any code injection.

A common technique used to investigate loading behavior on Windows is to use SysInternals Process Monitor to analyze how a process behaves when executed. After investigating some default Scheduled Tasks that exist on Windows 10 and their corresponding actions, we found that a scheduled task named “SilentCleanup” is configured on stock Windows 10 installations to be launchable by unprivileged users but to run with elevated/high integrity privileges. To find this, we simply went through each task and inspected the security options for “Run with Highest Privileges” to be checked with a non-elevated User Account (such as ‘Users’).

TaskElevation

Taking a closer look with procmon, we found that the actual process started by the scheduled task, cleanmgr.exe, auto-elevates due to “execute with highest privileges” being set in the task configuration.

Let’s dive in a bit more. When cleanmgr.exe executes, it creates a new folder with the name of a GUID in “C:\Users\<username>\AppData\Local\Temp”.

FolderCreation

Once cleanmgr.exe creates the temporary folder, it then copies multiple DLLs along with “dismhost.exe” into the new folder:

FileExtraction

After copying DismHost.exe and its DLLs to “C:\Users\<username>\AppData\Temp\<guid>”, cleanmgr.exe then starts “dismhost.exe” out of the newly created path as a high integrity process:

dismhostStart

Since dismhost.exe launches out of “C:\Users\<username>\AppData\Local\Temp\<guid>”, it begins to load DLLs out of the same folder in a certain order:

DllsLoaded

Because the  current medium integrity process has write access to the user’s %TEMP% directory, it is possible to hijack a DLL loaded by dismhost.exe and obtain code execution in a high integrity process. This is commonly known as a “BypassUAC” attack.
Since this particular situation is a race condition, we have to replace the target DLL before dismhost.exe loads it. We examined the entire process more closely and determined that “LogProvider.dll” is the last DLL loaded by dismhost.exe, giving us the best chance for a hijack opportunity. With this information, we can use a WMI event to monitor for the creation of “C:\Users\<username>\AppData\Local\Temp\<guid>” and then assign that WMI event an action of hijacking “LogProvider.dll” by copying our “malicious” DLL into “C:\Users\<username>\AppData\Local\Temp\<guid>” and naming it “LogProvider.dll”. Since this action happens before dismhost.exe loads it, it will load our DLL instead of the intended one.

ElevatedDllLoad

Once dismhost.exe loads the DLL, it will load as high integrity, allowing us to bypass User Access Control and obtain code execution as a high integrity process.

After additional testing, this technique does not apply to standard user accounts as cleanmgr.exe does not extract any files to %TEMP%. When executed as a standard user in low or medium integrity, the task runs as medium integrity and never elevates past that.

Matt Graeber (@mattifestation) wrote an excellent PoC PowerShell script that will register a WMI event to monitor for the creation of the GUID folder by cleanmgr.exe and once detected, it will take the specified DLL and copy it to the GUID folder with the name of “LogProvider.dll”. Once dismhost.exe goes to load “LogProvider.dll”, it will be our malicious DLL instead of the legitimate one, thus bypassing UAC and giving us code execution in High Integrity context. You can find the script here: https://gist.github.com/mattifestation/b4072a066574caccfa07fcf723952d54

To test this, you simply need the PoC script and a DLL with a standard export of dllmain. For testing, you can either create your own DLL or use a simple MessageBox one located here: https://github.com/enigma0x3/MessageBox

This technique differs from the other public techniques by having a few benefits that can be handy:

  1. This technique does not require any process injection, meaning the attack won’t get flagged by security solutions that monitor for this type of behavior.
  2. There is no privileged file copy required. Most UAC bypasses require some sort of privileged file copy in order to get a malicious DLL into a secure location to setup a DLL hijack. Since the scheduled task copies the required stuff to %TEMP%, no privileged file copy is required.
  3. This technique cleans up after itself. After the scheduled task is done (and loads our malicious DLL), the task deletes the GUID folder (and files) that it created in %TEMP%.
  4. This technique works with the UAC level being set at its highest setting (“Always Notify”) since the task is set to run with “Highest Privileges”. The majority of the public UAC bypasses rely on the IFileOperation COM object to perform a privileged file copy. IFileOperation honors the “Always Notify” UAC setting and prompts when set, causing the privileged file copy to fail:

AlwaysNotify

This was disclosed to Microsoft Security Response Center (MSRC) on 07/20/2016. As expected, they responded by noting that UAC isn’t a security boundary, so this doesn’t classify as a security vulnerability, as stated here. While not a vulnerability, it does allow an attacker an alternate method to move to high integrity that differs from previous bypasses and introduces one more location or chokepoint that must be monitored to observe attacker behavior.

This particular technique can be remediated or fixed by disabling the task or removing the requirement for running with highest privileges. Further, if you would like to monitor for this attack, you could utilize methods/signatures to look for new WMI events as it is required to monitor for new folder creation for this attack to succeed. Combining this with App/DLL whitelisting and monitoring for abnormal modules being loaded (e.g. Sysmon event ID 7) would also limit the success of such an attack. 

*Update: As always, users should follow best practices and not use an administrative account for daily computer usage.

 

Userland Persistence with Scheduled Tasks and COM Handler Hijacking

A while back I was exploring userland COM and stumbled across some 2011 research by Jon Larimer explaining the dangers of per-user COM objects. Recently Casey Smith (@subtee) started digging into COM and its implications as well, which motivated me to finish the research I had started. After some poking around, I found out that you can combine COM CLSID (ClassID) hijacking with a scheduled task to obtain userland persistence. It has been well known that you can use CLSID hijacking to persist via different functions of Windows, but one way that hasn’t been explored deeply (from what I have found) is combining this technique with Scheduled Tasks set to run upon user logon.

 

First, a bit of background on the registry and how this technique works. The HKEY_CLASSES_ROOT (HKCR) registry hive is just a combination of HKLM:\Software\Classes and HKCU:\Software\Classes. Due to the fact that these are merged, you can “hijack” keys in HKCR:\CLSID by adding keys in HKCU:\Software\Classes\CLSID. This is important because a lot of things (such as Scheduled Tasks) pull from HKCR:\CLSID for actions. Hijacking is possible due to how HKCR works- since HKCR is a merge of HKLM and HKCU, userland COM objects will load from HKCR:\CLSID, which is a merged view of HKLM:\Software\Classes\CLSID and HKCU:\Software\Classes\CLSID. Since we are able to write directly to HKCU:\Software\Classes\CLSID we can effectively hijack the keys in HKCR:\CLSID. You can read more about about HKCR and Per-User COM here and here, and more on COM registration/permissions here.

 

Looking at a few Scheduled Tasks, we can see that some of them have an action set as a “Custom Handler”:

actions_custom_handler

In order to view what the action actually is, we can simply pull the XML specification by running schtasks /query /XML /TN “\Microsoft\Windows\WindowsUpdate\Automatic App Update” :

class_id_xml_task

As you can see, there is a ComHandler associated with the Task’s action. This means that when the task runs, it reaches out to “HKCR:\CLSID\{A6BA00FE-40E8-477C-B713-C64A14F18ADB}\InprocServer32” (or whatever CLSID is supplied) and runs whatever DLL is specified.

legit_clsid_entry

By modifying HKCU:\Software\Classes\CLSID we are able to directly change the “Default” value in the corresponding key located in HKCR:\CLSID (which is what some scheduled tasks pull from). Since HKCR:\CLSID is a merged view of HKCU:\Software\Classes, we can only hijack HKCR:\CLSID keys in userland, so HKCU doesn’t apply to LocalSystem events (unless running as SYSTEM). Due to this, you can only abuse this via the user context you are running under. You are able to persist as SYSTEM via the same technique by obtaining access to write to HKLM:\Software\Classes\CLSID as LocalSystem tasks pull directly from HKLM instead of HKCR (in most instances). You can read more on this here and here.

 

By adding this key & the InprocServer32 subkey to HKCU:\Software\Classes\CLSID and adding our own DLL.

added_hkcu_entry

We can change the “Default” value of the corresponding key in HKCR:\CLSID to a different DLL.

modified_hkcr_entry

Since the selected Scheduled Task runs whatever DLL is present in the “Default” key, it will execute our DLL. By hijacking a task that is set to execute on user logon we can achieve userland persistence. You can determine which tasks are set to execute on logon by checking the “Triggers” tab:

logon_task

Once the task executes, it will load our specified malicious DLL:

dll_popup_on_execute

As you can see, the message box “pwn” popped up when the “Automatic App Update” task was executed (on logon). You can see that the DLL “C:\64.dll” was loaded by taskhostw.exe:

dll_loaded_in_procexp

By abusing the fact that some tasks have “Custom Handlers” set as actions, we can hijack the corresponding COM handler in the registry and force the task to execute our malicious DLL on logon instead of the legitimate one.

 

I have provided a script to check all tasks that execute on logon and have a COM Handler action associated. You can use this to check for any anomalies in keys associated with Scheduled tasks that execute on logon:

https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Get-ScheduledTaskComHandler.ps1

 

This script will pull every Scheduled Task that has a COM handler associated with its action and will tell you the task name, CLSID registry key, the DLL associated with that CLSID, if the task is set to execute automatically on user logon, and if the task executes in a normal user context (meaning it can be hijacked):

script_default

You can also only specify tasks that are set to execute on logon by specifying -OnLogon:

on_logon_flag

Finally, you can specify -PersistenceLocations to show only Scheduled Tasks that execute in userland and start on logon. This will give you all tasks that can be used to persist within the current user context:

persistence_locations_flag

If you wish to check this yourself, you can either download the “pwn” messageBox dll or compile it yourself and test some of your scheduled tasks!

https://github.com/enigma0x3/MessageBox

Cheers!