Cannot Create PSSession From macOS to Windows
l33 opened this issue · 5 comments
SUMMARY
Same problem as #70 but this time using SSL. Reading the README.md
I think this is supposed to work:
Technically NTLM auth on macOS can work but only does when running over HTTPS.
However, it does not in my case. Attempting to connect to a Windows Server 2019 (domain joined) from my M3 Mac:
New-PSSession -ComputerName [redacted] -UseSSL -Authentication Negotiate -Credential $null
PowerShell credential request
Enter your credentials.
User: [redacted]
Password for user [redacted]: [redacted]
New-PSSession: [[redacted]] Connecting to remote server [redacted] failed with the following error message : Authorization failed For more information, see the about_Remote_Troubleshooting Help topic.
Running the identical command from a Windows 10 computer works as expected.
MODULE VERSION
Import-Module -Name PSWSMan -PassThru
ModuleType Version PreRelease Name ExportedCommands
---------- ------- ---------- ---- ----------------
Script 2.3.1 PSWSMan {Disable-WSManCertVerification, Enable-WSManCertVerification, Get-WSManVersion, Install-WSMan…}
OS / ENVIRONMENT
macOS info
% system_profiler SPSoftwareDataType
Software:
System Software Overview:
System Version: macOS 14.1 (23B2073)
Kernel Version: Darwin 23.1.0
Boot Volume: Macintosh HD
Boot Mode: Normal
Computer Name: MacBook Pro
User Name: [redacted]
Secure Virtual Memory: Enabled
System Integrity Protection: Enabled
Time since boot: 22 days, 10 hours
PowerShell info
> $PSVersionTable
Name Value
---- -----
PSVersion 7.4.1
PSEdition Core
GitCommitId 7.4.1
OS Darwin 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct 9 21:32:11 PDT 2023; root:xnu-10002.41.9~7/RELEASE_ARM64_T6030
Platform Unix
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
WSMan setup
> Install-WSMan -verbose
VERBOSE: Attempting to get OpenSSL info with /opt/homebrew/bin/brew --prefix openssl
STDOUT: /opt/homebrew/opt/openssl@3
STDERR:
RC: 0
VERBOSE: Checking arch information for '/opt/homebrew/opt/openssl@3/lib/libcrypto.dylib' and '/opt/homebrew/opt/openssl@3/lib/libssl.dylib'
VERBOSE: Checking if 'arm64' for '/opt/homebrew/opt/openssl@3/lib/libcrypto.dylib' is one of 'arm64'
VERBOSE: Checking if 'arm64' for '/opt/homebrew/opt/openssl@3/lib/libssl.dylib' is one of 'arm64'
VERBOSE: Brew openssl libcrypto|ssl exists and is valid at '/opt/homebrew/opt/openssl@3/lib/libcrypto.dylib' and '/opt/homebrew/opt/openssl@3/lib/libssl.dylib'
VERBOSE: Getting OpenSSL version for '/opt/homebrew/opt/openssl@3/lib/libssl.dylib'
VERBOSE: OpenSSL Version: Major 3 Minor 2 Patch 0
VERBOSE: Host Info:
{
"Distribution": "macOS",
"StandardLib": "macOS",
"OpenSSL": "3",
"LibCrypto": {
"Source": "libcrypto.3.dylib",
"Target": "/opt/homebrew/opt/openssl@3/lib/libcrypto.dylib"
},
"LibSSL": {
"Source": "libssl.3.dylib",
"Target": "/opt/homebrew/opt/openssl@3/lib/libssl.dylib"
}
}
VERBOSE: Installing WSMan libs for 'macOS-3'
VERBOSE: Checking to see if libmi.dylib is installed
VERBOSE: Checking to see if libpsrpclient.dylib is installed
Excerpt from omiclient-send.trc
[Session: 7 Date: 2024-02-14 22:46:38.0877754Z]
POST /wsman?PSVersion=7.4.1 HTTP/1.1
Connection: Keep-Alive
Content-Length: 0
Content-Type: application/soap+xml;charset=UTF-8
Host: [redacted]:5986
Authorization: Negotiate YEg[redacted]
POST /wsman/ HTTP/1.1
Connection: Keep-Alive
Content-Length: 0
Content-Type: application/soap+xml;charset=UTF-8
Host: [redacted]:5986
Authorization: Negotiate oYI[redacted]
Server replies from taken from omi's debug log
HTTP/1.1 401
WWW-Authenticate: Negotiate oYI[redacted]
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 14 Feb 2024 22:46:38 GMT
Content-Length: 0
HTTP/1.1 401
Server: Microsoft-HTTPAPI/2.0
WWW-Authenticate: Negotiate
WWW-Authenticate: Kerberos
WWW-Authenticate: http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual
Date: Wed, 14 Feb 2024 22:46:38 GMT
Connection: close
Content-Length: 0
Some other things I noticed:
- Using username in UPN format introduces a ~35 s delay until the error pops up. Using its down-level logon name immediately returns the login failure. From the debug log it looks as if the server takes its time to reply to the first POST but the first request's content is always identical no matter what username is chosen.
- The debug log shows
MI_Result = MI_RESULT_ACCESS_DENIED
but the error message does not show the error code.
Is -Credential $null
actually what you used or is it just a placeholder?
Using username in UPN format introduces a ~35 s delay until the error pops up. Using its down-level logon name immediately returns the login failure. From the debug log it looks as if the server takes its time to reply to the first POST but the first request's content is always identical no matter what username is chosen.
Most likely this is because it will try and do a Kerberos KDC lookup and that's the time it takes for dns to return with a failure.
Is
-Credential $null
actually what you used or is it just a placeholder?
It's what I actually used to compile everything for this report. It was the only way to get New-PSSession
to ask for credentials. Normally I use some variable containing the output of Get-Credential
. To my knowledge the result is identical.
Using username in UPN format introduces a ~35 s delay until the error pops up. Using its down-level logon name immediately returns the login failure. From the debug log it looks as if the server takes its time to reply to the first POST but the first request's content is always identical no matter what username is chosen.
Most likely this is because it will try and do a Kerberos KDC lookup and that's the time it takes for dns to return with a failure.
That makes sense. I can try to confirm this later by looking into DNS network traffic.
As for the reasons why it's failing I've really given up on this approach unfortunately because of problems like this. GSSAPI authentication is extremely complicated on macOS and super hard to debug. I know I tested NTLM over HTTPS originally on an Intel mac but I don't have access to an arm based Mac right now to try it out again.
As for why it's failing you could look into the network traffic and see if it's actually authenticating and the server responds with a 200 at any point. If it's always 401 then the authentication doesn't work at all and there's another problem at hand.
Using username in UPN format introduces a ~35 s delay until the error pops up. Using its down-level logon name immediately returns the login failure. From the debug log it looks as if the server takes its time to reply to the first POST but the first request's content is always identical no matter what username is chosen.
Most likely this is because it will try and do a Kerberos KDC lookup and that's the time it takes for dns to return with a failure.
That makes sense. I can try to confirm this later by looking into DNS network traffic.
You were right. I can see how SRV records are looked up for the KDC and data is sent to the KDC (which never responds).
As for why it's failing you could look into the network traffic and see if it's actually authenticating and the server responds with a 200 at any point. If it's always 401 then the authentication doesn't work at all and there's another problem at hand.
I dont have the means to decrypt the traffic I captured. The TLS uses a ECDHE cypher so I can't easily decrypt it. However, I do see two encrypted responses from the server and I have two decoded replies in the logs (as I included in the problem description). Assuming those encrypted responses and decoded replies match, there are no HTTP/200 replies. They are all 401.
I would dig depper into the issue but I think I will need some help on what to look at next.
I dont have the means to decrypt the traffic I captured
The omi trace logs should show you the messages being sent and the replies, you even mentioned it in your original comment.
I dont have the means to decrypt the traffic I captured. The TLS uses a ECDHE cypher so I can't easily decrypt it.
That is unfortunately, OpenSSL does have the ability to create a keylogfile that Wireshark can use to decrypt the TLS traffic but I believe you need to opt into the callback in the C code which isn't currently being done. If you are interested you can run https://gist.github.com/jborean93/6c1f1b3130f2675f1618da56633eb1fa on the Windows host to produce the same file that Wireshark can use to decrypt the traffic. Keep in mind it is pretty invasive and requires a reboot to undo the hooks it places in lsass so it's mostly just for development purposes only.
However, I do see two encrypted responses from the server and I have two decoded replies in the logs (as I included in the problem description). Assuming those encrypted responses and decoded replies match, there are no HTTP/200 replies. They are all 401.
This is the key part to figure out unfortunately. NTLM auth on macOS was problematic due to how the gss_wrap_iov
functions worked IIRC. So knowing whether the authentication phase failed (all 401 replies) or whether it failed on the first encrypted message would be good to know.
Unfortunately I don't really have any recommendations for you as this isn't something simple you can fix. What you can try is
- Run the code in a Linux container with gss-ntlmssp so you can use NTLM over HTTP or at least avoid the problematic macOS NTLM stack
- Use basic auth over HTTPS (no NTLM at all and encryption is only through HTTPS itself), unfortunately this is local accounts only
- If it's a domain account, look into setting up a
krb5.conf
so you can use Kerberos auth