New module for Malware Bazaar API

After this post in August 2021 about MALWARE Bazaar “Download daily malware batches” on the SANS InfoSec Handlers Diary Blog, I thought that a PowerShell module using the API would be welcome.

I’m releasing the first 13 functions that perform a GET from the API without needing a private key:

Let’s see what it can do:

You can check if a particular malware sample is known to MalwareBazaar by query the API for the corresponding hash

Get-MalwareBazaarSampleHash -Hash '7de2c1bf58bce09eecc70476747d88a26163c3d6bb1d85235c24a558d1f16754' -Verbose

You can get a list of malware samples (max 1’000) associated with a specific tag

Get-MalwareBazaarSampleTag -Tag TrickBot -Limit 50 |ogv

You can get a list of recent malware samples (max 1’000) associated with a specific signature

Get-MalwareBazaarSampleSignature -Signature TrickBot -Limit 50

You can get a list of recent malware samples (max 1’000) having a specific filetype

Get-MalwareBazaarSampleFileType -FileType elf -Limit 50 | ogv

You can get a list of recent malware samples (max 1’000) associated with a specific ClamAV signature

Get-MalwareBazaarSampleClamAVSignature -Signature 'Doc.Downloader.Emotet-7580152-0' -Limit 50 | ogv

You can get a list of malware samples (max 1’000) associated with a specific imphash

Get-MalwareBazaarSampleImpHash -Hash '45d579faec0eaf279c0841b2233727cf' -Limit 50 | ogv

You can get a list of malware samples (max 1’000) associated with a specific TLSH hash

Get-MalwareBazaarSampleTLSH -Hash '4FB44AC6A19643BBEE8766FF358AC55DBC13D91C1B4DB4FBC789AA020A31B05ED12350' -Limit 50 | ogv

You can get a list of malware samples (max 1’000) associated with a specific telfhash hash

Get-MalwareBazaarSampleTelfhash -Hash 'ea2106f51e7e58d9b7e4a400c29b5f623d5df13b299037a00463e93033abe466069c7a' -Limit 50|ogv

You can get a list of malware samples (PE executables only, max 1’000) that are having a specific icon using the icon’s dhash.

Get-MalwareBazaarSampleIconDhash -Hash '48b9b2b0e8c18c90' -Limit 50 | ogv

You can get a list of malware samples (max 1’000) associated with a specific YARA rule

Get-MalwareBazaarSampleYaraRule -RuleName 'win_remcos_g0' -Limit 50 | ogv

You can get a list of malware samples (max 100) that are using code signing certificate issued by a certain Certificate Authority (Issuer CN) or you can get a list of malware samples (max 100) that are signed with a code signing certificate that matches a certain Subject Common Name (CN)

Get-MalwareBazaarSampleCodeSigningCertificate -Issuer 'Sectigo RSA Code Signing CA' | ogv
# or
Get-MalwareBazaarSampleCodeSigningCertificate -Subject 'Ekitai Data Inc.' |ogv

You can dump the content of the MalwareBazaar Code Signing Certificate Blocklist (CSCB)

 Get-MalwareBazaarSampleCodeSigningCertificateBlockList | ogv

You can retrieve a list of malware samples added to MalwareBazaar within the last 60 minutes

Get-MalwareBazaarSampleRecent -By 'Limit'|ogv

The module is available on the PowerShell gallery.

If you see issues, you can open an issue on its github repo using this link.

Enjoy 😎

Advertisement

Don’t use Get-Eventlog anymore

  • Problem

When you type the following

Get-EventLog -Source Microsoft-Windows-Kernel-General -Newest 20 -LogName System -InstanceId 1 | Select -ExpandProperty Message 

You get

Possible detection of CVE: 2023-01-09T09:08:23.5000000Z
Additional Information: 2023-01-08T19:56:29.1492612Z
This Event is generated when an attempt to exploit a known vulnerability (2023-01-09T09:08:23.5000000Z) is detected.
This Event is raised by a User mode process.

It raises some questions and you may feel insecure and that you’ve a security incident 😦

  • Solution

False alarm !

 Get-Help Get-EventLog -Online

The help of the cmdlet clearly states the following:

Get-EventLog uses a Win32 API that is deprecated. The results may not be accurate. 

Source: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-eventlog?view=powershell-5.1

To view the real events’ message associated to the Kernel provider, you can

(Get-WinEvent -ListProvider 'Microsoft-Windows-Kernel-General').Events | ? Id -eq 1

To get the correct message, you need to use the Get-WinEvent cmdlet 🙂

Get-WinEvent -ProviderName 'Microsoft-Windows-Kernel-General' -MaxEvents 20 | ? Id -eq 1

Changing the PDF Reader

Context

The PDF reader has reached its end-of-life on June 6, 2022 (official announcement)

I was about to change the software by deploying its new major version from the same release channel but I encountered an undesired end-user experience. The deployment has been stopped because of the following behavior.

Issue

The Reader 2017 is being successfully replaced by the Reader 2020. If there’s a standard user logged on the computer, his pdf file association is broken.

Yes, I pulled the carpet under his feet but neither Microsoft, nor Adobe offer a decent solution to handle it and update it smoothly.

The end-user is being prompted.

Easy, it’s documented on this page. This great but it doesn’t fix the broken pdf file association.

I remembered a similar issue that I posted here in 2018.

Unfortunately it doesn’t help anymore. The security mechanism still sees that there’s something wrong under the UserChoice key. It’s reported in the event log and there’s a reset.

The irony here is that it’s Adobe Reader that resets it but to MSEdgePDF (D’Oh!)

Solution

My solution consists in not allowing a reset to be performed.

I’m setting a temporary Deny rule on the .pdf registry key.

# Set a Deny of user on .pdf key
$acl = Get-Acl Path $extRegKeyPath
$rule = New-Object System.Security.AccessControl.RegistryAccessRule (
$user,
([System.Security.AccessControl.RegistryRights]::CreateSubKey+[System.Security.AccessControl.RegistryRights]::ChangePermissions),
[System.Security.AccessControl.AccessControlType]::Deny
)
$null = $acl.AddAccessRule($rule)
Set-Acl Path $acl.Path AclObject $acl
view raw set-regdeny.ps1 hosted with ❤ by GitHub

The key is deleted by a logoff script (using reg.exe import to avoid an access denied) and the computer GPO that handles file association using the official guidance from Microsoft and Adobe restores everything beautifully at next logon.

Happy days, PowerShell saved the day again 😎

From MSRC API to ZDI chart

ZDI, a.k.a ZeroDayInitiative, has a nice chart about updates published by the MSRC

I wondered how I could get the same in a grid view with PowerShell…

#Requires -Module MsrcSecurityUpdates
(Get-MSRCCvrfDocument ID "$((Get-Date).ToString('yyyy-MMM',[System.Globalization.CultureInfo]'en-US'))").Vulnerability |
Foreach-Object {
$v = $_
$Disclosed = $Exploited = $null
$Disclosed = ([regex]'Publicly\sDisclosed:(?<D>(Yes|No));').Match("$(($v.Threats | Where-Object { $_.Type -eq 1}).Description.Value)") |
Select-Object ExpandProperty Groups| Select-Object Last 1 ExpandProperty Value
$Exploited = ([regex]'Exploited:(?<E>(Yes|No));').Match("$(($v.Threats | Where-Object { $_.Type -eq 1}).Description.Value)") |
Select-Object ExpandProperty Groups| Select-Object Last 1 ExpandProperty Value
[PSCustomObject]@{
CVEID = $v.CVE
Tag = $($v.Notes | Where-Object { $_.Type -eq 7}).Value
CNA = $($v.Notes | Where-Object {$_.Type -eq 8}).Value
Title = $v.Title.Value
Date = $($v.RevisionHistory | Select-Object First 1 ExpandProperty Date)
Revision = $($v.RevisionHistory | Select-Object First 1 ExpandProperty Number)
Severity = $( ($v.Threats | Where-Object { $_.Type -eq 3 }).Description | Select-Object ExpandProperty Value ErrorAction SilentlyContinue | Sort-Object Unique)
CVSS = '{0:N1}' -f $($v.CVSSScoreSets.BaseScore | Sort-Object Unique | ForEach-Object { [double]$_} | Sort-Object Descending | Select-Object First 1)
Public = $Disclosed
Exploited = $Exploited
Type = $( ($v.Threats | Where-Object { $_.Type -eq 0 }).Description | Select-Object ExpandProperty Value ErrorAction SilentlyContinue | Sort-Object Unique)
}
} |
Select-Object Property CVEID,Title,Severity,CVSS,Public,Exploited,Type |
Out-GridView

Here’s what the result looks like for February 2022:

About the DellBIOSProvider module and ConstrainedLanguage mode

  • Context

I’ve just started working with the DellBIOSProvider module available on the PowerShell Gallery and had to see how to integrate it smoothly in the environment so that it’s compatible with the Constrained language mode.

  • Issues

If I do:

Import-Module -name DellBIOSProvider -Force -Verbose

What could go wrong? 🙄
Well, it depends on what you do. If you go down to the Applocker rules path, it depends on the rules, their type, on what’s missing.
I’ve listed below a few common road blocks you may encounter:

  • psd1 or psm1 has a dedicated rule trusting/allowing it while the other doesn’t:
  • there is/are rule(s) to allow both .psd1 and psm1, but when it loads the dll (listed in the .psd1 manifest file), it fails because there’s a missing rule:

Import-Module : Could not load file or assembly ‘file:///C:\Program
Files\WindowsPowerShell\Modules\DellBIOSProvider\2.6.0\DellBIOSProvider.dll’ or one of its dependencies. Operation is
not supported. (Exception from HRESULT: 0x80131515)

  • there is/are rule(s) to allow both .psd1 and psm1, but the .psm1 uses dot sourcing and tries to load single .ps1 file that don’t have an allow rule:
  • this is what happens when you’ve rule(s) allowing it to load and it’s a 100% success
  • Solution

It appears that there are 2 solutions.

The 1rst one and the longest is about declaring rules that will allow any file contained in the module. Let’s have a look at the content of the module with the following command:

Get-AppLockerFileInformation -Path 'C:\Program Files\WindowsPowerShell\Modules\DellBIOSProvider\2.6.0\*' | ogv -PassThru

We can see above that the dll, ps1, psd1, psm1 and cat files are all signed 🙂
Only the txt and pdf files are unsigned. These 2 files are not loaded so we don’t care.

At this step, we can choose either to add either:
– a single Applocker rule for the Path
or
– a single Applocker rule containing all the files’ hashes
or
– a single Applocker rule trusting the Publisher
O=DELL INC, L=ROUND ROCK, S=TEXAS, C=US
or
– a mix of files’ hashes and publisher based rules

I’ve chosen the latest option because it’s the most precise. I’ve listed the rules in this XML policy file.

The Applocker GUI will allow you to create rules for dll, .psd1 and psm1 files if you copy them with a .ps1 file extension 😎

Let’s see the 2nd solution and the shortest one:

Dell provided a signed catalog file. It contains all the files’ hashes. To trust it, I only have to copy it to its system location. There are various ways of doing this listed on this page.

copy 'C:\Program Files\WindowsPowerShell\Modules\DellBIOSProvider\2.6.0\DellBIOSProvider.cat' "c:\Windows\system32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}"
Restart-Service CryptSvc -Force -Verbose

That’s all. It’s magic. You don’t need Applocker rules 😀

Group policies update/refresh without gpupdate.exe

  • Context

I was testing group policies, adding, removing them and using gpupdate.exe to apply changes. I messed with the Applocker gpo and set the PC in an unstable state.

I still had my PowerShell console opened but couldn’t use gpupdate.exe anymore. The Start menu wasn’t working anymore… 😦

Here’s what it looks like:

Usually, I’d just restart the computer and the transient state is cleared: either Applocker would work normally or would be disabled.

In this case, I couldn’t restart the computer because of Bitlocker. I was remote and the next time the laptop restarts it’d ask for a PIN. I couldn’t also suspend bitlocker for the next restart or simply disable it. Bad situation actually for Bitlocker, no UI, no cmdlet, no manage-bde.exe… (maybe I could have tried WMI/CIM).

  • Question: how would you refresh group policies when you cannot use gpupdate.exe
  • Solution:

I can still type some PowerShell in the opened console but bitlocker cmdlets don’t work.

It appears that there are 2 super hidden scheduled tasks responsible for refreshing group policies in the background.

Yes, super hidden because you cannot see them in the UI as an administrator even though you’ve enabled the “show hidden tasks” option:

Fortunately, the cmdlets of the ScheduledTasks module can interact with these super hidden tasks 🙂

Answer:

 Get-ScheduledTask -TaskPath '\Microsoft\Windows\GroupPolicy\' |
Where-Object { $_.Actions.Arguments -match 'computer' } |
Start-ScheduledTask
  • Conclusion

The above one-line code allowed me to run gpupdate.exe and saved me from having to restart the computer. Happy days 😎

Get CISA vulnerabilities report

There’s a new initiative from the US CyberSecurity & Infrastructure Security Agency.

They publish a list of known exploited vulnerabilities. Nice, isn’t it?

They publish a json version of the catalog. So I wanted a PowerShell function able to get the list of recently added vulnerabilities, the same way it’s presented in this news article from bleepingcomputer.com or this one.

Let me introduce

Get-Help Get-CISAVulnerabilitiesReport
Get-CISAVulnerabilitiesReport | Measure-Object
Get-CISAVulnerabilitiesReport -Last 3
Get-CISAVulnerabilitiesReport -StartDate (Get-Date).AddDays(-15) | ogv

Here’s the full code of the function, enjoy 🙂

Function Get-CISAVulnerabilitiesReport {
<#
.SYNOPSIS
Get known exploited vulnerabilities
.DESCRIPTION
Get the known exploited vulnerabilities catalog from CISA
.PARAMETER StartDate
Datetime object used to filter the catalog
.PARAMETER Last
Last number of entries in the catalog sorted by published date
.EXAMPLE
Get-CISAVulnerabilitiesReport
Get all the known exploited vulnerabilities from the catalog published by CISA
.EXAMPLE
Get-CISAVulnerabilitiesReport | Measure-Object
Get the count of all the known exploited vulnerabilities published in the catalog by CISA
.EXAMPLE
Get-CISAVulnerabilitiesReport -Last 3
Get the 3 most recent known exploited vulnerabilities from the catalog published by CISA
.EXAMPLE
Get-CISAVulnerabilitiesReport -StartDate (Get-Date).AddDays(-15)
Get the known exploited vulnerabilities from the catalog published by CISA over the last 15 days
#>
[CmdletBinding(DefaultParameterSetName='__AllParameterSets')]
Param(
[Parameter(ParameterSetName = 'ByDate')]
[datetime]$StartDate,
[Parameter(ParameterSetName = 'ByLast')]
[int32]$Last
)
Begin {}
Process {
$HT = @{
URI = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json'
ErrorAction = 'Stop'
UseBasicParsing = [switch]::Present
}
try {
$vuln = (Invoke-RestMethod @HT).vulnerabilities |
ForEach-Object Process {
[PSCustomObject]@{
CVEId = $_.cveID
Vendor = $_.vendorProject
ProductName = $_.product
Name = $_.vulnerabilityName
StartDate = ([datetime]$_.dateAdded)
Description = $_.shortDescription
ActionRequired = $_.requiredAction
DueDate = ([datetime]$_.dueDate)
}
}
} catch {
Write-Warning Message "Failed to get data from CISA because $($_.Exception.Message)"
}
if ($vuln) {
Switch ($PSCmdlet.ParameterSetName) {
'ByDate' {
$vuln | Where-Object { $_.StartDate -gt $StartDate }
break
}
'ByLast' {
$vuln | Sort-Object Property StartDate Descending | Select-Object First $Last
break
}
default {
$vuln
}
}
}
}
End {}
}

Default ParameterSetName

  • Context:

Let’s say you use an advanced feature – ParameterSetName – in your PowerShell code.

Let’s say you don’t specify a default ParameterSetName in the CmdletBinding and that some parameters don’t have an explicitly defined ParameterSetName.

What’s the name of the parameterset in this case?

  • Hands-on!

Here’s a sample code I propose to discover it.

Function Test-Param {
[CmdletBinding(DefaultParameterSetName='Set1')]
Param(
[Parameter(ParameterSetName = 'Set1')]
[switch]$Param1,
[Parameter(ParameterSetName = 'Set2')]
[switch]$Param2,
[switch]$Common
)
Begin {}
Process {
    Switch ($PSCmdlet.ParameterSetName) {
        'Set1' {
            $PSCmdlet.ParameterSetName
            break
        }
        'Set2' {
            $PSCmdlet.ParameterSetName
            break
        }
        default {
            $PSCmdlet.ParameterSetName
        }
    }
}
End {}
}

Let’s execute some code and see what we can uncover:

# Execute the function to see that it works
Test-Param

# Check the DefaultParameterSetName specified in the CmdletBinding
(gcm Test-Param).DefaultParameterSet

# Get the properties of the first parameter of the function
(gcm Test-Param).Parameters['Param1']

As you know gcm is the alias for Get-Command.

Everything looks good and is expected so far.
Now, let’s have a look at the 3rd parameter that doesn’t have any ParameterSetName defined

(gcm Test-Param).Parameters['Common']

Got it. It seems that when there’s no ParameterSetName defined, its name is: __AllParameterSets

Let’s say, I change the above function and omit the DefaultParameterSetName in the CmdletBinding:

At runtime, there’s an error thrown saying that the parameterSet is ambiguous.
Get-Command is still able to see the syntax although the function will fail at runtime.

Let’s use the default parameter name __AllParameterSets instead of Set1 and compare the syntax of the functions

Set1 syntax:

Set1

__AllParameterSets syntax:

__AllParameterSets
  • Conclusion

Using the default parameter name __AllParameterSets gives us a 3rd way to execute with the Common parameter alone. That parameter is valid and used as well by the 2 other ParameterSetNames I specified in the Param block.

Nice and subtle. PowerShell rock 😎

How to fix cve-2021-43890

Microsoft recently published the following vulnerability cve-2021-43890 that is currently exploited by malware like Emotet/Trickbot/Bazaloader.

If your computer doesn’t have access to the store, it may not be that straightforward to install the fixed universal app to all users of a Windows 10 computer.

If the computer is not vulnerable, it’ll tell you the above message.

If it installed the required patched universal app, it’ll say “Successfully provisionned Microsoft.DesktopAppInstaller”.

You can run the code in a scheduled tasked running under the System account. Any user that has an interactive session opened will get the new Appx in his account.

If there’s a local user profile but the user is not logged on, it’ll automatically get the updated appx after an interactive logon.

#Requires -RunAsAdministrator
[CmdletBinding()]
Param()
Begin {}
Process {
if ([version]'1.16.13405.0' -gt [version](Get-AppxPackage Name 'Microsoft.DesktopAppInstaller' ErrorAction SilentlyContinue).Version) {
$zip = (Join-Path Path $env:TEMP ChildPath 'Microsoft.DesktopAppInstaller_1.16.13405.0_8wekyb3d8bbwe.zip')
$zipFolder = "$($zip -replace '\.zip','')"
if (-not(Test-Path Path $zip)) {
$HT = @{
Uri = 'https://download.microsoft.com/download/6/6/8/6680c5b1-3fbe-4b70-8189-90ea08609563/Microsoft.DesktopAppInstaller_1.16.13405.0_8wekyb3d8bbwe.zip'
UseBasicParsing = $true
ErrorAction = 'Stop'
OutFile = $zip
}
try {
Invoke-WebRequest @HT
} catch {
Write-Warning Message "Failed to download zip because $($_.Exception.Message)"
}
}
if (Test-Path Path $zip) {
if ((Get-FileHash Path $zip).Hash -eq 'e79cea914ba04b953cdeab38489b3190fcc88e566a43696aaefc0eddba1af6ab' ) {
try {
Expand-Archive Path $zip DestinationPath (Split-Path $zipFolder Parent) Force ErrorAction Stop
} catch {
Write-Warning Message "Failed to unzip because $($_.Exception.Message)"
}
if ('Valid' -in (Get-ChildItem Path "$($zipFolder)\*" Include * Recurse Exclude '*.xml' | Get-AuthenticodeSignature |
Select-Object ExpandProperty Status | Sort-Object Unique)
) {
$HT = @{
Online = $true
PackagePath = Join-Path Path $zipFolder ChildPath 'Microsoft.DesktopAppInstaller_1.16.13405.0_8wekyb3d8bbwe.msixbundle'
SkipLicense = $true
ErrorAction = 'Stop'
}
try {
$r = Add-AppxProvisionedPackage @HT
if ($r.Online) {
Write-Verbose 'Successfully provisionned Microsoft.DesktopAppInstaller' Verbose
}
} catch {
Write-Warning Message "Failed to install Appx because $($_.Exception.Message)"
}
}
} else {
Write-Warning Message "Downloaded zip file thumbprint (SHA256) doesn't match"
}
} else {
Write-Warning Message "Zip file $($zip) not found"
}
} else {
Write-Verbose Message 'Current Microsoft.DesktopAppInstaller appx version is not vulnerable' Verbose
}
}
End {}

Update of Windows Defender Attack Surface Reduction (ASR) Rules module

Following the announcement of a new rule in this blog post about blocking vulnerable drivers, I’ve added it to the module 🙂

Get-ASRRuleConfig | Select Name,Action | ft -AutoSize
Get-ASRRuleData | ogv -PassThru | fl *
Get-ASRRuleData -Name 'Block abuse of exploited vulnerable signed drivers' | Get-ASRRuleConfig
Get-ASRRuleData -Name 'Block abuse of exploited vulnerable signed drivers' | Set-ASRRuleConfig -Mode AuditMode -WhatIf 
Get-ASRRuleData -Name 'Block abuse of exploited vulnerable signed drivers' | Set-ASRRuleConfig -Mode AuditMode -Verbose
Get-ASRRuleData -Name 'Block abuse of exploited vulnerable signed drivers' | Get-ASRRuleConfig     

That’all folks, have fun 😎