Break Free from VirusTotal with ReversingLabs Threat IntelWatch AMA Replay

How PowerShell Gallery simplifies attacks

Installation automation in the tool presents threat actors with one key link in the kill chain of a possible supply chain attack.

PowerShell Gallery

PowerShell is a familiar name in system administration and automation, especially for the Windows ecosystem. Part of that is due to the useful Powershell tools that are shared on the open-source PowerShell Gallery repository. However, the widespread use of PowerShell and its abuse by malicious actors — and the public nature of the PowerShell Gallery repo — make it a double-edged sword, providing easy to use tools for legitimate actors while simultaneously providing malicious actors with an attack vector that is difficult to mitigate.

One attack vector ReversingLabs (RL) researchers have observed in relation to PowerShell is command hijacking, in which PowerShell is used to enable so-called “clobbering” (a type of injection attack) while autoloading modules installed from the PowerShell Gallery repository. This allows third-party modules to register commands that may conflict with, and take precedence over, those registered by the system.

In such a scenario, attackers can intercept system command calls made by the user, inspect and modify the arguments or pass them to the original command for stealthiness. This kind of attack used to require attackers to get access to the target system and plant malicious files for the autoloading and clobbering to take place. PowerShell Gallery replaces that step with a simple Install-Module command, significantly simplifying one link in the kill chain of a possible attack.

[ See update video: Spectra Assure Community launches support for PowerShell ]

Texture of nougat, but a different flavor

First developed by Microsoft, PowerShell is a cross-platform command line shell and scripting language that has, in nearly two decades, become an important part of the system management toolchain. In many cases, PowerShell is used to automate processes on the level of the operating system. What makes PowerShell stand out is its use of .NET objects instead of pure text as arguments and outputs of its commands.

The use of .NET objects is far from being the only thing PowerShell has in common with the .NET platform. Almost a decade ago, the .NET package manager NuGet and the rest of the NuGet infrastructure, like NuGet Gallery, was leveraged to create the PowerShell Gallery package repository. NuGet had already established the format, protocol, and installation commands, which PowerShell Gallery adopted with minor modifications. In both cases, the .nupkg package format is used, which is essentially a .zip archive with a .nuspec metadata file and other files that implement the package functionality. 

In the case of PowerShell Gallery, a .psd1 file is added alongside the .nuspec file. It shares some fields with its NuGet counterpart, but contains some PowerShell specific fields of its own. The .psd1 files contain the fields that may declare exported functions, cmdlets and variables. The declaration may be replaced with a wildcard, in which case the RootModule and NestedModules are scanned for matching command names during module installation and those are the commands registered by the module.

How PowerShell allows silent clobbering

As is the case with most scripting languages, PowerShell allows “clobbering” command names. Clobbering is a property that allows for commands within a session to be overwritten by other implementations with the same name. This behavior can be seen in Figure 1, below.

ModuleB clobbers the Get-MyStatus command of ModuleA

Figure 1. ModuleB clobbers the Get-MyStatus command of ModuleA.

As seen in Figure 1, when the modules are imported using the Import-Module command, clobbering occurs silently, without warning the user. What is even more concerning is the fact that no command is treated differently when it comes to clobbering, even the system ones. This gives a lot of flexibility in managing the system, but also poses a great risk for misuse. The problematic behaviour can be avoided by passing the flag -NoClobber which then silently skips importing the commands which clobber the existing ones. The user is only warned of this happening when using -Verbose along with -NoClobber. 

To an extent, this behavior does make sense for the modules that users implement themselves or manually import. But, as will be shown later, it can be abused by attackers to silently clobber any command in the session.

The silent clobbering on module import is an intended functionality which doesn’t obstruct the users in clobbering whichever command they find necessary. On the other hand, the Install-Module command does check which commands are exported by the module and warns the user if those commands’ names are overlapping with the existing commands of modules already installed on the system.

To test this behaviour, RL researchers set up a local repository for PS Gallery packages and published ModuleA and ModuleB there. As it can be seen on Figure 2., a warning is displayed and ModuleB isn’t installed. Adding the flag -AllowClobber allows the installation without warning.

Installing ModuleB which clobbers a command of ModuleA raises a warning

Figure 2: Warning about installing ModuleB, which clobbers a ModuleA command.

This behavior may leave you under the impression that this is all sorted out. Users have to consciously allow clobbering when installing a package and they are simply not bothered with that information again unless they choose to import a module with -NoClobbering and -Verbose flags. You’ll soon see that that isn’t the case.

Autoloading: PowerShell imports for you

Before delving into how to circumvent the warning message during installation, while still clobbering any command in a user session, let’s get familiar with autoloading and command precedence. Getting to know these functionalities of PowerShell will make the final example easier to understand.

As previously mentioned, .psd1 files contain the fields that may declare exported functions, cmdlets and variables which are used to identify commands registered by the module. This is important because PowerShell performs autoloading as a feature. This means that when a user calls a command which isn’t yet registered in the current session, it looks for modules that register that command and loads the found module. Registered commands are tracked for modules defined in any of the locations listed in $env:PSModulePath (different from $env:PATH) which is where the Install-Module command places the installed modules. If multiple modules register the same command, there is a prioritization based on command type when choosing which one to load. The order is the following: alias, function, cmdlet, external executable, with aliases having the highest priority. Note here that a function takes precedence over a cmdlet with the same name.

To motivate the final example, let’s try to abuse this functionality to clobber a system command using Import-Module. Modules that implement core functionality are loaded at startup when the shell is opened. If another module implements a command with the same name, it won’t be loaded when that command is called because the command is already loaded. A caveat here is that when autoloading identifies the module implementing the called command, it doesn’t just load the implementation of the command which was called, but the entire module. This allows overwriting the core commands (e.g. Get-ChildItem) during autoloading of a custom command (e.g. Get-CustomStatus) - a perfect opportunity for a malicious actor. Precedence is still taken into account, but system commands have no special treatment

By writing a CustomModule and placing it in the folder where packages are installed (for now manually), it becomes available to be autoloaded. ls is an alias that points to the command Get-ChildItem, which in turn is a cmdlet by default. This allows for the CustomModule implementation of the command to take precedence because it is implemented as a function. This behaviour can be seen on Figure 3.

Autoloading a manually placed module which clobbers a system command

Figure 3: Autoloading a manually-placed module that clobbers a system command.

Dynamic modules in global scope: A free ticket

In the last example, the module files had to be manually placed in a directory in the $env:PSModulePath because installing a module that clobbers an existing command (Get-ChildItem) would raise a warning. There is a way around that, and it includes using the New-Module command.

The New-Module command creates a new dynamic module in memory, without saving it to the disk. This command can be used to define and export a function that will perform the clobbering, all while keeping it practically invisible to the Install-Module command. New-Module creates a module object that can be piped to the Import-Module command, which, as was shown, silently allows clobbering.

 As seen in Figure 4, the Scope for Import-Module has to be set to Global in order for the module to be available outside the caller’s session state. Caller’s session state in this case is the local session of the module CustomModuleLocal which is being autoloaded, and outside refers to the shell where Get-CustomStatus command is called. With that, the clobbering of the system command was successful, without raising any warnings.

Autoloading an installed module which clobbers a system command

Figure 4: Autoloading an installed module which clobbers a system command.

How to detect PowerShell Gallery misuse

Manually examining all external software for security risk is, in the very best case, quite difficult and unproductive for a business to do on their own. That alone is reason enough to leave that part of the job to tools developed by professionals for that purpose, like RL Spectra Assure. It performs static analysis of the package, extracts relevant information and combines it with insights by RL threat researchers and analysts. After scanning the PoC package on Spectra Assure Portal, the behaviors identified by the tool (Figure 5) point towards those abused in the last example.

Figure 5

Figure 5: Spectra Assure Portal detects the behaviors indicating abuse.

Another useful resource is RL’s Spectra Assure Community, where RL shares knowledge about the threats that can be found in open-source software, including packages published to the PowerShell Gallery repository (which just added additional support for PowerShell.) Browse the community page as shown in Figure 6, or search for a specific package, whichever best suits your needs. A behavior list similar to the one shown in Figure 5 can be found on the community page for each published package.

Spectra Assure community page supports PowerShell Gallery community

Figure 6. Spectra Assure community supports the PowerShell Gallery community.

It's useful tool — and also a double-edged sword

The PowerShell Gallery offers many useful tools to system administrators and developers for optimizing their workflow. Unfortunately, using such tools without vetting their content may lead to becoming a victim of a software supply chain attack.

The example described above demonstrates one form that such attacks might take:  command clobbering that is executed silently by a package installed from PowerShell Gallery. Such behavior is perceived as a risk, which is why modules which declare clobbering commands are not installed by default. However, as we illustrate above: by using dynamic modules together with autoloading, attackers can exploit this attack vector. 

This is why it is important for development organizations to deploy security tools to verify the safety of third-party software — both open source and proprietary — regardless of whether it is used only during development or in production environments. Mitigating software supply chain risks that are amplified by PowerShell Gallery and other public repositories is now a crucial part in protecting businesses from cyber threats.

See the update video: Spectra Assure Community launches support for PowerShell.

Back to Top