CVE-2021-21270 (Other, published 2021-01-22T18:15Z)
Description: value: OctopusDSC is a PowerShell module with DSC resources that can be used to install and configure an Octopus Deploy Server and Tentacle agent. In OctopusDSC version 4.0.977 and earlier a customer API key used to connect to Octopus Server is exposed via logging in plaintext. This vulnerability is patched in version 4.0.1002.
Severity: LOW (CVSS v3 base score: 5.5)
Weakness: CWE-319 \u2014 Cleartext Transmission of Sensitive Information
The product transmits sensitive or security-critical data in cleartext in a communication channel that can be sniffed by unauthorized actors.
Repository: https://github.com/OctopusDeploy/OctopusDSC
Files changed:
OctopusDSC/DSCResources/cTentacleAgent/cTentacleAgent.psm1
OctopusDSC/OctopusDSCHelpers.ps1
Commit message: [Bug fix] Prevent exposing API key secret in stdout logging (#270)

* remove Write-Verbose line

* use masking function in commands

* simplify masking function

Vulnerable code:
    Write-Verbose "Registering with arguments: $registerArguments"
Function Get-MaskedOutput
{
    $reg = [System.Text.RegularExpressions.RegEx]::new("--masterkey|--password|--license",
                [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
    if(($arguments -match "--masterkey|--password|--license"))
    {
        for($x=0;$x -lt $arguments.count; $x++)
        {
            if(($arguments[$x] -match "--masterkey|--password|--license|--trust|--remove-trust|--apikey|--password|--pw|--pfx-password|--proxyPassword"))
            {
                $arguments[$x+1] = $arguments[$x+1] -replace ".", "*"
            }
        $out = $arguments
    }
    elseif(($arguments -match "password|pwd"))
    {
        $out = $arguments -replace "(password|pwd)=[^;|`"]*", "`$1=********"
    }
    else
    {
        $out = @("************************")
    return $out
    # todo: fix this up
    if ((($cmdArgs -match "masterkey|password|license|pwd=").Count -eq 0)) {
        Write-Verbose "Executing command '$octopusServerExePath $($cmdArgs -join ' ')'"
    } else {
        $copiedarguments = @() # hack to pass a copy of the array, not a reference
        $copiedarguments += $cmdArgs
        $maskedarguments = Get-MaskedOutput $copiedarguments
        Write-Verbose "Executing command '$octopusServerExePath $($maskedarguments -join ' ')'"
    }
    # todo: fix this up
    if ((($cmdArgs -match "masterkey|password|license|pwd=").Count -eq 0)) {
        Write-Verbose "Executing command '$tentacleExePath $($cmdArgs -join ' ')'"
    } else {
        $copiedarguments = @() # hack to pass a copy of the array, not a reference
        $copiedarguments += $cmdArgs
        $maskedarguments = Get-MaskedOutput $copiedarguments
        Write-Verbose "Executing command '$tentacleExePath $($maskedarguments -join ' ')'"
    }

Fixed code:
function Get-MaskedOutput {
    $singleAsterixArgs = "--masterkey|--license|--licence|--trust|--password|--remove-trust|--apikey|--pw|--pfx-password|--proxyPassword";
    $connectionStringArgs = "--connectionstring";
    # Scrub sensitive values
    for($x=0; $x -lt $arguments.count; $x++) {
        if($arguments[$x] -match $singleAsterixArgs) {
            $arguments[$x+1] = "**********"
        } elseif($arguments[$x] -match $connectionStringArgs) {
            $arguments[$x+1] = $arguments[$x+1] -replace "(password|pwd)=[^;|`"]*", "`$1=********"
    return $arguments
}

function Write-VerboseWithMaskedCommand ($cmdArgs) {
    $copiedarguments = @() # hack to pass a copy of the array, not a reference
    $copiedarguments += $cmdArgs
    $maskedarguments = Get-MaskedOutput $copiedarguments
    Write-Verbose "Executing command '$octopusServerExePath $($maskedarguments -join ' ')'"

    Write-VerboseWithMaskedCommand($cmdArgs);


    Write-VerboseWithMaskedCommand($cmdArgs);