First release of AutoRuns module

You may remember the excellent PowerShell Security series from PowerShell Magazine where I presented a Get-PSAutoRun function to investigate malware persistence ala “Sysinternals autoruns”.

I’ve actually revised its content during the last Christmas holidays and transformed it as a module.

I’ve updated the launch points the original Sysinternals autoruns utility checks and tried to do my best to keep track of what new launch points were added or removed between versions:
AutoRunsHistory
You may have noticed that there’s a new category for Office plugins.
I’ve also added some code about the PoweLiks malware although I hadn’t had yet a sample to fully test my detection code:
Powelik

The code has also undergone a major “quality review” to reduce the number of warnings or issues reported by the PSScriptAnalyzer module.
Test-Code-with-Invoke-ScriptAnalyzer
As you can see, it still complains about using the Get-WmiObject cmdlet and the fact that I sometimes use an empty catch block to avoid returning an error.

What’s next?
I’ve published the module in the PowerShell Gallery :-D
AutoRuns-On-PSGallery
Now, it’s your turn to test the module and tell me what you think about it. You can also contribute and fork it as it’s stored in my GitHub at this address.

Quick tip about PPKG files

I’ve been playing with the Windows 10 Deployment and Management Lab Kit Microsoft provided and applying provisioning packages (PPKG) files in chapter 6.

I’ve found the following documentation about applying provisioning packages on MSDN where I’ve learned that you can work with PPKG in an Offline image using DISM and that there’s an UI that looks like this:

ppk-UI-01

Unfortunately, you cannot list installed provisioning packages from an online image :-(
PowerShell to the rescue! Please, fix this shortcoming!

dir C:\ProgramData\Microsoft\Provisioning\ -Recurse -Include  customizations.xml | 
ForEach-Object {
 $x = [xml](Get-Content $_.FullName) ;
 if ($x.WindowsCustomizations.PackageConfig.OwnerType -ne 'Microsoft') {
  $x.WindowsCustomizations.PackageConfig
 }
} | Select ID,Name,Version,OwnerType,Rank | Format-Table -AutoSize

ppk-xml-parsing

Nice, I was able to grab the Package ID, its version, its name but not the ‘Author’ if you compare the output of my PowerShell command and the ‘Details’ UI below
(I’ve absolutely no clue where it’s stored. If you know, please share this information with us :-) )
ppk-UI-02

Working with shutdown reason code

I’m probably not the only one who ever wondered how to translate the error code in event ID 1074 to something meaningful.

The following blog post sheds some lights on this topic: https://blogs.msdn.microsoft.com/oldnewthing/20100831-00/?p=12993

Far from being the brightest code I’ve ever produced, here’s a function that tries to convert the shutdown reason code to something meaningful :-D

Function Convert-ShutdownReason {
[CmdletBinding()]
Param(
    [Parameter(Mandatory,ValueFromPipeLine)]
    $InputObject
)
Begin {}
Process {
    $InputObject | ForEach-Object {
        $test = $_
        
        $Planned = $major = $reasonMajor = $minor = $reasonMinor = $null
        switch ($_) {
            { ($_ -band 0x80000000) -eq 0x80000000 } {
                $Planned = $true # system defined
                $major = $_ -bxor 0x80000000
                break
            }
            { ($_ -band 0x40000000) -eq 0x40000000 } {
                $Planned = $true # custom user defined
                $major = $_ -bxor 0x40000000
                break
            }
            default {
                $Planned = $false # flag not set
                $major = $_
            }
        }
        switch ($major) {
            { ($_ -band 0x70000) -eq 0x70000 } {
                $reasonMajor = 'LEGACY_API'
                $minor = $_ -bxor 0x70000
                break
            }
            { ($_ -band 0x60000) -eq 0x60000 } {
                $reasonMajor ='POWER'
                $minor = $_ -bxor 0x60000
                break
            }
            { ($_ -band 0x50000) -eq 0x50000 } {
                $reasonMajor ='SYSTEM'
                $minor = $_ -bxor 0x50000
                break
            }
            { ($_ -band 0x40000) -eq 0x40000 } {
                $reasonMajor ='APPLICATION'
                $minor = $_ -bxor 0x40000
                break
            }
            { ($_ -band 0x30000) -eq 0x30000 } {
                $minor = $_ -bxor 0x30000
                $reasonMajor ='SOFTWARE'
                break
            }
            { ($_ -band 0x20000) -eq 0x20000 } {
                $reasonMajor ='OPERATINGSYSTEM'
                $minor = $_ -bxor 0x20000
                break
            }
            { ($_ -band 0x10000) -eq 0x10000 } {
                $reasonMajor ='HARDWARE'
                $minor = $_ -bxor 0x10000
                break
            }
            default {
                $reasonMajor ='OTHER'
                $minor = $_ -bxor 0x0
            }
        }

        switch ($minor) {
            { ($_ -band 0xff) -eq 0xff } {
                $reasonMinor = 'NONE'
                break    
            }
            { ($_ -band 0x22) -eq 0x22 } {
                $reasonMinor = 'DC_DEMOTION'
                break    
            }
            { ($_ -band 0x21) -eq 0x21 } {
                $reasonMinor = 'DC_PROMOTION'
                break    
            }
            { ($_ -band 0x20) -eq 0x20 } {
                $reasonMinor = 'TERMSRV'
                break    
            }
            { ($_ -band 0x19) -eq 0x19} {
                $reasonMinor ='MMC'
                break
            }
            { ($_ -band 0x18) -eq 0x18 } {
                $reasonMinor ='SECURITYFIX_UNINSTALL'
                break
            }
            { ($_ -band 0x17) -eq 0x17 } {
                $reasonMinor ='HOTFIX_UNINSTALL'
                break
            }
            { ($_ -band 0x16) -eq 0x16 } {
                $reasonMinor ='SERVICEPACK_UNINSTALL'
                break
            }
            { ($_ -band 0x15) -eq 0x15 } {
                $reasonMinor ='WMI'
                break
            }
            { ($_ -band 0x14) -eq 0x14 } {
                $reasonMinor ='NETWORK_CONNECTIVITY'
                break
            }
            { ($_ -band 0x13) -eq 0x13 } {
                $reasonMinor ='SECURITY'
                break
            }
            { ($_ -band 0x12) -eq 0x12 } {
                $reasonMinor ='SECURITYFIX'
                break
            }
            { ($_ -band 0x11) -eq 0x11} {
                $reasonMinor ='HOTFIX'
                break
            }
            { ($_ -band 0x10) -eq 0x10 } {
                $reasonMinor ='SERVICEPACK'
                break
            }
            { ($_ -band 0xf) -eq 0xf } {
                $reasonMinor ='BLUESCREEN'
                break
            }
            { ($_ -band 0xe) -eq 0xe } {
                $reasonMinor ='OTHERDRIVER'
                break
            }
            { ($_ -band 0xd) -eq 0xd } {
                $reasonMinor ='HARDWARE_DRIVER'
                break
            }
            { ($_ -band 0xc) -eq 0xc } {
                $reasonMinor ='ENVIRONMENT'
                break
            }
            { ($_ -band 0xb) -eq 0xb } {
                $reasonMinor ='CORDUNPLUGGED'
                break
            }
            { ($_ -band 0xa) -eq 0xa } {
                $reasonMinor ='POWER_SUPPLY'
                break
            }
            { ($_ -band 0x9) -eq 0x9 } {
                $reasonMinor ='NETWORKCARD'
                break
            }
            { ($_ -band 0x8) -eq 0x8 } {
                $reasonMinor ='PROCESSOR'
                break
            }
            { ($_ -band 0x7) -eq 0x7 } {
                $reasonMinor ='DISK'
                break
            }
            { ($_ -band 0x6) -eq 0x6 } {
                $reasonMinor ='UNSTABLE'
                break
            }
            { ($_ -band 0x5) -eq 0x5 } {
                $reasonMinor ='HUNG'
                break
            }
            { ($_ -band 0x4) -eq 0x4 } {
                $reasonMinor ='RECONFIG'
                break
            }
            { ($_ -band 0x3) -eq 0x3 } {
                $reasonMinor ='UPGRADE'
                break
            }
            { ($_ -band 0x2) -eq 0x2 } {
                $reasonMinor ='INSTALLATION'
                break
            }
            { ($_ -band 0x1) -eq 0x1 } {
                $reasonMinor ='MAINTENANCE'
                break
            }
            { ($_ -band 0x0) -eq 0x0 } {
                $reasonMinor ='OTHER'
                break
            }
            default {
            }
        }

        [PSCustomObject]@{
            Reason = '0x{0:X}' -f $test
            Text = '{0}: {1}' -f $reasonMajor,$reasonMinor
            Planned = $Planned
        }
    }
}
End {}
}

Here a a the most frequent shutdown reason codes I’ve encountered:

0x80020010,0x80070015,0x500ff,0x0,0x80030002,0x80030003 | 
Convert-ShutdownReason

convert-shutdown-reason-01

The shutdown reason code can be extracted from the event logs and directly piped into the function like this:

(Get-WinEvent -FilterHashtable @{ LogName = 'system';ProviderName='User32' ; Id = 1074} -MaxEvents 100) | 
Select -First 2 | Foreach-Object {
 ($_.Properties[3].Value) -as [int32]
}| 
Convert-ShutdownReason

convert-shutdown-reason-02

Virtual Bitlocker Containers

A few days ago a post on the Sans.org diary caught my attention because of its title: Virtual Bitlocker Containers. Very nice idea :-D

But instead of going through the old school diskpart.exe hassle, you can actually bring more automation with PowerShell with the following oneliner:


# Define a hashtable for readability
$BLHT = @{
 EncryptionMethod  = 'XtsAes256';
 PasswordProtector = $true;
 Password = (ConvertTo-SecureString 12345678 -AsPlainText -Force);
 UsedSpaceOnly = $true;
}

# Here we go!
New-VHD -Path c:\container.vhdx -SizeBytes 128MB -Fixed | 
Mount-VHD -Passthru | 
Initialize-Disk -PassThru | 
New-Partition -UseMaximumSize -AssignDriveLetter | 
Format-Volume -FileSystem NTFS | 
Select @{l='MountPoint';e={"$($_.DriveLetter):"}} |
Enable-BitLocker @BLHT | 
Add-BitLockerKeyProtector -RecoveryPasswordProtector -WA 0

NB1: XTS-AES encryption method is introduced in Windows 10 version 1511.
NB2: WA is the alias of WarningAction and 0 means SilentlyContinue.
You can try the oneliner without it and the recovery key will be displayed in the warning stream.

So, don’t forget to

# save the recovery key displayed by this command, somewhere...
Get-BitLockerVolume d: | Select -Expand KeyProtector

Bitlocker-Container-OneLiner

Bonus: about XTS-AES
Bitlocker-New-XTS-AES

Decipher Windows Update error codes

On PM.org, people sometimes ask questions and only provide the error code in various forms.
Wu-error-question
This time, it was provided as a int64.

What I usually to understand this error is locate my Get-ErrorCode function on my own blog and then browse the kb938205 page where I look for the hexadecimal value returned by my Get-ErrorCode function. Then I try give more context on PM.org.
WU-error-reply

As I repeat the same task over time, I wanted to have the answer as quick as possible. I reused some of the code of my Get-ErrorCode function and created the following gist

It works with the value supplied on the PM.org mailing list
Find-WUError-2

And you can also pass values from the pipeline :D
Find-WUError

WMF 5.0 is RTM

Awesome news! The Windows Management Framework (WMF) 5.0 RTM is now available. Congratulations and credits go to the Windows PowerShell Team :-D

It is a huge milestone for the following reasons:

  • WMF 5.0 is ready for production environments except those listed on this page.
  • WMF 5.0 is now supported on all Windows platforms (Windows 7, 2008 R2, 2012, 8.1, 2012 R2 and 10)
  • This means that Microsoft will continue to listen to customers’ feedback and fix issues. (They have introduced a new UserVoice site)

  • WMF 5.0 will allow you to benefit from the newest features on all platforms. Adopting WMF5.0 will bring a new level of standardization whatever the underlying operating system.
  • There’s a pretty long list of features on the Download Center page, where you can download the WMF 5.0 binaries.
    I would like to emphasize on some major features that will for sure change the way we operate in IT: Desired State Configuration (DSC), Oneget aka PackageManagement, Pester and of course the hardened security layer built into WMF 5.0.

    Here’s WMF 5.0 RTM on Windows 8.1
    WMF5-RTM-W81
    Here’s WMF 5.0 RTM on Windows 7
    WMF5RTM-W7

  • Last but not least, Microsoft has provided Release Notes on this page and you can also contribute to these notes on github. The release notes also contain a pretty impressive list of Scenarios Enabled by WMF 5.0

Here’s a summary of what package should be installed on what operating system:

Operating System Architecture Package Name SHA256
Windows Server 2012 R2 x64 W2K12R2-KB3094174-x64.msu CFDA8048978708FE4F31ADB6C045E848DB4AF5DECF88F64E38AA511DB92E1D49
Windows Server 2012 x64 W2K12-KB3094175-x64.msu 2B3776EF5E36DA6B383B84CB1A7247FDAF327FA0AEEA9D8E26CEA69076D8EEAD
Windows Server 2008 R2 x64 W2K8R2-KB3094176-x64.msu 22D4A4FF739A3734E1EFA410BD9F745C668C31EFEEFFA170F7968D42022C641F
Windows 8.1 x64 W2K12R2-KB3094174-x64.msu CFDA8048978708FE4F31ADB6C045E848DB4AF5DECF88F64E38AA511DB92E1D49
Windows 8.1 x86 Win8.1-KB3094174-x86.msu 8E3A1414E57211623C2A6097DBDF982855F2E53FEE65859DFA36891BF0BE8E87
Windows 7 SP1 x64 W2K8R2-KB3094176-x64.msu 22D4A4FF739A3734E1EFA410BD9F745C668C31EFEEFFA170F7968D42022C641F
Windows 7 SP1 x86 Win7-KB3094176-x86.msu DFAC22163AA6C51EAF0F0AE59A3F9632558AA08229350D0F272D742BD02F7109

Special note:
If you want to generalize WMF5.0 on all downlevel operating systems, you’ll have first to deal the installation requirements.
The shortest way to install the .Net prerequisites is to download and install .NET Framework 4.5.2 that is an in-place upgrade of the .Net 4.x family.
The .Net 4.5.2 is actually following the Internet Explorer 11 support path.

Beginning January 12, 2016 only .NET Framework 4.5.2 will continue receiving technical support and security updates.

You can find more info on this topic on this page. I currently don’t recommend .Net 4.6 because of the limits enumerated by Aaron Stebner on this page.

The .Net 4.5.2 KB support page is here, and its location on the Download Center here.

Then, you’ve to install some security updates that targets .Net 4.5.2 (I’ve got 10 on Windows 7). After that, you’re in a good position to deploy WMF5.0 :-D

Download Firefox

I’ve been using the ftp site of Mozilla for a few years to download Firefox releases and check their hashes.
Unfortunately, Mozilla decommissioned that site and left the following instructions on this page https://ftp.mozilla.org/pub/firefox/releases/latest/README.txt
MF-ftp

Here’s the function I quickly wrote to address these shortcomings:

#Requires -Version 3.0

Function Download-Firefox {
[CmdletBinding()]
Param(

[Parameter(Mandatory)]
[ValidateSet('Win32','Win64','OSX','Linux32','Linux64')]
[string]$TargetOS,

[Parameter()]
[ValidateSet('fr','en-US','en-GB')]
[string]$Language = 'en-GB',

[Parameter(Mandatory)]
[string]$TargetFolder

)
Begin {

    try {
        Get-Item $TargetFolder -ErrorAction Stop | Out-Null
    } catch {
        Write-Warning -Message "The target folder specified as parameter does not exist"
        break
    }

    Switch ($TargetOS) {
        'Win32'   { $OS = 'win'     ; break }
        'Win64'   { $OS = 'win64'   ; break }
        'OSX'     { $OS = 'osx'     ; break }
        'Linux32' { $OS = 'linux'   ; break }
        'Linux64' { $OS = 'linux64' ; break }
        default   {}
    }

    $URI = ('https://download.mozilla.org/?product=firefox-latest&os={0}&lang={1}' -f $OS,$Language)
    Write-Verbose -Message "Download URL set to $($URI)"
}
Process {

    $page  = Invoke-WebRequest -Uri $URI -MaximumRedirection 0 -ErrorAction SilentlyContinue -UseBasicParsing

    if ($page.StatusCode -eq 302) {

        $FileSource = $page.Headers.Location
        $FileName =  Split-Path ([uri]$page.Headers.Location).LocalPath -Leaf

        if (Test-Path -Path $TargetFolder -PathType Container) {
            try {
                Invoke-WebRequest -Uri $FileSource -UseBasicParsing -OutFile (Join-Path -Path $TargetFolder -ChildPath $FileName) -ErrorAction Stop
                Write-Verbose -Message "Successfully downloaded $($FileName)  from $($FileSource)"
                Get-Item (Join-Path -Path $TargetFolder -ChildPath $FileName)
            } catch {
                Write-Warning -Message "Failed to download $($FileName) from $($FileSource) because $($_.Exception.Message)"
                break
            }
        } else {
            Write-Warning -Message "The target folder specified as parameter should be a folder"
        }
    } else {
        Write-Warning -Message "Failed to query $URI. Return code was $($page.StatusCode)"
    }
}
End {}
} # end of function

Here’s how to use this function:

Download-Firefox -TargetOS Win64 -Language en-US -TargetFolder C:\ -Verbose |
Get-AuthenticodeSignature

Download-MF