Mitigating Winshock (CVE-2014-6321) Vulnerabilities Using BIG-IP iRules

Recently we’ve witnessed yet another earth shattering vulnerability in a popular and very fundamental service.
Dubbed Winshock, it follows and joins the Heartbleed, Shellshock and Poodle in the pantheon of critical vulnerabilities discovered in 2014.
Winshock (CVE-2014-6321) earns a 10.0 CVSS score due to being related to a common service such as TLS, and potentially allowing remote arbitrary code execution.

SChannel

From MSDN:
Secure Channel, also known as Schannel, is a security support provider (SSP) that contains a set of security protocols that provide identity authentication and secure, private communication through encryption.

Basically, SChannel is Microsoft’s implementation of TLS, and it is used in various MS-related services that support encryption and authentication – such as: Internet Information Services (IIS), Remote Desktop Protocol, Exchange and Outlook Web Access, SharePoint, Active Directory and more.

Naturally, SChannel also contains implementation for the TLS handshake protocol, which is performed before every secure session is established between the client and the server.

The TLS Handshake

The following image demonstrates how a typical TLS handshake looks like:

Image source: http://www-01.ibm.com/support/knowledgecenter/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm?lang=en

The handshake is used for the client and the server to agree on the terms of the connection.
The handshake is conducted using messages, for the purpose of authenticating between the server and the client, agreeing on cipher suites, and exchanging public keys using certificates.
Each type of message is passed on the wire as a unique “TLS Record”.
Several messages (TLS records) may be sent over one packet.

Some of the known TLS records are the following:

  • Client Hello – The client announces it would like to initiate a connection with the server. It also presents all the various cipher suites it can support. This record may also have numerous extensions used to provide even more data.

  • Server Hello – The server acknowledges the Client Hello and presents its own information.

  • Certificate Request – In some scenarios, the client is required to present its certificate in order to authenticate itself. This is known as two-way authentication (or a mutual authentication). The Certificate Request message is sent by the server and forces the client to present a valid certificate before the handshake is successful.

  • Certificate – A message used to transfer the contents of a certificate, including subject name, issuer, public key and more.

  • Certificate Verify – Contains signed value using the client’s private key. It is presented by the client along with their certificate during a 2-way handshake, and serves as a proof of the client actually holding the certificate they claim to.

    SChannel Vulnerabilities

    Two vulnerabilities were found in the way SChannel handles those TLS records.

    One vulnerability occurs when parsing the “server_name” extension of the Client Hello message. This extension is typically used to specify the host name which the client is trying to connect to on the target server. In some way this is similar to the HTTP “Host” header.
    It was found that SChannel will not properly manage memory allocation when this record contains more than one server name.
    This vulnerability leads to denial of service by memory exhaustion.

    The other vulnerability occurs when an invalid signed value is presented inside a Certificate Verify message. It was found that values larger than what the server expects will be written to the memory beyond the allocated buffer scope.
    This behavior may result in a potential remote code execution.

    Mitigation with BIG-IP iRules

    SSL offloading using BIG-IP is inherently not vulnerable as it does not relay vulnerable messages to the backend server. However, in a “pass-through” scenario, where all the TLS handshake messages are being forwarded without inspection, backend servers may be vulnerable to these attacks.

    The following iRule will detect and mitigate attempts to exploit above SChannel vulnerabilities:

    when CLIENT_ACCEPTED {
     TCP::collect
     set MAX_TLS_RECORDS 16
     set iPacketCounter 0
     set iRecordPointer 0
     set sPrimeCurve ""
     set iMessageLength 0
    }
    when CLIENT_DATA {
     #log local0. "New TCP packet. Length [TCP::payload length]. Packet Counter $iPacketCounter"
     set bScanTLSRecords 0
     if { $iPacketCounter == 0 } {
      binary scan [TCP::payload] cSS tls_xacttype tls_version tls_recordlen
      if { [info exists tls_xacttype] && [info exists tls_version] && [info exists tls_recordlen] } {
       if { ($tls_version == "769" || $tls_version == "770" || $tls_version == "771") && $tls_xacttype == 22 } {
        set bScanTLSRecords 1
       }
      }
     }
    
     if { $iPacketCounter > 0 } {
      # Got here mid record, collect more fragments
      #log local0. "Gather. tls rec $tls_recordlen, ptr $iRecordPointer"
      if { [expr {$iRecordPointer + $tls_recordlen + 5}] <= [TCP::payload length] } {
       #log local0. "Full record received"
       set bScanTLSRecords 1
      } else {
       #log local0. "Record STILL fragmented"
       set iPacketCounter [expr {$iPacketCounter + 1}]
       TCP::collect
      }
     }
    
     if { $bScanTLSRecords } {
       # Start scanning records
       set bNextRecord 1
       set bKill 0
       while { $bNextRecord >= 1 } {
        #log local0. "Reading next record. ptr $iRecordPointer"
        binary scan [TCP::payload] @${iRecordPointer}cSS tls_xacttype tls_version tls_recordlen
        #log local0. "SSL Record Type $tls_xacttype , Version: $tls_version , Record Length: $tls_recordlen"
        if { [expr {$iRecordPointer + $tls_recordlen + 5}] <= [TCP::payload length] } {
         binary scan [TCP::payload] @[expr {$iRecordPointer + 5}]c tls_action
         if { $tls_xacttype == 22 && $tls_action == 1 } {
          #log local0. "Client Hello"
          set iRecordOffset [expr {$iRecordPointer + 43}]
          binary scan [TCP::payload] @${iRecordOffset}c tls_sessidlen
          set iRecordOffset [expr {$iRecordOffset + 1 + $tls_sessidlen}]
          binary scan [TCP::payload] @${iRecordOffset}S tls_ciphlen
          set iRecordOffset [expr {$iRecordOffset + 2 + $tls_ciphlen}]
          binary scan [TCP::payload] @${iRecordOffset}c tls_complen
          set iRecordOffset [expr {$iRecordOffset + 1 + $tls_complen}]
          binary scan [TCP::payload] @${iRecordOffset}S tls_extenlen
          set iRecordOffset [expr {$iRecordOffset + 2}]
          binary scan [TCP::payload] @${iRecordOffset}a* tls_extensions
    
          for { set i 0 } { $i < $tls_extenlen } { incr i 4 } {
           set iExtensionOffset [expr {$i}]
           binary scan $tls_extensions @${iExtensionOffset}SS etype elen
           if { ($etype == "00") } {
            set iScanStart [expr {$iExtensionOffset + 9}]
            set iScanLength [expr {$elen - 5}]
            binary scan $tls_extensions @${iScanStart}A${iScanLength} tls_servername
            if { [regexp \x00 $tls_servername] } {
             log local0. "Winshock detected - NULL character in host name. Server Name: $tls_servername"
             set bKill 1
            } else {
             #log local0. "Server Name found valid: $tls_servername"
            }
            set iExtensionOffset [expr {$iExtensionOffset + $elen}]
           } else {
            #log local0. "Uninteresting extension $etype"
            set iExtensionOffset [expr {$iExtensionOffset + $elen}]
           }
           set i $iExtensionOffset
          }
         } elseif { $tls_xacttype == 22 && $tls_action == 11 } {
          #log local0. "Certificate"
          set iScanStart [expr {$iRecordPointer + 17}]
          set iScanLength [expr {$tls_recordlen - 12}]
          binary scan [TCP::payload] @${iScanStart}A${iScanLength} client_certificate
          if { [regexp {\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01(\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07|\x06\x05\x2b\x81\x04\x00(?:\x22|\x23))} $client_certificate reMatchAll reMatch01] } {
           #log local0. $match01
           switch $reMatch01 {
            "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" {
             set sPrimeCurve "P-256"
            }
            "\x06\x05\x2b\x81\x04\x00\x22" {
             set sPrimeCurve "P-384"
            }
            "\x06\x05\x2b\x81\x04\x00\x23" {
             set sPrimeCurve "P-521"
            }
            default {
             #log local0. "Invalid curve"
            }
           }
          }
         } elseif { $tls_xacttype == 22 && $tls_action == 15 } {
          #log local0. "Certificate Verify"
          set iScanStart [expr {$iRecordPointer + 11}]
          set iScanLength [expr {$tls_recordlen - 6}]
          binary scan [TCP::payload] @${iScanStart}A${iScanLength} client_signature
          binary scan $client_signature c cSignatureHeader
          if { $cSignatureHeader == 48 } {
           binary scan $client_signature @3c r_len
           set s_len_offset [expr {$r_len + 5}]
           binary scan $client_signature @${s_len_offset}c s_len
           set iMessageLength $r_len
           if { $iMessageLength < $s_len } {
            set iMessageLength $s_len
           }
          } else {
           #log local0. "Sig header invalid"
          }
         } else {
          #log local0. "Uninteresting TLS action"
         }
    
         # Curve and length found - check Winshock
         if { $sPrimeCurve ne "" && $iMessageLength > 0 } {
          set iMaxLength 0
          switch $sPrimeCurve {
           "P-256" {
            set $iMaxLength 33
           }
           "P-384" {
            set $iMaxLength 49
           }
           "P-521" {
            set $iMaxLength 66
           }
          }
    
          if { $iMessageLength > $iMaxLength } {
           log local0. "Winshock detected - Invalid message length (found: $iMessageLength, max:$iMaxLength)"
           set bKill 1
          }
         }
    
         # Exploit found, close connection
         if { $bKill } {
          TCP::close
          set bNextRecord 0
         } else {
          # Next record
          set iRecordPointer [expr {$iRecordPointer + $tls_recordlen + 5}]
          if { $iRecordPointer == [TCP::payload length]} {
           # End of records => Assume it is the end of the packet.
           #log local0. "End of records"
           set bNextRecord 0
           set iPacketCounter 0
           set iRecordPointer 0
           set sPrimeCurve ""
           set iMessageLength 0
           TCP::release
           TCP::collect
          } else {
           if { $bNextRecord < $MAX_TLS_RECORDS } {
            set bNextRecord [expr {$bNextRecord + 1}]
           } else {
            set bNextRecord 0
            #log local0. "Too many loops over TLS records, exit now"
            TCP::release
            TCP::collect
           }
          }
         }
        } else {
         #log local0. "Record fragmented"
         set bNextRecord 0
         set iPacketCounter [expr {$iPacketCounter + 1}]
         TCP::collect
        }
       }
     } else {
      # Exit here if packet is not TLS handshake
      if { $iPacketCounter == 0 } {
       TCP::release
       TCP::collect
      }
     }
    }

    Create a new iRule and attach it to your virtual server.

  •  

  •  

  •  

  •  

Updated Jun 23, 2022
Version 2.0

Was this article helpful?

13 Comments