Discover installed and potentially malicious browser extensions

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