Windows Server for beginners. Part 16: Setting up SSH on Windows Server

All parts of the series “Windows Server for beginners”

WinRM from part 14 is a transport only Windows knows. An administrator with half of the servers on Linux, a macOS laptop and a habit of living in a terminal wants one ssh for everything. Since 2018 this has been possible: Microsoft ported OpenSSH to Windows, built the client into Windows 10 1809 and Windows Server 2019, made the server an installable component, and in Windows Server 2025 it comes preinstalled. This is real OpenSSH — the same code, the same configuration files, the same keys — with adjustments for the fact that there is no Unix underneath. This part is about those adjustments: where the configuration lives, who may sign in, how permissions on administrators' keys work, how to make PowerShell the shell, how to sign in with Kerberos and how to run over SSH the same PowerShell Remoting we ran over WinRM.


Installation

The ssh.exe client is in C:\Windows\System32\OpenSSH on any modern Windows. The server is a Feature on Demand. Listing the components needs an elevated console (without elevation Get-WindowsCapability answers "The requested operation requires elevation."):

PS C:\> Get-WindowsCapability -Online -Name 'OpenSSH*' | Select-Object Name, State

Name                          State
----                          -----
OpenSSH.Client~~~~0.0.1.0 Installed
OpenSSH.Server~~~~0.0.1.0 Installed

PS C:\> Get-Service sshd, ssh-agent | Select-Object Name, Status, StartType

Name       Status StartType
----       ------ ---------
ssh-agent Stopped  Disabled
sshd      Stopped    Manual

On Windows Server 2025 the server is installed but not running; on 2019 and 2022 it is installed with Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0. Let's turn it on:

PS C:\> Set-Service sshd -StartupType Automatic
PS C:\> Start-Service sshd
PS C:\> Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' | Select-Object DisplayName, Enabled, Profile

DisplayName               Enabled Profile
-----------               ------- -------
OpenSSH SSH Server (sshd)    True Private

The component creates the firewall rule for port 22 by itself, but in Windows Server 2025 the rule applies only to the Private profile. A domain member recognizes its network as the domain network (Get-NetConnectionProfile shows NetworkCategory = DomainAuthenticated), the rule doesn't cover it, and port 22 stays closed: sshd is listening, yet a connection from the workstation never gets through. Let's add the domain profile:

PS C:\> Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Profile Domain
PS C:\> Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' | Select-Object DisplayName, Enabled, Profile

DisplayName               Enabled Profile
-----------               ------- -------
OpenSSH SSH Server (sshd)    True  Domain

In Windows Server 2019 and 2022 Add-WindowsCapability creates the same rule for all profiles (Any) right away, and it's worth narrowing the same way. Add-WindowsCapability itself is run on the server's console: from a remote PowerShell session it answers "Access is denied.". On its first start sshd creates the C:\ProgramData\ssh folder: it puts the configuration there — a copy of C:\Windows\System32\OpenSSH\sshd_config_default — and generates the RSA, ECDSA and Ed25519 host keys. A check from the workstation:

PS C:\> ssh alice.admin@dc1.corp.example.com
The authenticity of host 'dc1.corp.example.com (192.168.1.10)' can't be established.
ED25519 key fingerprint is SHA256:k7Hq3mF9…
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'dc1.corp.example.com' (ED25519) to the list of known hosts.
alice.admin@dc1.corp.example.com's password:
Microsoft Windows [Version 10.0.26100.6584]
(c) Microsoft Corporation. All rights reserved.

corp\alice.admin@DC1 C:\Users\alice.admin>

A password sign-in, and the shell is cmd.exe. We'll fix both right away.


Where things live

Linux Windows What
/etc/ssh/sshd_config C:\ProgramData\ssh\sshd_config server configuration
— C:\Windows\System32\OpenSSH\sshd_config_default default configuration, the working one is created from it
/etc/ssh/ssh_host_*_key C:\ProgramData\ssh\ssh_host_*_key host keys
~/.ssh/authorized_keys C:\Users\<user>\.ssh\authorized_keys keys of a regular user
— C:\ProgramData\ssh\administrators_authorized_keys keys of administrators, one shared file
/etc/ssh/ssh_config, ~/.ssh/config C:\ProgramData\ssh\ssh_config, C:\Users\<user>\.ssh\config client configuration
/var/log/auth.log the OpenSSH/Operational event log or C:\ProgramData\ssh\logs\sshd.log log
/usr/lib/openssh/sftp-server sftp-server.exe in C:\Windows\System32\OpenSSH SFTP subsystem

%ProgramData% is usually C:\ProgramData, a hidden folder. The server configuration is short and almost entirely commented out: the commented lines show the default values. In Windows Server 2025 four directives are in effect:

AuthorizedKeysFile	.ssh/authorized_keys

Subsystem	sftp	sftp-server.exe

AllowGroups administrators "openssh users"

Match Group administrators
       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

AllowGroups lets in only members of two groups: Administrators and OpenSSH Users. The latter is the built-in group BUILTIN\OpenSSH Users (SID S-1-5-32-585) that appeared in Windows Server 2025 with the description "Members of this group may connect to this computer using SSH."; on domain controllers it lives, like the other built-in groups, in the domain's CN=Builtin container. A domain user without administrator rights can't sign in over SSH until added to this group. In Windows Server 2022 and 2019 the default configuration has no AllowGroups line, and any user can sign in.

The Match Group administrators block is the main difference from Linux. For members of the local Administrators group, keys are read not from the profile but from one shared file in ProgramData. The reason: an administrator controls the profile folder themselves, and if their account were compromised without administrator rights (say, through a process running as them without elevation), writing a key to ~/.ssh/authorized_keys would grant a sign-in with full rights. The file in ProgramData must be accessible only to SYSTEM (full control) and Administrators; sshd checks the permissions and won't take keys from a file anyone else has access to. A domain controller has no local Administrators group, but the built-in domain Administrators group takes its place, and the rule applies.

To let administrators keep keys in their profiles like everyone else, the block is removed. We'll keep it: one file is easier to control.

Names in sshd_config are Windows names: domain users and groups are written as domain\name with the short (NetBIOS) domain name, a name with spaces goes in quotes, and everything is in lower case, as Microsoft's documentation requires:

AllowGroups administrators "openssh users" "corp\ssh admins"
DenyUsers corp\guest

It's simpler, though, to leave AllowGroups alone and add the group you need to OpenSSH Users: on a member server Add-LocalGroupMember -Group 'OpenSSH Users' -Member 'CORP\SSH Admins', on domain controllers Add-ADGroupMember -Identity 'OpenSSH Users' -Members 'SSH Admins'. Changes to sshd_config take effect after Restart-Service sshd: the service reads the file when it starts.


Keys

A key pair on the workstation and an agent that holds it:

PS C:\> ssh-keygen -t ed25519 -C 'alice.admin@ws1'
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\alice.admin/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\alice.admin/.ssh/id_ed25519
Your public key has been saved in C:\Users\alice.admin/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:0ODW1wKq21CFjAN+xua1bDduuAjOgooPluX97+GfBfo alice.admin@ws1
...
PS C:\> ssh-add
Error connecting to agent: No such file or directory

On Windows ssh-agent is a service, and it's disabled by default; it has to be enabled from an elevated console:

PS C:\> Set-Service ssh-agent -StartupType Automatic
PS C:\> Start-Service ssh-agent
PS C:\> ssh-add
Identity added: C:\Users\alice.admin/.ssh/id_ed25519 (alice.admin@ws1)

Keys added to the agent are stored in the registry under the user's account, encrypted with Windows means (DPAPI); they survive a reboot and are available only to that user. Windows has no ssh-copy-id, so the public key goes to the server by hand. For an administrator, into the shared file, with the permissions fixed every time: a file created in C:\ProgramData\ssh inherits read access for Authenticated Users from the folder, and sshd won't accept such a file. The first sign-in still uses the password:

PS C:\> $pub = Get-Content ~\.ssh\id_ed25519.pub
PS C:\> $file = 'C:\ProgramData\ssh\administrators_authorized_keys'
PS C:\> $remote = "powershell Add-Content -Force -Path $file -Value '$pub'; icacls.exe $file /inheritance:r /grant Administrators:F /grant SYSTEM:F"
PS C:\> ssh alice.admin@dc1 $remote
alice.admin@dc1's password:
processed file: C:\ProgramData\ssh\administrators_authorized_keys
Successfully processed 1 files; Failed processing 0 files

PS C:\> ssh alice.admin@dc1 hostname
dc1

The second sign-in went through without a password. On Windows with localized group names, write the SID instead of Administrators: /grant *S-1-5-32-544:F. If a key isn't accepted, nine times out of ten the file permissions are to blame: sshd logs a message about bad permissions (Bad permissions), and the icacls command above restores them. Once the keys work, passwords are turned off:

PasswordAuthentication no

For regular users, not administrators, the file is C:\Users\<user>\.ssh\authorized_keys in their profile; a domain user's profile is created at the first sign-in, including a password sign-in over SSH.

Key sign-in has a peculiarity Linux doesn't have. A Windows session opened with a key is created without the user's credentials: the server never saw the password and has no Kerberos ticket. Inside such a session, network resources can't be opened as the user — \\dc2\SYSVOL, another server — it's the same second hop as in part 14, only with no workaround at all. Kerberos sign-in without delegation works the same way: dir \\dc2\SYSVOL in such a session answers "Network access is denied.". A session opened with a password or with Kerberos and delegation (more on it below) does have credentials.


PowerShell as the shell

The default shell is set by the DefaultShell registry value under HKLM:\SOFTWARE\OpenSSH (the key is created together with OpenSSH), and it can be any program:

PS C:\> $key = 'HKLM:\SOFTWARE\OpenSSH'
PS C:\> New-ItemProperty -Path $key -Name DefaultShell -Value 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -PropertyType String -Force | Select-Object DefaultShell

DefaultShell
------------
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

For PowerShell 7 it's C:\Program Files\PowerShell\7\pwsh.exe, if it's installed on the server (Windows Server doesn't ship it). sshd passes the command from ssh host command to the shell with a switch: /c for cmd.exe and -c for any other program, and the server's process list shows "c:\program files\powershell\7\pwsh.exe" -c "command". Both powershell.exe and pwsh.exe read -c as -Command, so PowerShell needs nothing else. The switch can be replaced with the DefaultShellCommandOption value under the same key, but only a shell that doesn't understand -c needs it; the /c value sometimes recommended for PowerShell changes nothing, as both PowerShells accept it too. No sshd restart is needed: the values are read at every sign-in.

PS C:\> ssh alice.admin@dc1
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Users\alice.admin> Get-Service NTDS, DNS | Select-Object Name, Status

Name  Status
----  ------
DNS  Running
NTDS Running

PS C:\Users\alice.admin> exit
PS C:\> ssh alice.admin@dc1 'Get-ADDomainController | Select-Object HostName, Site'

HostName             Site
--------             ----
dc1.corp.example.com Office

This is a full interactive PowerShell with history, completion and colors: since Windows 10 1809 the console has ConPTY, a pseudoterminal, and sshd attaches the remote terminal to it, like a pty on Unix. Any console program, diskpart and sconfig included, works over SSH just as it does locally. The banner is shorter than the local one: Windows PowerShell prints two lines without the suggestion to install PowerShell 7, and pwsh.exe prints a single line with its version, PowerShell 7.4.21. An administrator gets a session with full rights, without UAC filtering: the window title the server sends to the terminal starts with Administrator:.

Commands with quotes in ssh host '…' reach the shell as a string, and escaping adds up from the rules of three parties: local PowerShell, SSH and remote PowerShell. For anything more complex than one command, it's easier to pass a script: Get-Content .\script.ps1 | ssh dc1 powershell -Command -.


Kerberos

A password or a key is SSH authentication that has nothing to do with the domain: an administrator's key on a server is as good as a password, and rotating, revoking and tracking keys is manual work. The domain has Kerberos, and OpenSSH can use it through GSSAPI — a standard programming interface to security mechanisms, on top of which SSH carries the Kerberos exchange (RFC 4462). The client presents to the server a ticket for host/dc1.corp.example.com, the very HOST SPN the computer account has had since it joined the domain (part 6). No password, no key, a sign-in like to any other domain service. In the Windows build of OpenSSH, GSSAPI works through SSPI, the same interface the rest of the Windows services use. Microsoft's documentation lists server-side GSSAPI as available starting with Windows Server 2022 and Windows 10 21H1, but the component installed on Windows Server 2019 today comes in the same OpenSSH_for_Windows_9.5 version as in 2025, and Kerberos sign-in works in it.

On the server:

GSSAPIAuthentication yes

On the client GSSAPI is off by default too (ssh -G dc1.corp.example.com shows gssapiauthentication no); it's turned on in ~\.ssh\config, where host patterns are case sensitive:

Host *.corp.example.com
    GSSAPIAuthentication yes
    GSSAPIDelegateCredentials yes

And the sign-in:

PS C:\> ssh -v dc1.corp.example.com hostname 2>&1 | Select-String 'Authenticat|gssapi'

debug1: Authenticating to dc1.corp.example.com:22 as 'corp\\alice.admin'
debug1: Authentications that can continue: publickey,gssapi-with-mic,password,keyboard-interactive
debug1: Next authentication method: gssapi-with-mic
Authenticated to dc1.corp.example.com ([192.168.1.10]:22) using "gssapi-with-mic".

No user name was given: the built-in Windows client substitutes the current user as corp\alice.admin (the debug output doubles the backslash) and takes the ticket from the Windows session cache. Linux servers in the domain don't accept such a name; they need ssh -l alice.admin. Without delegation the session holds only the ticket the server accepted the sign-in with: klist on dc1 shows a single HOST/dc1.corp.example.com ticket marked Cache Flags: 0x8 -> ASC, and the server doesn't go to the network as the user. GSSAPIDelegateCredentials yes adds delegation: a forwarded TGT travels to the server together with the ticket, and the session on dc1 has the user's full credentials:

PS C:\Users\alice.admin> klist | Select-String 'Server: krbtgt|Ticket Flags'

        Server: krbtgt/CORP.EXAMPLE.COM @ CORP.EXAMPLE.COM
        Ticket Flags 0x60a10000 -> forwardable forwarded renewable pre_authent name_canonicalize
PS C:\Users\alice.admin> Get-ChildItem \\dc2\SYSVOL | Select-Object -First 1 Name

Name
----
corp.example.com

The forwarded flag is the forwarded TGT from part 6, and the second hop from part 14 is solved here without CredSSP: \\dc2\SYSVOL opened from the session on dc1 because dc1 holds the user's TGT. This is unconstrained delegation in its pure form — convenient and exactly as dangerous as described in part 6: the user's TGT ends up on the server, and it should be given only to servers trusted as much as domain controllers.

Two clients. A workstation with Git for Windows has a second ssh — C:\Program Files\Git\usr\bin\ssh.exe. Git calls it for git clone git@…, it answers ssh in Git Bash, and it ends up in PATH if you choose the option with Unix tools when installing Git. It's OpenSSH from MSYS2, usually newer than the built-in one, built without SSPI: its GSSAPI goes through the Heimdal Kerberos library with its own ticket cache, it knows nothing about Windows tickets and silently skips the gssapi-with-mic method — the sign-in falls back to a key or a password. Its default user name comes without the domain as well. The built-in client is built with SSPI and takes the ticket straight from the session. The check is Get-Command ssh -All | Select-Object Source and ssh -V (the built-in client's version starts with OpenSSH_for_Windows); the cure is the full path or the PATH order.

From Linux and macOS with a Kerberos ticket (kinit alice.admin@CORP.EXAMPLE.COM) the sign-in is the same: ssh -K dc1.corp.example.com (-K enables GSSAPI with delegation, and the built-in Windows client understands it the same way — no lines in ~\.ssh\config needed); in part 30 we'll come back to this from the other side. Without GSSAPI, clients name a domain user in two ways: ssh 'corp\alice.admin'@dc1 or ssh alice.admin@corp.example.com@dc1 — the latter looks odd, but it's a UPN before the host's @.


PowerShell Remoting over SSH

PowerShell 7 can do over SSH what part 14 did over WinRM: Enter-PSSession, Invoke-Command, New-PSSession with the -HostName parameter instead of -ComputerName. The transport is an SSH subsystem that starts pwsh -sshs on the server, and the same PSRP protocol runs over the channel. PowerShell 7 is needed on both ends; Windows PowerShell 5.1 can't do this.

On the server, in sshd_config:

Subsystem	powershell	C:/progra~1/powershell/7/pwsh.exe -sshs

The 8.3 path (progra~1 instead of Program Files) works around a bug in the Windows build of OpenSSH: a subsystem program path with a space in it doesn't work. Another workaround is a symbolic link with no spaces in its path, for example C:\ProgramData\ssh\pwsh.exe pointing to C:\Program Files\PowerShell\7\pwsh.exe. After Restart-Service sshd:

PS C:\> Enter-PSSession -HostName dc1.corp.example.com
[dc1.corp.example.com]: PS C:\Users\alice.admin\Documents> $PSVersionTable.PSVersion.ToString()
7.6.9
[dc1.corp.example.com]: PS C:\Users\alice.admin\Documents> exit

PS C:\> Invoke-Command -HostName dc1, dc2, dc3 -ScriptBlock { (Get-Service NTDS).Status } -UserName alice.admin

PSComputerName RunspaceId                           Value
-------------- ----------                           -----
dc2            5edd4530-6f8d-445d-a747-e7ead4cf7805 Running
dc1            0b9c63f1-2d4e-4c55-9a51-7e8f3a6d2c10 Running
dc3            c4a1e7d2-93b8-4f06-8e2a-61d5b0f9e3a7 Running

PS C:\> $s = New-PSSession -HostName dc1 -KeyFilePath ~\.ssh\id_ed25519
PS C:\> $s

Id Name      Transport ComputerName ComputerType  State  ConfigurationName Availability
-- ----      --------- ------------ ------------  -----  ----------------- ------------
 1 Runspace1 SSH       dc1          RemoteMachine Opened DefaultShell         Available

PS C:\> Copy-Item .\fix.ps1 -ToSession $s -Destination C:\Temp\

Results arrive in order of readiness, with the PSComputerName and RunspaceId columns, just like WinRM in part 14; the session differs by the SSH transport and the DefaultShell configuration — there are no endpoints to choose from here.

Authentication is whatever SSH is configured for: a key, the agent, Kerberos; PowerShell adds none of its own. It starts the same ssh.exe, so the lines in ~\.ssh\config apply, and individual client options are passed with -Options, for example -Options @{ GSSAPIDelegateCredentials = 'yes' }. If SSH refuses, PowerShell first prints the ssh answer ("Permission denied (publickey,gssapi-with-mic,password,keyboard-interactive).") and then its own error: "The SSH client session has ended with error message: The SSH transport process has abruptly terminated causing this remote session to break.". -UserName is needed when the name differs from the current one, -KeyFilePath for a specific key. No TrustedHosts, no HTTPS listener, no WinRM rules: only port 22. And it works from any platform: from a macOS laptop Invoke-Command -HostName dc1 runs PowerShell on the controller, and implicit remoting from part 15 over such a session brings the ActiveDirectory and DnsServer modules to Linux.

What the SSH transport lacks compared to WinRM: session configurations and everything built on them — JEA, virtual accounts, per-endpoint permissions; disconnected sessions remain a WinRM feature as well: Disconnect-PSSession answers "The disconnection operation is not supported on the remote computer. To support disconnecting, the remote computer must be running Windows PowerShell 3.0 or a later version of Windows PowerShell and using the WSMan transport.". The second hop is solved with SSH means — TGT delegation through GSSAPI; without it Get-ChildItem \\dc2\SYSVOL in the session answers "Access to the path '\dc2\SYSVOL' is denied.". For administration from different platforms it's a trade-off people accept; for delegating to a help desk, WinRM with JEA has no replacement.


Security

Port 22 on a domain controller is a new door to the most important server, and behind it there must be the same rules as behind RDP:

  • Keys or Kerberos only: PasswordAuthentication no once the keys work.
  • Only the groups that need it: in Windows Server 2025 this is already done by the AllowGroups administrators "openssh users" line; on 2019 and 2022 you add it yourself, and membership in OpenSSH Users is kept under the same control as membership in Administrators.
  • Only from the administrators' network: Set-NetFirewallRule -Name OpenSSH-Server-In-TCP -Profile Domain -RemoteAddress 192.168.1.0/24 or the addresses of the tier 0 workstations from part 15.
  • The administrators' key file — one file, with the right permissions, under control: every key in it is an administrator password with no expiry.

The log. By default (SyslogFacility AUTH) sshd writes to the OpenSSH/Operational event log, all records with ID 4:

PS C:\> Get-WinEvent -LogName OpenSSH/Operational -MaxEvents 4 | Select-Object TimeCreated, Message

TimeCreated           Message
-----------           -------
10/15/2026 3:40:12 PM sshd: Accepted gssapi-with-mic for corp\\alice.admin from 192.168.1.100 port 52010 ssh2: CORP\\alice.admin
10/15/2026 3:38:51 PM sshd: Accepted publickey for corp\\alice.admin from 192.168.1.100 port 52004 ssh2: ED25519 SHA256:0ODW1wKq…
10/15/2026 3:36:03 PM sshd: Failed password for invalid user root from 203.0.113.77 port 41022 ssh2
10/15/2026 3:36:00 PM sshd: Invalid user root from 203.0.113.77 port 41022

The same lines as in auth.log on Linux, including sign-in attempts as root from foreign addresses if the port is visible from outside; the backslash in the domain name is doubled, because that's how sshd escapes it in the log. After a GSSAPI sign-in sshd appends whose ticket confirmed it (CORP\\alice.admin). Besides sign-ins, the log shows sign-outs (Received disconnect from … disconnected by user, Disconnected from …) and the service starting and stopping (Server listening on 0.0.0.0 port 22., Received signal …; terminating.). To log to a file, set SyslogFacility LOCAL0 in sshd_config (for more detail, LogLevel VERBOSE), and the log goes to C:\ProgramData\ssh\logs\sshd.log. In the Security log an SSH sign-in is event 4624, two of them per connection: logon type 3 (network), logon process sshd, process name C:\Windows\System32\OpenSSH\sshd.exe, the Kerberos package for Kerberos, an elevated token (Elevated Token: Yes) for an administrator and an empty network address (-): the client's address is only in the sshd log.


Looking under the hood

The server's effective configuration in full, with the defaults, is sshd -T; it has to run as an administrator, because sshd reads the host keys:

PS C:\> ssh alice.admin@dc1 'sshd -T' | Select-String 'authentication|authorizedkeysfile|subsystem|allowgroups'

hostbasedauthentication no
pubkeyauthentication yes
gssapiauthentication yes
passwordauthentication no
kbdinteractiveauthentication yes
authorizedkeysfile .ssh/authorized_keys
allowgroups administrators
allowgroups openssh users
authenticationmethods any
subsystem sftp sftp-server.exe
subsystem powershell C:/progra~1/powershell/7/pwsh.exe -sshs

authorizedkeysfile .ssh/authorized_keys is for regular users; administrators get the Match block, and sshd -T -C user=corp\alice.admin,host=ws1,addr=192.168.1.100 shows it: authorizedkeysfile __PROGRAMDATA__/ssh/administrators_authorized_keys. sshd -T prints some lines that do nothing on Windows: Microsoft's documentation lists the unavailable directives, among them KbdInteractiveAuthentication, HostbasedAuthentication and StrictModes.

The process tree. This is what the server shows while a Remoting over SSH session to it is open:

PS C:\> Get-CimInstance Win32_Process -Filter "Name='sshd.exe' or Name='cmd.exe' or Name='pwsh.exe'" | Select-Object ProcessId, ParentProcessId, Name, @{n='User'; e={(Invoke-CimMethod -InputObject $_ -MethodName GetOwner).User}}, CommandLine

ProcessId ParentProcessId Name     User        CommandLine
--------- --------------- ----     ----        -----------
     2588             688 sshd.exe SYSTEM      C:\WINDOWS\System32\OpenSSH\sshd.exe
     5708            2588 sshd.exe SYSTEM      "C:\WINDOWS\System32\OpenSSH\sshd.exe" -R
     6012            5708 sshd.exe alice.admin "C:\WINDOWS\System32\OpenSSH\sshd.exe" -z
     6104            6012 cmd.exe  alice.admin "c:\windows\system32\cmd.exe" /c "c:/progra~1/powershell/7/pwsh.exe -sshs"
     6120            6104 pwsh.exe alice.admin c:/progra~1/powershell/7/pwsh.exe  -sshs

This is privilege separation, as on Unix. The sshd service under SYSTEM only accepts connections; for each connection it starts its own copy (-R), also under SYSTEM. Before sign-in that copy hands protocol parsing to the unprivileged sshd.exe -y process under the virtual account VIRTUAL USERS\sshd_<PID>, which has no rights in the system, and after sign-in sshd.exe -z under the signed-in user runs in its place. The shell or the subsystem runs under it, and sshd executes the subsystem line through the default shell: cmd.exe /c "…pwsh.exe -sshs".

File transfer — SFTP and SCP work, with Windows paths and forward slashes, and in sftp's answers the drive letter is preceded by /:

PS C:\> scp .\report.csv alice.admin@dc1:C:/Temp/
report.csv                                    100%   18KB   4.1MB/s   00:00
PS C:\> sftp alice.admin@dc1
Connected to dc1.
sftp> ls C:/Windows/NTDS
/C:/Windows/NTDS/edb.chk                /C:/Windows/NTDS/edb.log
/C:/Windows/NTDS/edbres00001.jrs        /C:/Windows/NTDS/edbres00002.jrs
/C:/Windows/NTDS/edbtmp.log             /C:/Windows/NTDS/ntds.dit
/C:/Windows/NTDS/ntds.jfm               /C:/Windows/NTDS/temp.edb

And the exchange in packets — pktmon filter add -p 22: after the version exchange (SSH-2.0-OpenSSH_for_Windows_9.5 is how the server introduces itself) and KEX everything is encrypted, and all you can see is packet lengths and direction. Unlike WinRM, where HTTP headers still let you guess the operation, SSH shows nothing, and that is why the sshd log on the server is the only source of information about who signed in and how.


Sources


The management block is done: the module, WinRM, the workstation and SSH. The next four parts are about operations: what breaks in a domain, how to diagnose it and how to fix it without reinstalling. We'll start with the five roles the first controller received in part 4 and that haven't been mentioned since.

Next — part 17: FSMO roles. Why a multi-master directory has five things owned by just one server; what each role does and what stops working when its owner is down; how to find the owners; Move-ADDirectoryServerOperationMasterRole for transfer and for seizure, and why a former owner must not come back to the network after a seizure.

Only registered users can leave a comment.

Sign in or register to leave a comment.