Adding some Pester tests to Autoruns module

I’ve been working with Pester for a while and I thought it would be nice to have some Pester tests in the Autoruns module.

I don’t have very high expectations and thought I’d start with some tests to validate that previous issues encountered are still fixed and keep being remediated.

While writing these tests, I encountered two challenges.

  • First challenge

The first one is that the module exports a single function and there are many functions in the begin block of this exported function.
I needed to find a solution to be able to test anything in the code whatever the function.
I decided to use the Abstract Syntax Tree (a.k.a AST) to extract every function and inject them in a fake module. In other words the function tree in the original module would be flatten in this fake module.
This way I can use the InModuleScope and test anything I want.

Here’s the only exported function from the original Autoruns module:

Here are the inner functions extracted from the original module and loaded in the FakeAutoRuns module:

Here’s the code that extracts the functions and loads them in a flat (fake) module:

  • The 2nd challenge

The 2nd challenge was that I needed to mock some calls made to a .Net method.

I needed to mock the Test-Path, Get-Item cmdlets and especially the call to the method named GetSubKeyNames on the Microsoft.Win32.RegistryKey objects returned by the Get-Item cmdlet in the following example:

Here’s what I did to mock these cmdlets and their invocation of a .Net method

I feel that the learning curve is steep with Pester. If you know a better way to write Pester tests for this issue #26, please tell me, your input is more than welcome.

What to do after patching CVE-2020-0601

The FAQ section of CVE-2020-0601 includes the following:

How can I tell is someone is attempting to use a forged certificate to exploit this vulnerability?

NB: Once patched, events are generated after a reboot.

It appears you can also rely on Windows Defender to answer this question: exploit attempts are detected as Exploit:Win32/CVE-2020-0601.A and Exploit:Win32/CVE-2020-0601.B

    • Is my system patched against CVE-2020-0601 exploits?
# Updated against CVE-2020-0601 exploits?
if ($updated =Get-Item C:\Windows\System32\crypt32.dll |
Select-String '\[CVE-2020-0601\]' -Encoding unicode) {
 'Already Updated against CVE-2020-0601'
} else {
 'Protection against CVE-2020-0601 missing'

If your system is patched, you get:

If it’s not, you get:

    • Any exploit attempt detected?

There’s a provider named Microsoft-Windows-Audit-CVE that can generate 2 events:

# What kind of events we get from this provider
(Get-WinEvent -ListProvider 'Microsoft-Windows-Audit-CVE').Events

Once patched, you could know if an attempt to exploit this CVE has been made

# Anything that would require investigation
$HT = @{
 FilterHashTable = @{ProviderName ='Microsoft-Windows-Audit-CVE'}
 ErrorAction = 'SilentlyContinue'
 Verbose = [switch]::Present
if ($events = Get-WinEvent @HT) {
} else {
 'No CVE exploit detected'

# Display more properties
if ($events) {
 $events | 
 Select -Property * -Exclude MachineName,UserId

NB: The above query allows to find both events id 1 and 2 from the Application and System log.

There no known attempt to exploit it yet, according to this MSRC blog post.

This vulnerability is classed Important and we have not seen it used in active attacks.

Let’s say that there’s an exploit in the wild.
You can follow this guide to analyze the para field of Audit-CVE event ID 1 (mentioned in the FAQ of CVE-2020-0601):

# Hex string taken from the "para" field of Audit-CVE event ID 1 event in the Application log
$EventParaString = '3081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377304404207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9042026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B60441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101'
# Convert the hex string to a byte array
[Byte[]] $EventParaBytes = $EventParaString -split '([0-9A-F]{2})' | Where-Object { $_ } | ForEach-Object { [Byte] "0x$_" }
# Save the byte array to disk
[IO.File]::WriteAllBytes("$PWD\ECCCurveParams.bin", $EventParaBytes)
# Use certutil to parse the ASN.1-encoded ECC curve parameters
certutil.exe -asn "$PWD\ECCCurveParams.bin"

Credit: Matt Graeber

Other links:

Quick post: find users who have an Exchange Online mailbox

If you have a hybrid Exchange infrastructure, it appears that you can query your local Active Directory to find out users who have already been migrated to Exchange Online.

Thanks to the attribute named msExchRecipientTypeDetails (values detailed here), you can find these accounts with a remote mailbox.

Here’s a small function that leverages this msExchRecipientTypeDetails attribute:

Function Get-RemoteUserMailbox {
[string]$SearchBase = "$((ActiveDirectory\Get-ADDomain).DistinguishedName)",
[int32]$ResultSetSize = 10000,
[string]$SearchScope = 'SubTree'
Begin {}
Process {
$HT = @{
Filter = 'msExchRecipientTypeDetails -eq 2147483648'
SearchScope = $SearchScope
ResultSetSize = $ResultSetSize
SearchBase = $SearchBase
ErrorAction = 'Stop'
try {
ActiveDirectory\Get-ADUser @HT
} catch {
Write-Warning -Message "Failed to query AD because $($_.Exception.Message)"
End {}

You can even bind cmdlets like this for example:

Get-RemoteUserMailbox  |
Get-ADUser -Properties AccountExpirationDate | 

Nice, isn’t it? 😀