Created
September 22, 2017 05:27
-
-
Save gravcat/fc008df03d6c33cb2bdb0f8d8aaf9877 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# @manojampalam - authored initial script | |
# @friism - Fixed issue with invalid SDDL on Set-Acl | |
# @manojampalam - removed ntrights.exe dependency | |
# @bingbing8 - removed secedit.exe dependency | |
$scriptpath = $MyInvocation.MyCommand.Path | |
$scriptdir = Split-Path $scriptpath | |
$sshdpath = Join-Path $scriptdir "sshd.exe" | |
$sshagentpath = Join-Path $scriptdir "ssh-agent.exe" | |
$logsdir = Join-Path $scriptdir "logs" | |
$sshdAccount = "NT SERVICE\SSHD" | |
$sshdSid = "S-1-5-80-3847866527-469524349-687026318-516638107-1125189541" | |
#Idea borrowed from https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0 | |
Add-Type @' | |
using System; | |
namespace MyLsaWrapper | |
{ | |
using System.Runtime.InteropServices; | |
using System.Security; | |
using System.ComponentModel; | |
using System.Security.Principal; | |
using LSA_HANDLE = IntPtr; | |
[StructLayout(LayoutKind.Sequential)] | |
struct LSA_OBJECT_ATTRIBUTES | |
{ | |
internal int Length; | |
internal IntPtr RootDirectory; | |
internal IntPtr ObjectName; | |
internal int Attributes; | |
internal IntPtr SecurityDescriptor; | |
internal IntPtr SecurityQualityOfService; | |
} | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
struct LSA_UNICODE_STRING | |
{ | |
internal ushort Length; | |
internal ushort MaximumLength; | |
[MarshalAs(UnmanagedType.LPWStr)] | |
internal string Buffer; | |
} | |
sealed class Win32Sec | |
{ | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] | |
internal static extern uint LsaOpenPolicy( | |
LSA_UNICODE_STRING[] SystemName, | |
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, | |
int AccessMask, | |
out IntPtr PolicyHandle | |
); | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] | |
internal static extern uint LsaAddAccountRights( | |
LSA_HANDLE PolicyHandle, | |
IntPtr pSID, | |
LSA_UNICODE_STRING[] UserRights, | |
int CountOfRights | |
); | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] | |
internal static extern uint LsaRemoveAccountRights( | |
LSA_HANDLE PolicyHandle, | |
IntPtr pSID, | |
bool AllRights, | |
LSA_UNICODE_STRING[] UserRights, | |
int CountOfRights | |
); | |
[DllImport("advapi32")] | |
internal static extern int LsaNtStatusToWinError(int NTSTATUS); | |
[DllImport("advapi32")] | |
internal static extern int LsaClose(IntPtr PolicyHandle); | |
} | |
internal sealed class Sid : IDisposable | |
{ | |
public IntPtr pSid = IntPtr.Zero; | |
public System.Security.Principal.SecurityIdentifier sid = null; | |
public Sid(string account) | |
{ | |
try { sid = new SecurityIdentifier(account); } | |
catch { sid = (SecurityIdentifier)(new NTAccount(account)).Translate(typeof(SecurityIdentifier)); } | |
Byte[] buffer = new Byte[sid.BinaryLength]; | |
sid.GetBinaryForm(buffer, 0); | |
pSid = Marshal.AllocHGlobal(sid.BinaryLength); | |
Marshal.Copy(buffer, 0, pSid, sid.BinaryLength); | |
} | |
public void Dispose() | |
{ | |
if (pSid != IntPtr.Zero) | |
{ | |
Marshal.FreeHGlobal(pSid); | |
pSid = IntPtr.Zero; | |
} | |
GC.SuppressFinalize(this); | |
} | |
~Sid() { Dispose(); } | |
} | |
public sealed class LsaWrapper : IDisposable | |
{ | |
enum Access : int | |
{ | |
POLICY_READ = 0x20006, | |
POLICY_ALL_ACCESS = 0x00F0FFF, | |
POLICY_EXECUTE = 0X20801, | |
POLICY_WRITE = 0X207F8 | |
} | |
const uint STATUS_ACCESS_DENIED = 0xc0000022; | |
const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; | |
const uint STATUS_NO_MEMORY = 0xc0000017; | |
const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034; | |
const uint STATUS_NO_MORE_ENTRIES = 0x8000001a; | |
IntPtr lsaHandle; | |
public LsaWrapper() : this(null) { } // local system if systemName is null | |
public LsaWrapper(string systemName) | |
{ | |
LSA_OBJECT_ATTRIBUTES lsaAttr; | |
lsaAttr.RootDirectory = IntPtr.Zero; | |
lsaAttr.ObjectName = IntPtr.Zero; | |
lsaAttr.Attributes = 0; | |
lsaAttr.SecurityDescriptor = IntPtr.Zero; | |
lsaAttr.SecurityQualityOfService = IntPtr.Zero; | |
lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES)); | |
lsaHandle = IntPtr.Zero; | |
LSA_UNICODE_STRING[] system = null; | |
if (systemName != null) | |
{ | |
system = new LSA_UNICODE_STRING[1]; | |
system[0] = InitLsaString(systemName); | |
} | |
uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle); | |
if (ret == 0) return; | |
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); | |
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); | |
} | |
public void AddPrivilege(string account, string privilege) | |
{ | |
uint ret = 0; | |
using (Sid sid = new Sid(account)) | |
{ | |
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; | |
privileges[0] = InitLsaString(privilege); | |
ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.pSid, privileges, 1); | |
} | |
if (ret == 0) return; | |
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); | |
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); | |
} | |
public void RemovePrivilege(string account, string privilege) | |
{ | |
uint ret = 0; | |
using (Sid sid = new Sid(account)) | |
{ | |
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; | |
privileges[0] = InitLsaString(privilege); | |
ret = Win32Sec.LsaRemoveAccountRights(lsaHandle, sid.pSid, false, privileges, 1); | |
} | |
if (ret == 0) return; | |
if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); | |
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); | |
} | |
public void Dispose() | |
{ | |
if (lsaHandle != IntPtr.Zero) | |
{ | |
Win32Sec.LsaClose(lsaHandle); | |
lsaHandle = IntPtr.Zero; | |
} | |
GC.SuppressFinalize(this); | |
} | |
~LsaWrapper() { Dispose(); } | |
// helper functions: | |
static LSA_UNICODE_STRING InitLsaString(string s) | |
{ | |
// Unicode strings max. 32KB | |
if (s.Length > 0x7ffe) throw new ArgumentException("String too long"); | |
LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING(); | |
lus.Buffer = s; | |
lus.Length = (ushort)(s.Length * sizeof(char)); | |
lus.MaximumLength = (ushort)(lus.Length + sizeof(char)); | |
return lus; | |
} | |
} | |
public class LsaWrapperCaller | |
{ | |
public static void AddPrivilege(string account, string privilege) | |
{ | |
using (LsaWrapper lsaWrapper = new LsaWrapper()) | |
{ | |
lsaWrapper.AddPrivilege(account, privilege); | |
} | |
} | |
public static void RemovePrivilege(string account, string privilege) | |
{ | |
using (LsaWrapper lsaWrapper = new LsaWrapper()) | |
{ | |
lsaWrapper.RemovePrivilege(account, privilege); | |
} | |
} | |
} | |
} | |
'@ | |
$references = @() | |
if(($psversiontable.Containskey("psedition")) -and ($psversiontable.PSEdition -ieq "desktop")) | |
{ | |
$references = "System.Security.Principal.Windows", "Microsoft.Win32.Primitives" | |
} | |
try { | |
$null = [MyLsaWrapper.LsaWrapperCaller] | |
} | |
catch { | |
$types = Add-Type $definition -ref $references -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | |
} | |
#Add-Type $definition | |
function Add-Privilege | |
{ | |
param( | |
[ValidateNotNullOrEmpty()] | |
[string] $Account, | |
[ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")] | |
[string] $Privilege | |
) | |
[MyLsaWrapper.LsaWrapperCaller]::AddPrivilege($Account, $Privilege) | |
} | |
if (-not (Test-Path $sshdpath)) { | |
throw "sshd.exe is not present in script path" | |
} | |
if (Get-Service sshd -ErrorAction SilentlyContinue) | |
{ | |
Stop-Service sshd | |
sc.exe delete sshd 1>$null | |
} | |
if (Get-Service ssh-agent -ErrorAction SilentlyContinue) | |
{ | |
Stop-Service ssh-agent | |
sc.exe delete ssh-agent 1>$null | |
} | |
New-Service -Name ssh-agent -BinaryPathName $sshagentpath -Description "SSH Agent" -StartupType Manual | Out-Null | |
cmd.exe /c 'sc.exe sdset ssh-agent D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)' | |
New-Service -Name sshd -BinaryPathName $sshdpath -Description "SSH Daemon" -StartupType Manual -DependsOn ssh-agent | Out-Null | |
sc.exe config sshd obj= $sshdAccount | |
sc.exe privs sshd SeAssignPrimaryTokenPrivilege | |
Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege | |
Add-Privilege -Account $sshdSid -Privilege SeServiceLogonRight | |
if(-not (test-path $logsdir -PathType Container)) | |
{ | |
$null = New-Item $logsdir -ItemType Directory -Force -ErrorAction Stop | |
} | |
$rights = [System.Security.AccessControl.FileSystemRights]"Read, Write" | |
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($sshdAccount, $rights, "ContainerInherit,ObjectInherit", "None", "Allow") | |
$acl = Get-Acl -Path $logsdir | |
$Acl.SetAccessRule($accessRule) | |
Set-Acl -Path $logsdir -AclObject $acl | |
Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment