Malicious browser extensions is a big problem.
A later blog post will talk more on this matter and recommend implementing extension whitelisting.
But before you block all extensions, you would probably want to make an inventory of extensions, that your users have installed.
From this you can build an initial list of whitelisted extensions to avoid preventing the use of useful, harmless extensions.
You might also just want to make an inventory to look specifically for extensions, that are known to be malicious.
This Powershell script can assist you.
The script will:
Enumerate all user profiles on the computer
Enumerate all extensions of all browser profiles in Edge and Chrome in all Windows user profiles
Gather information from the extensions' manifest.json
Gather extra information if needed and possible from the Google extensions web store
Check each extension against a list of known malicious extensions
(CREDITS: List of malicious extensions: https://github.com/mallorybowes/chrome-mal-ids)
It outputs the results from the $extensions variable.
I will leave it up to you, how you want use the data from $extensions, whether you want to add the data to a SQL DB, or as CSV files in a share or something else 😎
As the script traverses all user profiles, it will need elevated permissions to run.
If you run this on a Citrix server, for example, make sure to run it assigned low priority so as not to overload the Citrix server! It could run for many hours, depending on the number of user profiles.
#requires -version 5
<#
.SYNOPSIS
Script to enumerate all extensions installed on a computer
gather information about the extensions
and check them against a list of known malicious extensions
.DESCRIPTION
The script will:
Enumerate all user profiles on the computer
Enumerate all extensions of all browser profiles in Edge and Chrome in all Windows user profiles
Gather information from the extensions' manifest.json
Gather extra information if needed and possible from the Google extensions web store
Check each extension against a list of known malicious extensions
CREDITS: List of malicious extensions: https://github.com/mallorybowes/chrome-mal-ids
.INPUTS
None.
.OUTPUTS
Outputs contents of $extensions variable
.NOTES
Version: 1.0
Author: Martin Jeppesen, https://www.avantia.dk
Creation Date: 2022-09-21
Purpose/Change: Initial script development
#>
#----------------------------------------------------------[Declarations]----------------------------------------------------------
#Create object with list of known malicious extensions
#From https://github.com/mallorybowes/chrome-mal-ids
$tempFolder = $env:TEMP
$csvFile = (Invoke-WebRequest -Uri "https://raw.githubusercontent.com/mallorybowes/chrome-mal-ids/master/current-list-meta.csv").content
$csvFile | Out-File $tempFolder\CSVFile.csv
$maliciousExtensions = Import-Csv $tempFolder\CSVFile.csv
Remove-Item $tempFolder\CSVFile.csv
#Prepopulate extensions-object with known built-in Chrome extensions
$extensions = @()
$extensions += [PSCustomObject]@{
"WebStore" = "Google"
"ID" = "pkedcjkdefgpdelpbcmbmeomcjbeemfm"
"Publisher" = "Google"
"Name" = "Chrome Cast"
"FindCount" = 0
"URI" = "https://chrome.google.com/webstore/detail/pkedcjkdefgpdelpbcmbmeomcjbeemfm"
"KnownMalicious" = $false
"KnownMaliciousSource" = ""
"KnownMaliciousArticle" = ""
}
$extensions += [PSCustomObject]@{
"WebStore" = "Google"
"ID" = "mhjfbmdgcfjbbpaeojofohoefgiehjai"
"Publisher" = "Google"
"Name" = "Chrome PDF Viewer"
"FindCount" = 0
"URI" = "https://chrome.google.com/webstore/detail/mhjfbmdgcfjbbpaeojofohoefgiehjai"
"KnownMalicious" = $false
"KnownMaliciousSource" = ""
"KnownMaliciousArticle" = ""
}
$extensions += [PSCustomObject]@{
"WebStore" = "Google"
'ID' = "nmmhkkegccagdldgiimedpiccmgmieda"
'Publisher' = "Google"
'Name' = "Google Wallet"
'FindCount' = 0
'URI' = "https://chrome.google.com/webstore/detail/nmmhkkegccagdldgiimedpiccmgmieda"
"KnownMalicious" = $false
"KnownMaliciousSource" = ""
"KnownMaliciousArticle" = ""
}
#-----------------------------------------------------------[Functions]------------------------------------------------------------
Function GetExtensionInfo ($extensionID)
{
$extensionName = "YetToBeResolved"
$extensionURI = "YetToBeResolved"
Try
{
#Search for the extension in the Chrome Store
$uRI = "https://chrome.google.com/webstore/detail/"
$data = Invoke-WebRequest -Uri ($URI + $extensionID) | select Content
$data = $data.Content
# Regex which pulls the title from og:title meta property
$title = [regex] '(?<=og:title" content=")([\S\s]*?)(?=">)'
$extensionName = $title.Match($data).value.trim()
$publisher = [regex] '(?<=<span class="e-f-Me">fra )([\S\s]*?)(?=<\/span>)'
$extensionPublisher = $publisher.Match($data).value.trim()
If (-not $extensionPublisher)
{
$publisher = [regex] '(?<=fra <a target="_blank" class="e-f-y" href=")([\S\s]*?)(?=" rel="nofollow">)'
$extensionPublisher = $publisher.Match($data).value.trim()
}
$extensionURI = "https://chrome.google.com/webstore/detail/$extensionID"
}
#If the extension's ID is not found in Chrome Store:
#It is probably one of the built-in extensions declared at the top
#In that case it would have been found in this line: If ($extensions | Where-Object {$_.ID -eq $extension})
#If it is NOT one of the built-in, and it is NOT found in Store, this will catch it
Catch
{
$extensionPublisher = "UNKNOWN"
$extensionName = "UNKNOWN"
$extensionURI = "Could not be found"
}
$extensionInfo = [PSCustomObject]@{
"ExtensionID" = $extensionID
"Publisher" = $extensionPublisher
"Name" = $extensionName
"URL" = $extensionURI
}
Return $extensionInfo
}
#-----------------------------------------------------------[Execution]------------------------------------------------------------
#Get list of user profiles on local computer
$userProfilePaths = (Get-WmiObject win32_userprofile | Where-Object {$_.LocalPath -like "C:\Users\*"}).LocalPath
#Array with Chrome and Edge for use in paths
$browsers = @(
[pscustomobject]@
[pscustomobject]@{BrowserDeveloper="Google"; BrowserName="Chrome"}
[pscustomobject]@{BrowserDeveloper="Mozilla"; BrowserName="Firefox"}
)
#Discovery of extensions for each profile of each browser of each Windows profile
foreach ($userProfilePath in $userProfilePaths)
{
foreach ($browser in $browsers)
{
$browserDeveloper = $browser.BrowserDeveloper
$browserName = $browser.BrowserName
#Gathering of extension info for Chromium browsers
If (($browserName -eq "Chrome") -or ($browserName -eq "Edge"))
{
$browserUserData = $userProfilePath + "\AppData\Local\$BrowserDeveloper\$browserName\User Data\"
$browserUserDataSubfolders = (Get-ChildItem -Directory -Path $browserUserData).FullName
#Find subfolders of the browser's User Data folder, that are profiles containing an extension-folder
Foreach ($browserUserDataSubfolder in $browserUserDataSubfolders)
{
#If the subfolder contains a subfolder named "Extensions", it's a Profile folder, and we will scan it for extensions
if (Get-ChildItem -Path $browserUserDataSubfolder -Depth 1 -Name "Extensions")
{
$extensionFoldersPaths = (Get-ChildItem -Directory -Path "$browserUserDataSubfolder\Extensions").FullName
$extensionFoldersNames = (Get-ChildItem -Directory -Path "$browserUserDataSubfolder\Extensions").Name
$extensionFolders = Get-ChildItem -Directory -Path "$browserUserDataSubfolder\Extensions"
foreach ($extensionFolderPath in $extensionFolders)
{
$extensionID = $extensionFolderPath.Name
$extensionPublisher = "UNKNOWN"
$extensionName = "UNKNOWN"
$extensionURI = "UNKNOWN"
$extensionWebStore = "UNKNOWN"
#Check if the extension is already in the inventory and increase Findcount if so
If ($extensions | Where-Object {$_.ID -eq $extensionID})
{
#Recurse through the known extensions to get to the index-value, where the extension is stored
for ($i = 0; $i -le $extensions.GetUpperBound(0); $i++)
{
#When the indexvalue of the extensions object is found, increment FindCount
If ($extensions[$i].ID -eq $extensionID)
}
}
#If the extensions is not already in the inventory, find more info about the extension and add it to inventory
Else
{
#Check Extension against list of known malicious extensions
$maliciousExtensionInfoSource = $null
$maliciousExtensionInfoArticle = $null
$knownMalicious = $false
$maliciousExtensionInfo = $maliciousExtensions -match $extensionID
#If extensionID matches a known malicious extension, add information about it to extension-info.
If ($maliciousExtensionInfo)
{
$knownMalicious = $true
$maliciousExtensionInfoSource = $maliciousExtensionInfo.Source
$maliciousExtensionInfoArticle = $maliciousExtensionInfo.Article
Write-Warning "Found $extensionID which is known malicious. See infolinks for more info"
}
#Retreive information about extension from manifest.json file
$extensionFolderFullPath = $extensionFolderPath.FullName
$extensionVersionFolder = (Get-ChildItem -Directory -Path $extensionFolderFullPath\*.*).FullName
$extensionManifestFile = Get-ChildItem -Path $extensionVersionFolder -Name "manifest.json" -ErrorAction Continue
$manifestExtensionInfo = Get-Content -Path "$extensionVersionFolder\$extensionManifestFile" | ConvertFrom-Json | Select author,name,description,update_url
$extensionPublisher = $manifestExtensionInfo.author
$extensionName = $manifestExtensionInfo.name
#If it is an extension from the Microsoft extensions store, add this information
If ($manifestExtensionInfo.update_url -like "*microsoft.com*")
{
$extensionWebStore = "Microsoft"
$extensionURI = "https://microsoftedge.microsoft.com/addons/detail/$extensionID"
}
#If it is an extension from the Google extensions store, add this information
Elseif ($manifestExtensionInfo.update_url -like "*google.com*")
{
$extensionWebStore = "Google"
#For Google store extensions, we can get more information from the website, if some info is missing in the manifest.json file
If ((!$extensionPublisher) -or ($extensionPublisher -like "*__MSG*") -or (!$extensionName) -or ($extensionName -like "*__MSG*"))
{
#Call the function GetExtensionInfo to retreive info from Google Web Store
$chromeStoreExtensionInfo = GetExtensionInfo($extensionID)
#Add the retreived information to the extensions info
$extensionPublisher = $chromeStoreExtensionInfo.Publisher
$extensionName = $chromeStoreExtensionInfo.Name
$extensionURI = $chromeStoreExtensionInfo.URL
}
#If no info is missing in the manifest.json file, just add a link to the extension on the Google extension store
Else
{
$extensionURI = "https://chrome.google.com/webstore/detail/$extensionID"
}
}
#If the extension is neither from Microsoft or Google extension stores, just add "UNKNOWN" to extension info
Else
{
$extensionWebStore = "UNKNOWN"
$extensionURI = "UNKNOWN"
}
#Add the newly found extension to the extensions-object
$extensions += [PSCustomObject]@{
"WebStore" = $extensionWebStore
"ID" = $extensionID
"Publisher" = $extensionPublisher
"Name" = $extensionName
"FindCount" = 1
"URI" = $extensionURI
"KnownMalicious" = $knownMalicious
"KnownMaliciousSource" = $maliciousExtensionInfoSource
"KnownMaliciousArticle" = $maliciousExtensionInfoArticle
}
}
}
}
}
}
#Gathering of extension info from Firefox
If ($browserName -eq "Firefox")
{
$browserUserData = $userProfilePath + "\AppData\Roaming\Mozilla\Firefox\Profiles\"
$browserUserDataSubfolders = (Get-ChildItem -Directory -Path $browserUserData -ErrorAction Continue).FullName
Foreach ($browserUserDataSubfolder in $browserUserDataSubfolders)
{
$extensionManifestFilePath = $browserUserDataSubfolder+"\addons.json"
If (Test-Path -Path $extensionManifestFilePath)
{
$manifestInfo = Get-Content -Path $extensionManifestFilePath | ConvertFrom-Json
$manifestExtensionInfo = $manifestInfo.addons
foreach ($addon in $manifestExtensionInfo)
{
$extensionID = $addon.id
$extensionPublisher = $addon.creator.name
$extensionName = $addon.name
$extensionDescription = $addon.description
$extensionURI = $addon.sourceURI
#Check if the extension is already in the inventory and increase Findcount if so
If ($extensions | Where-Object {$_.ID -eq $extensionID})
{
#Recurse through the known extensions to get to the index-value, where the extension is stored
for ($i = 0; $i -le $extensions.GetUpperBound(0); $i++)
{
#When the indexvalue of the extensions object is found, increment FindCount
If ($extensions[$i].ID -eq $extensionID)
}
}
#If the extensions is not already in the inventory, add it to inventory
Else
{
#Add the newly found extension to the extensions-object
$extensions += [PSCustomObject]@{
"WebStore" = "Mozilla"
"ID" = $extensionID
"Publisher" = $extensionPublisher
"Name" = $extensionName
"FindCount" = 1
"URI" = $extensionURI
"KnownMalicious" = $false
"KnownMaliciousSource" = "Firefox addons are not in the list of malicious extensions"
"KnownMaliciousArticle" = "Firefox addons are not in the list of malicious extensions"
}
}
}
}
}
}
}
}
$extensions