Quantcast
Channel: >_
Viewing all 281 articles
Browse latest View live

Check CVE-2020-1048 with AutoRuns

$
0
0

If you’ve seen what’s going on with CVE-2020-1048, it looks quite scary.

I’ve created an issue (#71) for this and added a detection in the Print Monitors category (see this commit)

I’ve published a digitally signed version of the AutoRuns module on the PowerShell Gallery as well.
(If you get started with AutoRuns, have a look at this README page.)

Now, if you do this,

Add-PrinterPort -Name "C:\windows\tracing\myport.txt"

You get it detected with the AutoRuns Module like this

Get-PSAutorun -PrintMonitorDLLs -VerifyDigitalSignature | 
Where { -not($_.Signed) }

Notice that there’s still an issue with the ImagePath property that needs to be fixed.
Anyway, it’s quick & dirty and detected 🙂
Happy hunting 😎


How to clear a local Applocker policy

$
0
0

There are 3 main actions in this menu when you edit the local Applocker policy. You can Import, Export and Clear a policy.

Let’s see how one can clear a local Applocker policy.

If you use Windows PowerShell, you can directly access the built-in Applocker module.
In this case, you can use the following shortcut:

$null | New-AppLockerPolicy -User EveryOne -EA 0 | 
Set-AppLockerPolicy -Verbose

NB: EA is the Alias of ErrorAction and 0 means SilentlyContinue.
It’s required to avoid displaying a message saying:

New-AppLockerPolicy : Cannot validate argument on parameter ‘FileInformation’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

.
Even if there’s an error thrown, a Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy is created and sent to the output stream.

Unfortunately, the same shortcut cannot be used within PowerShell 7.0.1 (current latest version).
You need the following 2 steps:

# step 1: write an empty policy to a file
$null | New-AppLockerPolicy -User EveryOne -EA 0 -Xml | 
Out-File ~/Documents/empty.xml

# step 2: import that file
Set-AppLockerPolicy ~/Documents/empty.xml

NB: Notice the addition of the -XML switch in the first step.

Here’s another approach for Windows PowerShell that looks like the example provided by Microsoft, named delete-an-applocker-rule that tells you actually how to clear *all* the rules.

The following example doesn’t write a file to disk and directly clears the local Applocker Policy

NB: Notice the first call at line 11 to a built-in command from the Applocker module. It’s used to avoid this error message: Unable to find type [Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy].
If that first call at line 11 is missing, when you do the following, you get:

Weird, isn’t? If you’ve an explanation, please add a comment 🙂

About PowerShell update notifications

$
0
0

It’s nice that we have such a good documentation about_update_notifications 🙂

What is unclear for me that could be improved is what kind of environment variable are required and when the check happens.

First let’s see how these notifications look like:

I naively set an environment variable inside the current user profile.

if ($PSVersionTable.PSEdition -eq 'Core') {
 Set-Item -Path Env:\POWERSHELL_UPDATECHECK -Value 'Off'
}

It means that on Windows, a ‘Process‘ environment variable being set using PowerShell profiles doesn’t work.
The notification check happens before a profile is processed.

Let’s test a ‘User‘ environment variable

# Set
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 'Off',
 [System.EnvironmentVariableTarget]::User
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::User
)

A new shell or process should be started to take into account this new User environment variable defined.
The notification check doesn’t occur in this case.

Delete first the User env. variable.

# Set to NUL to delete
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 $null,
 [System.EnvironmentVariableTarget]::User
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::User
)

Set a ‘Machine‘ environment variable

# Set
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 'Off',
 [System.EnvironmentVariableTarget]::Machine
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::Machine
)

A new shell or process should be started to take into account this new Machine environment variable defined.

The variable appears to be defined in the PowerShell environment drive and can be retrieved using a .Net method call.

What else works?
If you are in a Command Prompt, this works:

If you are in a Windows PowerShell console,

Set-Item -Path Env:\POWERSHELL_UPDATECHECK -Value 'Off'

The environment variable can be inherited by the parent process whatever its type.

Conclusion: The type is actually irrelevant.
You have multiple choices to set this environment variable. Either in the Windows PowerShell profile (if pwsh.exe is started from there), or in the Machine or User environment variables (if pwsh.exe is started by double-clicking or has cmd.exe as a parent process). What doesn’t work is a Process environment variable defined in a PowerShell (Core) profile.

About Autoruns 13.98

Quick post about: LCM failed to deserialize configuration status

$
0
0
    • Context

I’ve been running some DSC configuration after a feature update to fix missing or broken things left behind.
I’ve encountered some computers where a DSC config was forgotten and running for quite some time.

    • Problem

On these computers, I sometimes get an error saying LCM failed to deserialize configuration status when I run the Get-DscConfigurationStatus cmdlet.

Here are some potential reasons why it fails when I run the the Get-DscConfigurationStatus cmdlet.


The DSCStatusHistory.mof is about 10MB


And there are 50K files in the C:\Windows\system32\Configuration\ConfigurationStatus\ folder.

    • Solution

Here’s what I run to fix it. Kudo to Dave Wyatt who posted the solution on the powershell.org forum. Thanks Dave 🙂

ren C:\Windows\System32\Configuration\DSCStatusHistory.mof DSCStatusHistory.mof.prev
dir C:\Windows\System32\Configuration\ConfigurationStatus | del -Force

If you do not wait that the LCM applies at least once, I get another message:

But, when I do and the LCM runs at least once, I get it fixed 😀

About the Applocker service

$
0
0
  • Problem

I use both PowerShell and Applocker a lot. It’s quite natural to do the following

Set-Service -Name AppIDSvc -StartupType Automatic

Instead of configuring it, I get an ‘Access Denied’ 😦

  • Cause

It appears that

Starting with Windows 10, the Application Identity service is now a protected process. Because of this, you can no longer manually set the service Startup type to Automatic by using the Sevices snap-in.

Source: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/configure-the-application-identity-service

It also documents the official two ways of configuring the service StartupType to automatic.
Note that if your device is domain joined, you can use a Domain based GPO to change the service StartupType instead of using LGPO.exe

It’s protected and I can see in the registry the following indicator: the LaunchProtected dword value set to 0x2

  • Solution

I came up with a 3rd (longer) way of doing it (that could be a very long one-liner).

# Get the StartupType
Get-CimInstance -ClassName Win32_Service -Filter "Name='AppIDSvc'"

$cmd = 'Set-Service -Name AppIDSvc -StartupType Auto'
$aHT = @{
 Execute = 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe'
 Argument = '-Exec Bypass -Command "{0}"' -f "$($cmd)"
}
$HT = @{
 TaskName = 'ConfigAppIdSvc'
 User = 'S-1-5-18' # 'NT Authority\System'
 Force = [switch]::Present
 Action = (New-ScheduledTaskAction @aHT)
}
Register-ScheduledTask @HT | 
Start-ScheduledTask

# Wait a little bit and get the StartupType
Get-CimInstance -ClassName Win32_Service -Filter "Name='AppIDSvc'"

NB: Although the service is running under the NT Authority\LocalService (S-1-5-19), it requires the NT Authority\System (S-1-5-18) to modify its StartupType.

AutoLogon on Windows 10

$
0
0
  • Context

I’ve been using Autologon for a while on some dedicated computers since Windows XP.
I needed it to configure the user environment/session on hundreds of computers. In other words, I simply needed automation. The combination of both the autologon and a logonscript allowed to do it quickly in a consistent, repeated and reliable manner.
It worked well on Windows 7 by following the steps described in this support page, named How to turn on automatic logon in Windows and without modifying anything in the registry keys used to set it up on Windows XP.

  • Problem

We had to change our Windows 7 computers before its end of life on the 14th of January, 2020.
We decided to use the 1909 branch of Windows 10 but failed to get the autologon working using the technique and registry keys that worked on Windows 7 and since XP 😦
The above support page were not updated yet. In January 2020, it still applied to Windows XP, Windows 7,…
Now that it has been updated, any client Windows operating system has been removed.

We were not the only ones who reported the same issue: it just didn’t work on Windows 10.

There was no explanation on the behavior we observed and no official guidance on how to do it.

There was something underlying that was modifying the registry values. The result was that autologon failed.
It looked like the keyboard layout behavior I described earlier in this blog post.

  • Solution

I used the technique I’ve shown in my previous blog post. Running a scheduled task as NT AUTHORITY\SYSTEM account (S-1-5-18) that sets the registry keys made our day 🙂

Here’s what the code looks like in the post-installation script of our computers.
There’s a script autologon.ps1 dropped in C:\Windows.
It already contains everything required to autologon. The domain name, the username, the password,..
It first takes the last 2 digits of the computername and uses it in the username.
All the standard domain accounts have the same password and they can only logon interactively. There’s a group policy that prevents any lateral movement so that the credential of one account cannot be used on the other computer over the wire.

@'
$desk = -join "$($env:computername)"[-2..-1]
$key = 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('delete',"$($key)",'/v','AutoLogonCount','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('delete',"$($key)",'/v','AutoLogonChecked','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultUserName','/t','REG_SZ','/d',"MyUserPrefix$($desk)",'/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultPassword','/t','REG_SZ','/d','""','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultDomainName','/t','REG_SZ','/d','MyDomainName','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','AutoAdminLogon','/t','REG_SZ','/d','1','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','ForceAutoLogon','/t','REG_DWORD','/d','0x1','/f')
'@ | Out-File 'C:\Windows\autologon.ps1' -Encoding 'Ascii'
try {
$errHT = @{ ErrorAction = 'Stop' }
$aHT = @{
Execute = 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe'
Argument = '-Exec Bypass -File "C:\Windows\autologon.ps1"'
}
$HT = @{
TaskName = 'Set-AutoLogon'
User = 'S-1-5-18' #'nt authority\system'
Force = [switch]::Present
Action = (New-ScheduledTaskAction @aHT @errHT)
}
Register-ScheduledTask @HT @errHT
Write-Verbose -Message "Successfully registered autlogon scheduled task" -Verbose
} catch {
Write-Warning -Message "Failed to register autologon scheduled task because $($_.Exception.Message)"
}
view raw autologon.ps1 hosted with ❤ by GitHub

MSEdge start page

$
0
0
  • Context

I needed to configure a few Windows 10 computers as if they were a kiosk computer.
These 5 computers are part of a workgroup. They are not part of a domain or AD joined or whatever.
I installed the new Microsoft Edge (chromium based) browser and have been tasked to set a start page.

  • Problem

Some settings that can be defined to set the StartPage for example don’t work when the computer is part of a workgroup.
They only work when the computer is part of a domain or is managed by a MDM 😦

It feels like the Apple guru told me that I cannot change the default browser on an iPhone, that Safari is good for me and that I’ve to stick with it 😦 D’oh!

The behavior is documented on this page and says:

If you browse the address edge://policy, you can see the settings that are not applied. Their Status reports ‘Error, Ignored’:

  • Solution

I took me 4 days to come up with a solution. 2 more days than I took me to come up with ideas that led to @sys:doesnotexist : Disallow AutoPlay/Autorun from Autorun.inf.

It wasn’t straightforward. Here’s a quick overview of the ideas I explored:

  1. Observe the behavior of msedge.exe: what dll it loads, what registry keys it queries to find out that the computer is part of a workgroup and is not domain joined or Azure AD joined.
  2. Lie somehow to the computer and make it believe it’s part of a domain.
  3. Provision a temporary Azure AD tenant, configure Intune, push a MSEdge configuration, enroll the device and observe what’s being done at enrolloment.

It appears that MSEdge.exe reads all the registry values under HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge.
It determines after that if the device is part of a workgroup, or is domain-joined or AAD-joined.
It uses and loads the following Dlls: C:\Windows\System32\mdmregistration.dll, C:\Windows\System32\dsreg.dll,…

To join the computer to a domain that’s unreachable. I provisioned a offline blob using djoin.exe.

.\djoin.exe /requestODJ /loadfile C:\blob.dat /windowspath C:\Windows /localos

The 3 most interesting registry keys written by the above djoin.exe command are:
HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\JoinDomain
HKLM\CurrentControlSet\Control\Lsa\OfflineProvisioning
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\OfflineJoin

The problem with an offline join is that there’s a slowdown because the netlogon service is trying to reach the domain controller and the Built-in local groups (users and administrators) have the SID of the Domain Users and Domain Admins assigned as members.

The Intune provisioning path is actually the most promising. It takes only a few hours to configure before you can enroll the device. The device should be Azure AD joined and not Azure AD registered to have its security baseline being pushed.

Procmon.exe shows that MSEdge.exe reads the following registry keys:

It looks first for the key name (a GUID) under HKLM\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts.

It uses this key name to open HKLM\SOFTWARE\Microsoft\Enrollments\ and look for a DWORD value named EnrollmentType

0x0 means not enrolled or to be reset according to this page, 0x6 MDM enrolled.

It also reads HKLM\SYSTEM\CurrentControlSet\Control\CloudDomainJoin but this key and its info aren’t required to make MSEdge.exe think it’s MDM enrolled.

Here’s my solution:

@'
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge]
"BrowserSignin"=dword:00000000
"BrowserAddProfileEnabled"=dword:00000000
"NonRemovableProfileEnabled"=dword:00000001
"HomepageIsNewTabPage"=dword:00000000
"NewTabPageLocation"="about:blank"
"SmartScreenEnabled"=dword:00000001
"SSLErrorOverrideAllowed"=dword:00000000
"ShowHomeButton"=dword:00000001
"HideFirstRunExperience"=dword:00000001
"PasswordManagerEnabled"=dword:00000000
"SSLVersionMin"="tls1.2"
"RestoreOnStartup"=dword:00000004
"SitePerProcess"=dword:00000001
"AuthSchemes"="ntlm,negotiate"
"SmartScreenPuaEnabled"=dword:00000001
"PreventSmartScreenPromptOverride"=dword:00000001
"DefaultPluginsSetting"=dword:00000002
"NativeMessagingUserLevelHosts"=dword:00000000
"HomepageLocation"="https://myhomepage"
"ClearBrowsingDataOnExit"=dword:00000001
"PreventSmartScreenPromptOverrideForFiles"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallBlocklist]
"1"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\RestoreOnStartupURLs]
"1"="https://myhomepage"
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
"EnrollmentType"=dword:00000000
'@ |
Out-File 'C:\Windows\edge.dat' -Encoding 'Ascii'
regedit.exe --% /s C:\Windows\edge.dat
view raw fix-MSedge.ps1 hosted with ❤ by GitHub

At the end, I’ve got Edge Chromium running on a workgroup based computer, reading and applying all the settings under the HKLM\SOFTWARE\Policies key including the RestoreOnStartup and the HomepageLocation values.

Happy days 😎


Quick post about MBSA

$
0
0
  • Context

A PM.org member asked to the list:

We use MBSA with a few down-level OS’s as well as Windows 10 in several offline environments, and until this month, it still worked.

  • Problem

It appears that MBSA isn’t supported anymore: https://docs.microsoft.com/en-us/windows/security/threat-protection/mbsa-removal-and-guidance

  • Solution

Another member suggested the following:

Why not use WUA script to scan for updates offline ?
https://docs.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline

If you’ve a downloaded version of the wsusscn2.cab file that you indicate as the FilePath parameter of the following script, you can do:

Start-WUOfflineScan -FilePath C:\temp\wsusscn2.cab -Verbose

Function Start-WUOfflineScan {
<#
.SYNOPSIS
Start an offline WUA scan
.DESCRIPTION
Start an offline WUA (Windows Update Agent) scan using wsusscn2.cab.
.PARAMETER FilePath
Specifies the path to the wsusscn2.cab file to be used to perform the offline scan.
.PARAMETER IncludeSupersededUpdate
Specifies to include superseded updates in the results if any.
.EXAMPLE
Start-WUOfflineScan -FilePath C:\temp\wsusscn2.cab -Verbose
.NOTES
You can get the offline wsusscn2.cab from:
http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab
Official doc is:
https://docs.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidateScript({Test-Path -Path $_ -PathType Leaf})]
$FilePath,
[switch]$IncludeSupersededUpdate
)
Begin {
if ($FilePath -match '^~') {
$FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
}
try {
$UpdateSvc = New-Object -ComObject Microsoft.Update.ServiceManager
} catch {
Write-Warning -Message "Failed to query if server is configured against a WSUS server because $($_.Exception.Message)"
}
Function Test-WUARebootRequired {
try {
(New-Object -ComObject 'Microsoft.Update.SystemInfo').RebootRequired
} catch {
Write-Warning -Message "Failed to query COM object because $($_.Exception.Message)"
}
}
}
Process {
if (-not(Test-WUARebootRequired)) {
try {
# Create a session
$Session = New-Object -ComObject Microsoft.Update.Session
# Import the the offline cab
$UpdateService = $UpdateSvc.AddScanPackageService('Offline Sync Service',"$($FilePath)", 1)
$Searcher = $Session.CreateUpdateSearcher()
$Searcher.ServerSelection = 3 #ssOthers
if ($IncludeSupersededUpdate) {
$Searcher.IncludePotentiallySupersededUpdates = $true
}
$Searcher.ServiceID = $UpdateService.ServiceID.ToString()
$Criteria = "IsInstalled=0 and DeploymentAction='Installation' or IsPresent=1 and DeploymentAction='Uninstallation' or IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1"
# Search for updates
$SearchResult = $Searcher.Search($Criteria)
} catch {
Write-Warning -Message "Failed to search for missing updates because $($_.Exception.Message)"
if ($SearchResult) {
Write-Warning -Message "The search result status code was $($SearchResult.ResultCode)"
}
break
}
if ($SearchResult.ResultCode -eq 2) {
# Output what was found
if (($SearchResult.Updates).Count -ne 0) {
$SearchResult.Updates |
ForEach-Object {
Write-Verbose -Message "Missing update: $($_.Title)"
$_
}
} else {
Write-Verbose -Message 'There are no updates to install' -Verbose
}
} else {
Write-Warning -Message 'Failed to search for updates'
Write-Warning -Message "The search result status code was $($SearchResult.ResultCode)"
}
} else {
Write-Warning -Message 'A reboot is pending'
}
}
End {}
}

Friday fun: Windows Feature Update progress

$
0
0
  • Context

I started recently to launch some feature updates of Windows 10 in a scheduled task running as ‘NT AUTHORIY\SYSTEM’ account. It also means that the UI is hidden for whoever is logged on the computer.

I was also looking for some additional info about:
– how to detect that a feature is running
– how to detect that a feature has just finished running
– how to detect that a reboot is pending because of the feature update that just ran

  • Solution

I started investigating using procmon.exe and tracked what’s written under HKLM\System\setup.

I noticed that I can track the progress of a feature update by using a one-liner:

While (ps setup* ) { Write-Progress -Activity Upgrade -PercentComplete "$((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)" -Status "at $((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)%" }

It appears that a SetupProgress value is written under the registry key HKLM:\SYSTEM\Setup\MoSetup\Volatile
Here’s a more readable splatted version of the one-liner:

While (Get-Process -Name setup*) {
 $k = 'HKLM:\SYSTEM\Setup\MoSetup\Volatile'
 $n = 'SetupProgress'
 $p = Get-ItemProperty -Path $k -Name $n
 $HT = @{
  Activity =  'Upgrade'
  PercentComplete = "$($($p).$n)"
  Status = "at $($($p).$n)%"
 }
 Write-Progress @HT
}

History of previous feature updates

$
0
0
  • Context

I’ve discovered that the history of previous versions you had before running a feature update is stored in the registry under HKLM\System\Setup

  • Solution

I wrote a function to read the information under this key:

Function Get-PreviousUpgrade {
<#
.SYNOPSIS
Get info about previous feature updates
.DESCRIPTION
Get info about previous feature updates being used to upgrade Windows 10 to a new branch.
.EXAMPLE
Get-PreviousUpgrade
#>
[CmdletBinding()]
Param()
Begin {}
Process {}
End {
try {
$names = (Get-Item -Path 'HKLM:\SYSTEM\Setup' -ErrorAction Stop).GetSubKeyNames() |
Where-Object { $_ -match '^Source\sOS\s\(Updated\son\s' }
} catch {
Write-Warning -Message "Failed to read the registry key because $($_.Exception.Message)"
}
if ($names) {
$names |
ForEach-Object {
$date = ([regex]'^Source\sOS\s\(Updated\son\s(?<Date>.+)\)').Matches($_) |
Select-Object -ExpandProperty Groups |
Select-Object -Last 1 -ExpandProperty Value
$FromBranch = (Get-ItemProperty -Path (Join-Path -Path 'HKLM:\SYSTEM\Setup' -ChildPath $_) -Name 'ReleaseId').'ReleaseId'
[PSCustomObject]@{
Date = [datetime]$date
Branch = $FromBranch
}
}
} else {
Write-Warning -Message 'No previous upgrades found'
}
}
}

Here’s how to use it 🙂

Get-PreviousUpgrade | 
Where  { $_.Branch -eq '1809' } | 
Select -Property Date

#PowerShell at #Microsoft #MSIgnite

$
0
0

The Microsoft Ignite conference took place this year on https://myignite.microsoft.com/home

Here’s some content related to #PowerShell, what else ! (NB: it’s not the only content in this year’s event):

  • Taking your automation to the next level with #PowerShell 7

Please allow me to add a quick note about the presentation and this slide:

WinRM and Remoting have been released in PowerShell 2.0 with the RTM release of Windows 7 and 2008 R2.
It means Remoting is available since August 2009.
PowerShell 3.0 was released in 2012 and shipped in Windows 8 and Server 2012.

What Joey Aiello meant on the slide is that Remoting is enabled by default since Windows Server 2012, released in September 2012.

Source: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_requirements

Windows Server 2012 and newer releases of Windows Server are enabled for PowerShell remoting by default.

Remoting is the killer feature that made me adopt PowerShell in 2009, it’s so cool because it enables remote management of anything in a secure manner 😎
Remoting is on and rocks since 11 years.

Please let me also add another thought about this presentation.
Windows PowerShell is part of the WMF, a.k.a. the Windows Management Framework that adds these components:

So, my question is: what are the investments made into the above components of the WMF since Microsoft open-sourced PowerShell?

  • PowerShell Unplugged – Challenge Edition

What a mind-blowing kickboxing match of new features 😉 !

Reinstall preinstalled (appx) Apps

$
0
0
  • Context

I’ve discovered recently this link to reinstall-preinstalledApps.zip that was provided on this page

  • Solution

The code is straightforward and probably does the job in most situations.
But, there are some assumptions about the version, admin rights,.. and the code has no error handling. You can see the file here.
I’ve decided to improve it by adding a help, a #require statement, error handling and the ability to use the -WhatIf parameter.

If you use my “slightly improved” version, you can do:

# Show what would be done using the -WhatIf parameter
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*" -WhatIf

# Do it:
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*" -Verbose

#Requires -RunAsAdministrator
Function Start-ReInstallAppx {
<#
.SYNOPSIS
Get back all the apps that come default with Windows 10
.DESCRIPTION
Get back all the apps that come default with Windows 10
.EXAMPLE
Start-ReInstallAppx -Filter "*Microsoft.WindowsStore*"
.NOTES
http://go.microsoft.com/fwlink/?LinkId=619547
Adapted from
http://download.microsoft.com/download/5/F/0/5F04003A-035E-4A0F-9662-43E32C546F6C/reinstall-preinstalledApps.zip
#>
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$Filter
)
Begin {
$HT = @{ ErrorAction = 'Stop'}
}
Process {
try {
# Get all the provisioned packages
$Packages = Get-Item -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Applications' @HT |
Get-ChildItem @HT
} catch {
Write-Warning -Message "Failed to get packages list from the registry or filesystem because $($_.Exception.Message)"
}
if ($Packages) {
if ($Filter) {
$Packages = $Packages | Where-Object {$_.Name -like "$($Filter)" }
}
$Packages |
ForEach-Object -Process {
$p = $_
$PackageName = $PackagePath = $null
# get package name & path
try {
$d = $p | Get-ItemProperty @HT
if ($d) {
$PackageName = $d | Select-Object -ExpandProperty PSChildName
$PackagePath = [System.Environment]::ExpandEnvironmentVariables(($d| Select-Object -ExpandProperty Path))
}
} catch {
Write-Warning -Message "Failed to get package name and path for package $($p) because $($_.Exception.Message)"
}
# register the package
if ($PackageName -and $PackagePath) {
Write-Verbose -Message "Attempting to register package: $($PackageName)"
if ($PSCmdlet.ShouldProcess("$($PackageName))",'Register Appx package')) {
try {
Add-AppxPackage -Register $PackagePath -DisableDevelopmentMode @HT
Write-Verbose -Message "Successfully registered package from path $($PackagePath)"
} catch {
Write-Warning -Message "Failed to regiter from $($PackagePath) because $($_.Exception.Message)"
}
}
}
}
}
}
End {}
}

How to whitelist your AutoRuns artifacts

$
0
0

Have you ever wondered how can I create a whitelist of my known harmless AutoRuns artifacts?

There are various ways to filter and ignore known artifacts. It can be done either for hunting purposes and to spot more quickly what’s unknown that requires a review and (probably) further actions.

You can filter Autoruns artifacts by using the authenticode certificate, the SHA256 hash,…

All these methods will work but there are some known artifacts that are still difficult to filter out.

I’ll show here a way to deal with these artifacts and create a baseline or whitelist of known artifacts.

I’ve introduced a new function in the module to create a whitelist of artifacts stored in a .ps1 file.

You can directly send the output of the main Get-PSAutorun function to the new New-AutoRunsBaseLine so that a .ps1 file is created. This powershell script stores an array of artifcats stored as PSCustomobject.

You may wonder why a .ps1 file and not a json file. The reason is that you can use a code signing certificate to sign the created ps1 file using the built-in Set-AuthenticodeSignature cmdlet. It brings integrity. It’s a signed whitelist. Signing may also be required if you use the PowerShell constrained language mode.

You can for example store all the artifacts with the maximum properties available.

Get-PSAutorun -VerifyDigitalSignature -ShowFileHash|
New-AutoRunsBaseLine -Verbose

Later on, to see if there are any changes, you can use the second function I’ve introduced: Compare-AutoRunsBaseLine

Here’s an example where I duplicated an existing file and modified one of its properties: the ending digit of the version.

For hunting purposes, you may want to store a minimalist whitelist and use it as a filter to view what’s relevant.

Get-PSAutorun -VerifyDigitalSignature|
Where { -not($_.isOSbinary)}|
New-AutoRunsBaseLine

If you want to view what has been exported in the whitelist, you just need to run the script created.

~\Documents\PSAutoRunsBaseLine-20201102214715.ps1 |
Out-GridView -PassThru

Here’s an example where I selected only one difficult artifact to filter out from the grid output:

Here’s another example I use on my personal computer:

Get-PSAutorun -VerifyDigitalSignature -ShowFileHash |
Where {-not($_.Version)} | Where { -not($_.isOSbinary)} |
New-AutoRunsBaseLine -Verbose

If you want to see what that file contains, I’ve uploaded a version here.

If you think that these new functions are useful, please vote for them 😀

The above new functions are currently only part of the experimental branch of Autoruns repository on Github. It’s not signed digitally and the Autoruns.cat file has not been modified to reflect the changes of the main Autoruns files (.psd1 and psm1). It’s also not been published yet to the PowerShell Gallery. It all depends on your votes 😀 . If you think it’s useful, please let me know. If you think, it’s not, please let also me know. If you find issues with the code, please introduce it directly on Github using this link.

Enjoy and happy hunting 😎

Scoring Applocker rules

$
0
0

To be able to audit Applocker rules created or hunt for human error, I wanted to have a simple way to score Applocker rules.
Are they good or evil? Are they tuned and specific enough to allow or block something?
Is there any rule that is more permissive or not well designed due to a human error?

  • Issue

My Google-fu didn’t find anything relevant to help me score Applocker rules 😦

  • Solution

How are rules scored?
I’ve tried to score various aspects of the rule, based on the type (Publisher, Path or Hash), the identity targeted, the action (Allow or Deny) and the conditions.
The code aims at finding and identifying some rules that are more permissive than others, so that it’s easier to spot them and pay attention to these rules.
All the functions scoring rules follow a simple principle. The higher score, the more dangerous/permissive the rule is actually.
There’s a limitation, the code doesn’t evaluate at all the Exceptions and worse, having conditions could have a negative impact on the score (lowering it).
The other limitation is that the code assumes that there isn’t an administrator introducing malicious rules.

How does it perform? Show me examples !

Import-Module -Name Applocker -SkipEditionCheck
Import-Module ~/Documents/ApplockerScore.psm1 -Verbose
Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts'


You can see that there’s tab completion for the parameter. You just need to begin by the Type to be able to display the rule names on the 2nd parameter using tab completion.
The default rule that allows anything for an admin is score over 1000. It means it’s too permissive. It’s a Bypass rule for anything (a wildcard) for the members of the local Administrators group. It’s not the highest score you can get.

cd\
Import-Module -Name Applocker -SkipEditionCheck
Import-Module ~/Documents/ApplockerScore.psm1
Get-Help Get-ApplockerPolicyRuleScore -Full
$rule = Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts' -Verbose
 Write-Verbose -Message "Final score for '$($rule.Name)' is $($rule.Score)" -Verbose
$rule.Object
$rule.Object | gm


It shows how the score is calculated and what was evaluated to score the rule.
There’s an embedded object property that is its XML definition converted back to its original object type.

Besides simple examples, can it be used against exported Applocker policies to XML files?

Yes, it can. You just need to use the internal private functions and load them.
Here’s a practical example on Ultimate AppLocker ByPass List from Oddvar Moe

Let’s say, you’ve downloaded his Github repo as a zip file, extracted it and loaded the internal functions.
You can now do the following:

Get-ChildItem -Path '~\Downloads\UltimateAppLockerByPassList-master\UltimateAppLockerByPassList-master\AppLocker-BlockPolicies\*.xml' |
ForEach-Object {
([xml](Get-Content -Path $_.FullName -ReadCount 0)).AppLockerPolicy.RuleCollection.ChildNodes |
Out-GridView -PassThru |
ForEach-Object {
$TypeScore = Get-ApplockerRuleTypeScore $_.OuterXML -Verbose
$ActionSore = Get-ApplockerRuleActionScore $_.OuterXML -Verbose
$IdScore = Get-ApplockerRuleUserOrGroupSidScore $_.OuterXML -Verbose
$ConditionScore = Get-ApplockerRuleConditionsScore $_.OuterXML -Verbose
[PSCustomObject]@{
Id = $_.Id
Name = $_.Name
Score = ($TypeScore+$IdScore+$ConditionScore)*$ActionSore
XML = $_.OuterXML
}
}
}

Here’s an example on the rule named ‘Signed by Skype’:

Here’s a 2nd example on the rule about MSHTA:

Where’s the code?

It’s stored in a psm1 file uploaded as gist on GitHub.com:

#Requires -Version 3.0
#region Score the identity
Function Get-ApplockerRuleUserOrGroupSidScore {
[CmdletBinding()]
[OutputType([int32])]
Param(
[Parameter(Mandatory)]
[Alias('UserOrGroupSid','SID')]
$InputObject
)
Begin {}
Process {
$InputObject |
ForEach-Object {
Switch -Regex ($_) {
'S-1-1-0' { # EveryOne
Write-Verbose -Message 'Applocker score: 50 ; Everyone'
50 ; break
}
'S-1-5-32-544' { # Administrators
Write-Verbose -Message 'Applocker score: 10 ; Administrators'
10 ; break
}
'S-1-5-32-545' { # Users
Write-Verbose -Message 'Applocker score: 10 ; Users'
10 ; break
}
# S-1-5-11 # Authenticated Users
# S-1-5-32-546 # Guests
# S-1-5-32-547 # Power Users
default {
Write-Verbose -Message 'Applocker score: Id is specific'
10
}
}
}
}
End {}
}
#endregion
#region Score the rule type
Function Get-ApplockerRuleTypeScore {
[CmdletBinding()]
[OutputType([int32])]
Param(
[Parameter(Mandatory)]
[string[]]$InputObject
)
Begin {}
Process {
$InputObject |
ForEach-Object {
Switch -Regex ($_) {
'^<FilePath(Rule|Condition)' { Write-Verbose -Message 'Applocker score: 100 ; Path type' ; 100 }
'^<FilePublisher(Rule|Condition)' { Write-Verbose -Message 'Applocker score: 50 ; Path publisher' ; 50}
'^<FileHash(Rule|Condition)' {Write-Verbose -Message 'Applocker score: 50 ; Path Hash' ; 50}
default {}
}
}
}
End {}
}
#endregion
#region Score the rule action
Function Get-ApplockerRuleActionScore {
[CmdletBinding()]
[OutputType([int32])]
Param(
[Parameter(Mandatory)]
[string[]]$InputObject
)
Begin {}
Process {
$InputObject |
ForEach-Object {
Switch -Regex ($_) {
'\sAction="Allow"' {
Write-Verbose -Message 'Applocker score: *1 ; Action is Allow'
1
}
'\sAction="Deny"' {
Write-Verbose -Message 'Applocker score: /10 ; Action is Deny'
0.1
}
default {}
}
}
}
End {}
}
#endregion
#region Score the rule conditions
Function Get-ApplockerRuleConditionsScore {
[CmdletBinding()]
[OutputType([int32])]
Param(
[Parameter(Mandatory)]
[string[]]$InputObject
)
Begin {}
Process {
$InputObject |
ForEach-Object {
($(
Switch -Regex ($_) {
'^<FilePath(Rule|Condition)' {
Write-Verbose -Message 'Applocker score: Path type'
# Depth: count the occurrence of '\'
$depth = 110 - (($_ -split '\\').Count)*10
Write-Verbose -Message "Depth score is $($depth)"
$depth
# Evaluate if there's a wildcard in the path
if ($_ -match '\\\*\\') {
50
}
Switch -Regex ($_) {
# Just a specific extension
# .exe, .com *.msi, msp mst ps1 cmd bat vbs js appx msix dll ocx
'\sPath="\*\.([eE][xX][eE]|[cC][oO][mM]|[mM][sS][iI]|[mM][sS][pP]|[mM][sS][tT]|[pP][sS]1|[cC][mM][dD]|[bB][aA][tT]|[vV][bB][sS]|[jJ][sS])"\s' {
Write-Verbose -Message "Path score: 500 (only a *.ext)"
500
}
# Path letter
'\sPath="(c|C):\\' {
Write-Verbose -Message "Path score: 100 (Path on C: drive)"
100
}
'\sPath="[abABd-zD-Z]:\\' {
Write-Verbose -Message "Path score: 100 (Path not on C: drive)"
50
}
# Extension: just a wildcard
'\sPath="\*\.\*"\s' { # *.*
Write-Verbose -Message "Path score: 1000 (only a *.*)"
1000 ; break
}
'\sPath="\*"\s' { # *
Write-Verbose -Message "Path score: 1000 (only a *)"
1000 ; break
}
default {}
}
}
'^<FilePublisher(Rule|Condition)' {
Write-Verbose -Message 'Applocker score: Path publisher'
Switch -Regex ($_) {
'PublisherName="\*"\sProductName="\*"\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="0\.0\.0\.0"\sHighSection="\*"' {
Write-Verbose -Message "Publisher score: 1000 (default Appx PublisherName=*)"
1000 ; break
}
'ProductName="\*"\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="\*"\sHighSection="\*"' {
Write-Verbose -Message "Publisher score: 500 (only a PublisherName)"
500 ; break
}
'\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="\*" HighSection="\*"' {
Write-Verbose -Message "Publisher score: 500 (PublisherName and ProductName)"
200 ; break
}
'BinaryVersionRange\sLowSection="\*"\sHighSection="\*"' {
Write-Verbose -Message "Publisher score: 100 (PublisherName and ProductName and FileName)"
100 ; break
}
default {
Write-Verbose -Message "Publisher score: 50 (default)"
50
}
}
}
'^<FileHash(Rule|Condition)' {
Write-Verbose -Message "Hash score: 50"
50
}
default {}
}
) | Measure-Object -Sum).Sum
}
}
End {}
}
#endregion
#region Convert to Object from Xml
Function Get-ApplockerRuleObjectFromXml {
[CmdletBinding()]
[OutputType([int32])]
Param(
[Parameter(Mandatory)]
[string[]]$InputObject
)
Begin {}
Process {
$InputObject |
ForEach-Object {
$o = $_
Switch -Regex ($_) {
'^<FilePathRule' {
try {
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FilePathRule]::FromXml($_)
} catch {
Write-Warning -Message "Failed to translate $($o) to Applocker FilePathRule because $($_.Exception.Message)"
}
break
}
'^<FilePublisherRule' {
try {
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FilePublisherRule]::FromXml($_)
} catch {
Write-Warning -Message "Failed to translate $($o) to Applocker FilePublisherRule because $($_.Exception.Message)"
}
break
}
'^<FileHashRule' {
try {
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FileHashRule]::FromXml($_)
} catch {
Write-Warning -Message "Failed to translate $($o) to Applocker FilePublisherRule because $($_.Exception.Message)"
}
break
}
default {
Write-Warning -Message 'Failed to transtale input into an Applocker rule'
}
}
}
}
End {}
}
#endregion
Function Get-ApplockerPolicyRuleScore {
<#
.SYNOPSIS
Score an Applocker policy rule
.DESCRIPTION
Score an Applocker policy rule
.PARAMETER Type
Parameter that indicates the RuleCollectionType to find a rule
.PARAMETER Rule
Dynamic parameter that indicates the Name of the rule to score
.EXAMPLE
Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts'
Id Name Score XML
-- ---- ----- ---
ed97d0cb-15ff-430f-b82c-8d7832957725 (Default Rule) All scripts 1210 <FilePathRule Id
.EXAMPLE
$rule = Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts' -Verbose
Write-Verbose -Message "Final score for '$($rule.Name)' is $($rule.Score)" -Verbose
$rule.Object
VERBOSE: Dealing with Rule Collection type: Script
VERBOSE: Dealing with Rule Name: (Default Rule) All scripts
VERBOSE: Applocker score: 100 ; Path type
VERBOSE: Applocker score: *1 ; Action is Allow
VERBOSE: Applocker score: 10 ; Administrators
VERBOSE: Applocker score: Path type
VERBOSE: Depth score is 100
VERBOSE: Path score: 1000 (only a *)
VERBOSE: Final score for '(Default Rule) All scripts' is 1210
PathConditions : {*}
PathExceptions : {}
PublisherExceptions : {}
HashExceptions : {}
Id : ed97d0cb-15ff-430f-b82c-8d7832957725
Name : (Default Rule) All scripts
Description : Allows members of the local Administrators group to run all scripts.
UserOrGroupSid : S-1-5-32-544
Action : Allow
#>
[CmdletBinding()]
Param(
[ValidateSet('Exe','Script','Msi','Appx','Dll')]
[Parameter(Mandatory)]
[String]$Type
)
DynamicParam {
$Dictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary
#region helper function
Function New-ParameterAttributCollection {
[CmdletBinding(SupportsShouldProcess)]
Param(
[Switch]$Mandatory,
[Switch]$ValueFromPipeline,
[Switch]$ValueFromPipelineByPropertyName,
[String]$ParameterSetName,
[Parameter()]
[ValidateSet(
'Arguments','Count','Drive','EnumeratedArguments','Length','NotNull',
'NotNullOrEmpty','Pattern','Range','Script','Set','UserDrive'
)][string]$ValidateType,
[Parameter()]
$ValidationContent
)
Begin {}
Process {
if ($PSCmdlet.ShouldProcess('Create new Attribute')) {
$c = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$a = New-Object System.Management.Automation.ParameterAttribute
if ($Mandatory) {
$a.Mandatory = $true
}
if ($ValueFromPipeline) {
$a.ValueFromPipeline = $true
}
if ($ValueFromPipelineByPropertyName) {
$a.ValueFromPipelineByPropertyName=$true
}
if ($ParameterSetName) {
$a.ParameterSetName = $ParameterSetName
}
$c.Add($a)
if ($ValidateType -and $ValidationContent) {
try {
$c.Add((New-Object "System.Management.Automation.Validate$($ValidateType)Attribute"(
$ValidationContent
)))
} catch {
Throw $_
}
}
$c
}
}
End {}
}
#endregion
try {
$ApplockerPolicyXml = [xml](Get-AppLockerPolicy -Effective -Xml -ErrorAction Stop)
} catch {
Throw 'Failed to read the effective Applocker policy into XML'
}
#region param Rule
$Dictionary.Add(
'Rule',
(New-Object System.Management.Automation.RuntimeDefinedParameter(
'Rule',
[string],
(New-ParameterAttributCollection -Mandatory -ValidateType Set -ValidationContent (
$ApplockerPolicyXml.SelectNodes("/AppLockerPolicy/RuleCollection[@Type='$($PSBoundParameters['Type'])']").ChildNodes| ForEach-Object { $_.Name }
))
))
)
$Dictionary
}
Begin {}
Process {
Write-Verbose -Message "Dealing with Rule Collection type: $($PSBoundParameters['Type'])"
Write-Verbose -Message "Dealing with Rule Name: $($PSBoundParameters['Rule'])"
# Select node
$n = $ApplockerPolicyXml.SelectNodes("/AppLockerPolicy/RuleCollection[@Type='$($PSBoundParameters['Type'])']").ChildNodes |
Where-Object { $_.Name -eq "$($PSBoundParameters['Rule'])" }
try {
$TypeScore = Get-ApplockerRuleTypeScore -InputObject $n.OuterXml
$ActionSore = Get-ApplockerRuleActionScore -InputObject $n.OuterXml
$IdScore = Get-ApplockerRuleUserOrGroupSidScore -InputObject $n.OuterXml
$ConditionScore = Get-ApplockerRuleConditionsScore -InputObject $n.OuterXml
$r = [PSCustomObject]@{
Id = $n.Id
Name = $n.Name
Score = ($TypeScore+$IdScore+$ConditionScore)*$ActionSore
XML = $n.OuterXml
Object = Get-ApplockerRuleObjectFromXml -InputObject $n.OuterXml
}
Update-TypeData -TypeName 'Applocker.Rule.Score' -DefaultDisplayPropertySet 'Id','Name','Score','XML' -Force
$r.PSTypeNames.Insert(0,'Applocker.Rule.Score')
$r
} catch {
Throw "Something went wrong while scoring applocker rule: $($_.Exception.Message)"
}
}
End {}
} # endof Get-ApplockerPolicyRuleScore
Export-ModuleMember -Function 'Get-ApplockerPolicyRuleScore'
view raw ApplockerScore.psm1 hosted with ❤ by GitHub

End of Life of Flash?

$
0
0
  • Context

Since Adobe will no longer be supporting Flash Player after December 31, 2020 and Adobe will block Flash content from running in Flash Player beginning January 12, 2021, Adobe strongly recommends all users immediately uninstall Flash Player to help protect their systems.

Source: https://www.adobe.com/products/flashplayer/end-of-life.html

This statement is worrying because it suggests that Adobe has some sort of kill switch.

It appears that they have hardcoded the EoL (End-Of-Life) date in the code. If you change the date of your computer before January 12, 2021, you’ll notice a different behavior and won’t get the above blue EoL logo.

Instead you can get for example, just a reminder (IE11 in my case):

  • Issue

You may still have a critical Line of Business (LoB) application that requires Flash to run in a browser.

What can you do after the EoL date?

If you don’t rely on Flash at all, instead of waiting for it to be removed by Microsoft, you can for example implement what I described in this post to avoid Flash from being instantiated at all in Windows and Office. (This post doesn’t include what you’d need to do for Google Chrome or Edge Chromium).

  • Solution

Let’s you have to run Flash for a critical LoB application after January 12, 2021. What can be done?

Well, it depends on the browser.

Let’s say you want it to run in Google Chrome or Edge Chromium.

Both Chromium based browsers (Edge and Google) implement the Chromium based Flash component named PepperFlash.

First, you’ll need to get rid of what’s installed under:

$($env:LOCALAPPDATA)\Google\Chrome\User Data\PepperFlash
$($env:LOCALAPPDATA)\Microsoft\Edge\User Data\PepperFlash

Yes, it’s a per-user non-roaming location 😦
Once, you did the above step, you don’t have any Flash at all in these Chromium based browsers.

To be able to restore Flash, you need to get an older vulnerable version of the PPAPI provided by Adobe and install it.
Once installed, you’ll have for example a pepflashplayer64_32_0_0_371.dll file in C:\Windows\system32\Macromed\Flash.
Don’t ask me what I think about having 2 vulnerable dll files planted in System32 🙄 .

NB: The following registry keys store the version and location of the Adobe PPAPI component: HKLM\SOFTWARE\Macromedia\FlashPlayerPepper and HKLM\SOFTWARE\WOW6432Node\Macromedia\FlashPlayerPepper

To make Google Chrome or Edge Chromium load it, you’ll have to create a folder in these per-user locations

mkdir "$($env:LOCALAPPDATA)\Google\Chrome\User Data\Default\Pepper Data\Shockwave Flash\System"
mkdir "$($env:LOCALAPPDATA)\Microsoft\Edge\User Data\Default\Pepper Data\Shockwave Flash\System"

You’ll also need to create files there, named mms.cfg, that contain some instructions:

'System32','SysWOW64' |
Foreach-Object {
 $p = $_
 try {
  @'
AutoUpdateDisable=1
SilentAutoUpdateEnable=0
EOLUninstallDisable=1
EnableAllowList=1
AllowListUrlPattern=http://my-LOB-server
'@ | 
  Out-File -FilePath "C:\Windows\$($p)\Macromed\Flash\mms.cfg" -Encoding UTF8 -Force -ErrorAction Stop
 } catch {
  Write-Warning -Message "Failed to write C:\Windows\$($p)\Macromed\Flash\mms.cfg because $($_.Exception.Message)"
 }
}

NB 1: Yes, I’ve created the mms.cfg in C:\Windows\System32\Macromed\Flash.
It just needs to be copied to the “LOCALAPPDATA..\Shockwave Flash\System” folders created previously.

NB 2:
The Adobe® Flash® Player 32.0 Administration Guide (uploaded here as well, if it disappears any time soon) says the following:

EnableAllowList. Disabled by default.
Allows system administrators to allow Flash Player to only load content from a set of allowed URLs.
After Flash Player EOL, EnableAllowList will default to true and the MMS.CFG setting will be ignored.

Since you rolled out an older version, you need this setting to be enabled 🙂 .

You may also want to have a tighter control on Flash in these Chromium based browsers using Group Policy that block or allow an list of URL if you don’t trust that much text files (mms.cfg, especially in the user profile as it can be modified).
Here are 2 examples:

Let’s say you want Flash to run in Internet Explorer 11.
You’ve already created the 2 mms.cfg files in C:\Windows\System32\Macromed\Flash and C:\Windows\SysWOW64\Macromed\Flash.

There are two security updates that would need to be removed:
4580325, Security Update for Adobe Flash Player: October 13, 2020
4561600, Security Update for Adobe Flash Player: June 9, 2020
😥
Yes, that’s worse! Now, all binaries under C:\Windows\System32\Macromed\Flash are vulnerable and can potentially be loaded in a browser 😥

How to notify logged-on users

$
0
0

You may have already seen or know how to display a nice pop-up toast notification to your Windows 10 users.
If not, I’d like to point out some material related to these notifications:

Now, that you’re familiar with the notifications part, let’s see how to display them.
Being a big fan of what’s robust and sustainable, I’ll show you how to leverage the built-in Windows 10 scheduled tasks to notify your users 😀

Let’s say you have a PowerShell script that triggers a notification in the end-user context. It’s named toast.ps1.
Let’s say you’ve a Group Policy (GPO) that runs a scheduled task under the NT AUTHORITY\System account.

How can you escape the SYSTEM account and run the Toast.ps1 in every interactively logged on standard account?

Well, the solution is to identify these interactively logged on users and run a new scheduled task that targets their identity.

# Identify interactive users
try {
 $Users = @(Get-WmiObject -Query 'Select *  from Win32_Process' -ErrorAction Stop) |
 Where-Object { $_.ProcessName -eq 'explorer.exe'} |
 ForEach-Object {
  [PSCustomObject]@{
   User = '{0}\{1}' -f $($_.GetOwner().Domain),$($_.GetOwner().User)
  }
 }
} catch {
 Write-Warning -Message "Failed to get logged on users because $($_.Exception.Message)"
}
# If any and hour is 10AM for example, notify
if ($Users -and ((Get-Date).ToString('HH',[Globalization.CultureInfo]::InvariantCulture) -in '10')) {
 $Users | ForEach-Object {
 Start-UserNotification -UserName $_ -Prefix 'WhatEver' -FilePath 'C:\Toast.ps1'
 }
}

Here’s the code sample of the Start-UserNotification function used above:

#Requires -RunAsAdministrator
Function Start-UserNotification {
<#
.SYNOPSIS
Start a script in the user context
.DESCRIPTION
Start a script in the user context using scheduled tasks
.PARAMETER UserName
String that represents the targeted user name
.PARAMETER FilePath
String that represents the path to the script to be executed
.PARAMETER Prefix
String that is being used to prefix the task name
.EXAMPLE
Start-UserNotification -UserName LocalUserName -FilePath C:\notif.ps1
#>
[CmdletBinding(SupportsShouldProcess)]
[OutputType('System.Boolean')]
Param(
[Parameter(Mandatory)]
[string]$UserName,
[Parameter(Mandatory)]
[ValidateScript({Test-Path -Path $_ -PathType Leaf})]
[string]$FilePath,
[Parameter()]
[string]$Prefix='__'
)
Begin {
$errHT = @{ ErrorAction = 'Stop' }
$cmd = '& {0}' -f [Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($FilePath)
$aHT = @{
Execute = 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe'
Argument = '-NoProfile -WindowStyle Hidden -Exec Bypass -Command "{0}"' -f "$($cmd)"
}
}
Process {
}
End {
# Notify user using a Scheduled task
try {
$taskName = '{0}-Notify' -f [regex]::Escape($Prefix)
Get-ScheduledTask -TaskPath '\' @errHT | Where-Object { $_.TaskName -match "^$($taskName)_" } | ForEach-Object {
if ($pscmdlet.ShouldProcess("$($_.TaskName)", 'Remove previous task')) {
$null = $_ | Stop-ScheduledTask -Verbose @errHT
$null = $_ | Unregister-ScheduledTask -Verbose -Confirm:$false @errHT
Write-Verbose -Message "Successfully remove task $($_.TaskName)"
}
}
} catch {
Write-Warning -Message "Failed to stop and unregister because $($_.Exception.Message)"
}
try {
$HT = @{
TaskName = '{0}_{1}' -f "$($taskName)","$($UserName)"
User = '{0}' -f "$($UserName)"
Force = [switch]::Present
Action = (New-ScheduledTaskAction @aHT @errHT)
Settings = (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -Compatibility 'Win8' @errHT)
}
if ($pscmdlet.ShouldProcess("$($HT['TaskName'])", 'Create and run task')) {
$null = Register-ScheduledTask @HT @errHT | Start-ScheduledTask @errHT
Write-Verbose -Message "Successfully created and ran task $($HT['TaskName'])"
$true
}
} catch {
Write-Warning -Message "Failed to register and run notification task because $($_.Exception.Message)"
$false
}
}
}

Here’s how to use the function:

MSRC API and MsrcSecurityUpdates module improvements

$
0
0

Great news, the PowerShell MsrcSecurityUpdates has been updated to version 1.9.0 (available on the PSGallery) and doesn’t have any API key requirement anymore 😎

Here’s the latest blog post from the MSRC about this API key requirement removal and more

While the API key removal is a very nice move, it’s also somehow the tip of the iceberg 🙂

I’d like to highlight the recent changes made in the code that appear in its GitHub repository.

Change desscription: Related commit:
Adding the info about who issues the CVE (CNA info) 5c8dd29a86fce3146986acdff4f234ca6bfed531
Adding known issues column and info b5876b0aaa22c9f40ea904320ee533a2aed612bd
Forcing any https call to use TLS 1.2 47557e9a2f34b7938d346a932ef37cb7c78895a1
Fixing some CSS issues about a timeout and changing some colors 555acbe8a437ab76dbc91fdb26254aa616091467
Adding more info in error messages (it was discussed here in issue 73) 598755821c8d4473ef54a13c41bd4cc523ebe19a
Restoring Summary (makes sense in Advisories for example) and sort revisions 2114e895900f2d9de0f1f6ea59279cb615e69a0d
Adding a tag in the html commented lines about the version of the MsrcSecurityUpdates module used to generate the html pages 87ba10278b0cbf03921c21e25b881c4de5128acf
Adding a switch to avoid displaying a progress bar 34b528dd954f907c6c7d29217270df72bd041b4d
Yes, we try to fix PSScriptAnalyzer issues reported fdb79f03733dccd4112111516bc92ec0910af26a


If you encounter issues about the module, they can be reported here

Because of the CSS timeout issue, I’ve downloaded all the bulletin data I store in this separate repository.

Quick post: Delete a WSUS update

$
0
0
  • Context

I started working on the KB4577586, the update responsible for the removal of Adobe Flash Player.

I’ve encountered two issues.

  • Issue

First issue, I imported the update for Windows 10 2OH2 x64 using the Id of Server 2019, D’oh!

Second issue, when I first imported my files and their Id, it failed with this message:
Exception calling “ImportUpdateFromCatalogSite” with “2” argument(s): “The underlying connection was closed: An unexpected error occurred on a send.”

Solution

Let’s start with the 2nd issue: the fact that I cannot import an update.
It appears that although I’ve set the correct Protocol to use

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

The ImportUpdateFromCatalogSite method is a .Net method and .NET requires to StrongCrypto value to be set in the registry.

My 2nd issue was solved by

$HT = @{
 Name = 'SchUseStrongCrypto'
 Value = '1'
 Type = 'DWord'
}
$null,'Wow6432Node' | 
Foreach-Object {
 Set-ItemProperty @HT -Path "HKLM:\SOFTWARE$($_)\Microsoft\.NetFramework\v4.0.30319" 
}
Restart-Computer

Let’s get back to my first issue. I got the correct file but not the Id.

# Identify the offending update (no Out-GridView available)
(Get-WsusServer).SearchUpdates('4577586') | Select Title
# Find its Id
(Get-WsusServer).SearchUpdates('4577586') | Select-Object -First 3 | 
Select -Last 1 -ExpandProperty Id
# Detete (no output if ok)
(Get-WsusServer).DeleteUpdate('20bd2d6b-26a9-4ddd-8a3f-04a79b683c1f')

I could reimport the same file using the correct Id, happy days 😎

Windows Defender Attack Surface Reduction (ASR) Rules module

$
0
0

I’m pleased to share with you a new #PowerShell module about Windows Defender Attack Surface Reduction (ASR) Rules 🚀

After seeing what Palantir did about their ASR telemetry and the content of the following repository, I thought that we need a more “PowerShell-friendly” way to view and set Windows Defender Attack Surface Reduction (ASR) rules.

It seems that I’m not the first person to have this idea and you can find an another implementation of this idea on the PowerShell gallery here.

If you don’t know anything about Windows Defender Attack Surface Reduction, I’d recommend that you watch this 6 minutes long video Susan Bradley made

My approach is slightly different than the 2 other PowerShell code implementations/repositories I mentioned above. I don’t provide any graphical interface (GUI), although you can for sure use the built-in Out-GridView cmdlet to send the output and inspect it in a GUI. I propose to have 3 functions that you can use to bind properly using the pipeline and made an effort to have all the parameters data being discoverable using the TAB key.

Let’s see some practical examples in a video:

I’d like to point out another useful resource to test the defense measures and configuration.

What else?
If you encounter an issue with this module, you’re welcome to open an issue in the github repo with this link.

Last but not least. Let’s say you’ve configured some ASR rules using GPO but not all of them. The Get-ASRRuleConfig function is able to display the effective rules that apply (GPO or local and if GPO, GPO wins over local). But the Set-ASRRuleConfig is only able to set the local rules. It cannot touch GPO rules. If you use it to set a rule that is already managed by GPO, it will work on the local value only. If you then use back Get-ASRRuleConfig, you get the results of what’s effective. Remember GPO wins.

How do I get started?

Find-Module -Name ASRRules -Repository PSGallery
Save-Module -Name ASRRules -Repository PSGallery -Path ~/Downloads

Import-Module ~/Downloads/ASRRules/1.0.0/ASRRules.psd1 -Force -Verbose

# if in PS 7.x and there's a complaint about the required module,
Import-Module -Name ConfigDefender -Force
Import-Module ~/Downloads/ASRRules/1.0.0/ASRRules.psd1 -Force -Verbose

Enjoy 😎

Viewing all 281 articles
Browse latest View live