Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrypt authorized message sent over HTTPS #577

Merged
merged 1 commit into from
Sep 27, 2018

Conversation

Iristyle
Copy link
Contributor

  • Previously a check was performed to see if the current message was
    sent over SSL. In cases where the message was sent as
    Content-Type: multipart/encrypted, the message would be improperly parsed
    resulting in a 401 unauthorized being sent to the client.

    By all accounts, the PSRP implementation on Windows in PowerShell 5 and
    PowerShell 6 allows a multipart/encrypted message to be sent in response
    to the challenge message.

    Since the Http_DecryptData function already ignores / returns immediately
    for messages that are not multipart/encrypted, this change should
    simply make the protocol handling more tolerant.

  • This fixes an issue when trying to establish a connection to an OMI server
    from the WinRM Ruby library where NTLM/SPNEGO is enabled in the OMI server.

 - Previously a check was performed to see if the current message was
   sent over SSL. In cases where the message was sent as
   Content-Type: multipart/encrypted, the message would be improperly parsed
   resulting in a 401 unauthorized being sent to the client.

   By all accounts, the PSRP implementation on Windows in PowerShell 5 and
   PowerShell 6 allows a multipart/encrypted message to be sent in response
   to the challenge message.

   Since the Http_DecryptData function already ignores / returns immediately
   for messages that are not multipart/encrypted, this change should
   simply make the protocol handling more tolerant.

 - This fixes an issue when trying to establish a connection to an OMI server
   from the WinRM Ruby library where NTLM/SPNEGO is enabled in the OMI server.
@Iristyle
Copy link
Contributor Author

@dantraMSFT / @mwrock - I wanted to open this up and get a conversation started on the protocol handling here. I haven't fully read through all the relevant specs yet, but it's my understanding that the NTLMSSP_AUTH generated in response to a NTLMSSP_CHALLENGE can be multipart/encrypted over HTTPS as I've observed with the WinRM gem.

I have done a number of traces with WireShark to look at the sequence of messages from various clients (PowerShell 5 and 6 on Windows, PowerShell 6 on Linux, omicli on Linux, winrm gem) to various servers to get a better understanding of what's happening. The WinRM gem successfully connects to a Windows host using a multipart/encrypted response to the challenge (message construction code is at https://github.com/WinRb/WinRM/blob/master/lib/winrm/http/transport.rb#L111-L120). However, when trying to connect to OMI server, the OMI code completely skips the multipart/encrypted decoding as mentioned in this PR.

That said, two other clients respond to the server differently. The OMI client application on Linux sends an empty Body of application/soap+xml and the server returns 200. PowerShell 6.1 on Windows, when using the Enter-PSSession cmdlet, also sends application/soap+xml, with a non-empty Body that includes creation Xml to create a PowerShell instance (obviously these payloads are influenced by the action they're taking).

Given my understanding is SPNEGO can be sent over HTTPS during this part of the protocol lifecycle and given the standard PowerShell endpoint on Windows accepts a message of this Content-Type, I'm inclined to believe that OMI server should follow the same behavior. But, as I mentioned, I haven't really dug into the protocol spec enough to know for certain if OMI is following the protocol more strictly... and I haven't looked closely enough at this codebase to understand if the current processing is intentional for that or other reasons.

Once this change is made, at least OMI server decrypts the message, parses the SOAP message and tries to proceed. It actually ends up with another issue immediately after that based on the message that the gem sends. That message looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:env="http://www.w3.org/2003/05/soap-envelope" 
    xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
    xmlns:b="http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd" 
    xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" 
    xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer" 
    xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" 
    xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd" 
    xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" 
    xmlns:cfg="http://schemas.microsoft.com/wbem/wsman/1/config">
    <env:Header>
        <a:To>https://172.28.54.141:5986/wsman</a:To>
        <a:ReplyTo>
            <a:Address mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
        </a:ReplyTo>
        <w:MaxEnvelopeSize mustUnderstand="true">153600</w:MaxEnvelopeSize>
        <a:MessageID>uuid:B62FFCAA-CBE3-4DC5-9103-B87D99D42AA2</a:MessageID>
        <p:SessionId mustUnderstand="false">uuid:93D0EB38-EBD3-4358-9E0E-F9794556C709</p:SessionId>
        <w:Locale xml:lang="en-US" mustUnderstand="false"/>
        <p:DataLocale xml:lang="en-US" mustUnderstand="false"/>
        <w:OperationTimeout>PT60S</w:OperationTimeout>
        <w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/config</w:ResourceURI>
        <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action>
    </env:Header>
    <env:Body></env:Body>
</env:Envelope>

OMI server code throws an error from https://github.com/Microsoft/omi/blob/edbe231042173018c52903e4170dd8b782fc4f8a/Unix/wsman/wsman.c#L2012 about get-instance: instance name parameter is missing, which I'm still working on. I haven't gotten far enough to know if OMI server should accept the message payload that the gem has sent or if it's really invalid. (FWIW, I did modify the WinRM gem code to send a different response. An emtpy message worked OK, but a KeepAlive message didn't). But again, this initial message is parsed OK by a PowerShell Windows endpoint and doesn't seem to cause any errors in that environment.

Any input here is welcome!

@Iristyle
Copy link
Contributor Author

UPDATE: I think I understand why the message being sent is failing to parse. The WinRM gem is issuing a config request message, which I believe is part of a set of WSMV protocol extensions that neither OMI server or the PSRP OMI provider supports.

MS-WSMV 4.2.1 covers this message - which looks like:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" 
             xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
             xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
     <s:Header>
         <wsa:To>http://localhost:80/wsman</wsa:To>
         <wsman:ResourceURI s:mustUnderstand="true">
           http://schemas.microsoft.com/wbem/wsman/1/config
         </wsman:ResourceURI>
         <wsa:ReplyTo>
             <wsa:Address s:mustUnderstand="true">
 http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
             </wsa:Address>
         </wsa:ReplyTo>
         <wsa:Action s:mustUnderstand="true">
 http://schemas.xmlsoap.org/ws/2004/09/transfer/Get
         </wsa:Action>
         <wsman:MaxEnvelopeSize s:mustUnderstand="true">
           51200
         </wsman:MaxEnvelopeSize>
         <wsa:MessageID>
           uuid:613DCD71-95AF-4ED5-86E2-1D6AB44ECE66
         </wsa:MessageID>
         <wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>
     </s:Header>
     <s:Body/>
 </s:Envelope>

This looks pretty familiar as it would appear to be the same request the WinRM gem is sending (WinRM sends a few extra options in the SOAP header)

It looks like the OMI client understands to ask for this at https://github.com/Microsoft/omi/blob/4a0b6ebabdce442c3e526cd47fb326425aeed79a/Unix/wsman/wsmanclient.c#L1399-L1406, but given it's coded defensively, I'm assuming there's no expectation that all servers respond?

@Iristyle
Copy link
Contributor Author

Iristyle commented Sep 25, 2018

I'm still plugging along (slowly) on getting the gem talking to the OMI server. I was able to create a fallback in the WinRM gem for the max envelope size when the server doesn't accept config requests (like OMI)

I've found an issue with the shell creation xml payload that the winrm gem sends. The OMI server code is expecting the shell to have a Name attribute in the XML per the code at / like the following example: https://github.com/Microsoft/omi/blob/232a38192e16ca724f3f3438419f8ccaead10f34/Unix/wsman/wsmanparser.c#L2126

<rsp:Shell xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" Name="Runspace3" ShellId="DC96D496-39F6-49D1-978C-CC6F32F99CC9">

However, the winrm gem does not set one at https://github.com/WinRb/WinRM/blob/master/lib/winrm/wsmv/init_runspace_pool.rb#L38 when creating the SOAP message body. Once that's fixed OMI responds with a 200. Then it's on to the next problem!

@Iristyle
Copy link
Contributor Author

I've landed some additional changes to the gem to be more tolerant of OMI behavior in WinRb/WinRM#288. These changes were necessary to enable negotiate support in the WinRM gem. I also have a few additional changes to push up to this PR to allow spnego over HTTPS.

@palladia
Copy link
Contributor

@paulcallen, @yakman2020: Please review this PR.

@paulcallen
Copy link
Member

paulcallen commented Sep 26, 2018

This change looks fine, just removing the prerequisite of decrypting only when not using SSL. We should run some internal tests on this to validate a few different scenarios though before committing it.

@JumpingYang001 JumpingYang001 merged commit ebe43ea into microsoft:master Sep 27, 2018
@Iristyle
Copy link
Contributor Author

Iristyle commented Sep 27, 2018 via email

@Iristyle Iristyle deleted the decrypt-spnego-over-https branch September 28, 2018 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants