An Irule for Client Ssl Profile that Allows Unassigned TLS Extension Values (17516)

Hello Community,

I have a requirement to allow enriched https header enrichment. The SSL negotiation (I’m doing ssl termination on F5) fails because the enriched header from client contains reserved tls extension values. (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtmltls-extensiontype-values-1).

The Client Hello request in the SSL Handshake was captured and contained an Extensions list, which included a reserved TLS Extension value (17156), which the F5 isn’t presenting in Server Hello.

I need an irule that can allow that Extension to be added on the client ssl profile so the ssl handshake doesn’t fail.

I added this irule, it created an extension actually, but there was no data under it.

when CLIENTSSL_CLIENTHELLO { set my_ext “Hello world!” set my_ext_type 17516 SSL::extensions insert [binary format S1S1a* $my_ext_type [string length $my_ext] $my_ext] }

Is this extension inserted by a public tool or are you writing your SSL client?

your code try to insert the extension in CLIENTHELLO packet

Do you want to insert it in SERVERHELLO packet?

If you’re trying to reflect the client’s MSISDN extension to the server, you definitely want to use the SERVERSSL_CLIENTHELLO_SEND event. It does not require forward proxy to use this event.

when SERVERSSL_CLIENTHELLO_SEND {
    set msisdn "foobar"
    set bin [binary format S1S1S1S1ca* 17516 [expr [string length ${msisdn}] + 5] [expr [string length ${msisdn}] + 3] 0 [string length ${msisdn}] ${msisdn}]   
    SSL::extensions insert $bin
}

Hi Kazeemyu1.5586213523653357E12,

Did you have a solution on this request, Im confronted to same issue.

Exactly the same issue while im trying to perform https Client Hello Enrichment.

Regards,

Baba TABOURE

when CLIENTSSL_HANDSHAKE {
    if { [SSL::extensions exists -type 17516] } then {
        set tls_extension [SSL::extensions -type 17516]
    } else {
        set tls_extension ""
    }
}
when SERVERSSL_CLIENTHELLO_SEND {
    if { $tls_sni_extension ne "" } then {
        SSL::extensions insert $tls_extension
    }
}

this code is a copy of this code with your extension type

https://devcentral.f5.com/s/articles/client-side-to-server-side-sni-relay-irule-967

Thanks Stanislas, is this the reason why we have that kind of error sent during TLS Handshake?:

Alert (Level: Fatal, Description: Bad Record Mac).

Our F5 is the first network element we have before getting to the server.
image_259681.png

I remember this.. Can you confirm this is this scenario:

  • The client does not insert this extension
  • A service between the client and the BigIP add this extension in the CLIENT_HELLO message
  • The client reject the BigIP Handshake Message

If this is the scenario, there is no solution as TLS protocol does not support such change.

Hereafter the scénario:

  • The client does not insert this extension
    • The client is a mobile which does not add the extension
  • A service between the client and the BigIP add this extension in the CLIENT_HELLO message
    • A service (such as DPI) between the client mobile and the BigIP add this extension
  • The reject is coming from the floating IP of BigIP (the public ip 196.207.246.112 in the image).
    • Alert (Level: Fatal, Description: Bad Record Mac)

PS: The BigIP is suppose to forward alls request coming from the client to the server

You can try this to catch and remove this extension from CLIENT_HELLO packet (not tested)

it will then insert it in server side TLS handshake

when CLIENT_ACCEPTED {
    set tls_extension_17516 ""
    SSL::disable   
    TCP::collect
}

when CLIENT_DATA {
    # Store TCP Payload up to 2^14 + 5 bytes (Handshake length is up to 2^14)
    set payload [TCP::payload 16389]
    set payloadlen [TCP::payload length]

    # If valid TLS 1.X CLIENT_HELLO handshake packet
    if { [binary scan $payload cH4ScH6H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action tls_handshakelen_hex tls_handshake_version tls_handshake_sessidlen] == 7 && \
        ($tls_record_content_type == 22) && \
        ([string match {030[1-3]} $tls_version]) && \
        ($tls_handshake_action == 1) && \
        ($payloadlen == ($tls_recordlen & 0xffff )+5)} {

        # store in a variable the handshake length
        #set tls_handshakelen [expr 0x$tls_handshakelen_hex]
        scan $tls_handshakelen_hex %x tls_handshakelen
        # store in a variable the handshake version
        set tls_handshake_prefered_version $tls_handshake_version

        # skip past the session id
        set record_offset [expr {44 + ($tls_handshake_sessidlen & 0xff)}]

        # skip past the cipher list
        binary scan $payload @${record_offset}S tls_ciphlen
        set record_offset [expr {$record_offset + 2 + ($tls_ciphlen & 0xffff)}]

        # skip past the compression list
        binary scan $payload @${record_offset}c tls_complen
        set record_offset [expr {$record_offset + 1 + ($tls_complen & 0xffff)}]

        # check for the existence of ssl extensions
        if { ($payloadlen > $record_offset) } {
            # skip to the start of the first extension
            set tls_extension_length_start $record_offset
            binary scan $payload @${record_offset}S tls_extension_length
            set record_offset [expr {$record_offset + 2}]
            # Check if extension length + offset equals payload length
            if {$record_offset + ($tls_extension_length & 0xffff) == $payloadlen} {
                # for each extension
                while { $record_offset < $payloadlen } {
                    binary scan $payload @${record_offset}SS tls_extension_type tls_extension_record_length
                    set tls_extension_record_length [expr {$tls_extension_record_length & 0xffff}]
                    if { $tls_extension_type == 17516 } {
                        # if it's extension type 17516
                        binary scan $payload @${record_offset}A[expr {$tls_extension_record_length +4}] tls_extension_17516
                        set ext_start $record_offset
                        set ext_len [expr {$tls_extension_record_length + 4}]
                        set record_offset [expr {$record_offset + $tls_extension_record_length + 4}]
                    } else {
                        # skip over other extensions
                        set record_offset [expr {$record_offset + $tls_extension_record_length + 4}]
                    }
                }
            }
        }
    }
    unset -nocomplain payload payloadlen tls_record_content_type tls_handshake_action tls_handshake_sessidlen record_offset tls_ciphlen tls_complen tls_extension_type tls_extension_record_length tls_supported_versions_length tls_supported_versions
    if {$tls_extension_17516 ne ""} {
        # remove extension from Payload
        TCP::payload replace $ext_start $ext_len ""
        # Change extension Length
        TCP::payload replace $tls_extension_length_start 2 [binary format S [expr {($tls_extension_length & 0xffff) - $ext_len}]]
        # Change Handshake Length
        TCP::payload replace 6 3 [binary format H6 [format %06X [expr {$tls_handshakelen - $ext_len}]]]
        # Change Message Length
        TCP::payload replace 3 2 [binary format S [expr {($tls_recordlen & 0xffff) - $ext_len}]]
    }
    SSL::enable
    TCP::release
}

when SERVERSSL_CLIENTHELLO_SEND {
    if { $tls_extension_17516 ne "" } {
        SSL::extensions insert $tls_extension_17516
    }
}

Hum ok…DO you have a summary of what you have propose and the purpose please in order to understand

The goal of this code is:

  • disable SSL profile on client side to disable TLS inspection before the code ends
  • binary search the expected extension
  • save in variable tls_extension_17516 the content of extension type 17516
  • save in variable ext_start the index of beginning of extension 17516
  • save in variable ext_len the extension 17516 length
  • replace in payload the extension with no value (from ext_start with length ext_len)

missing in the code :

  • change extension length to new value
  • change handshake length to new value

I will update the code with missing commands later.

I just updated the code above.

Can you try it and update this thread?

Hi Stanislas,

Our IT team try to implement your script, this is what we got in BIGIP outgoing packet (extracted for wireshark) regarding what is in the tls_extension_17516 variable:

Dl\300\200\v\001\300\200\b\300\200\300\200\300\2003\302\242\302\231^\302\235\r\n\r\n

image_259689.png

This what we have before the BIGIP (in hexa from wireshark)

Hi Stanislas,

Our IT team try to implement your script, this is what we got in BIGIP outgoing packet (extracted for wireshark) regarding what is in the tls_extension_17516 variable:

Dl\300\200\v\001\300\200\b\300\200\300\200\300\2003\302\242\302\231^\302\235\r\n\r\n

image_259689.png

This what we have before the BIGIP (in hexa from wireshark)

Data: 010008000000 33a2995e9d (in bold the value which is inserted i.e221771292317 in decimal)

Does it mean the client side TLS session succeed now after removing the attribute? (it was unsuccessful without this code)

So next problem to solve is to add the attribute on server side.

The TLS session succeed now but the problem is to fetch the data value in the extension and sent it to a proper format (hexa, decimal ou string) to the server.

The TLS session succeed now but the problem is to fetch the data value in the extension and sent it to a proper format (hexa, decimal ou string) to the server.

According to your variable, you must have this:

$ tclsh
% set var Dl\300\200\v\001\300\200\b\300\200\300\200\300\2003\302\242\302\231^\302\235\r\n\r\n
% binary scan $var SSa* type length data
3
% echo $type
17516
% echo $length
-16256
% expr {$length & 0xffff}
49280
% binary scan $data H* data_hex
1
% echo $data_hex
0b01c08008c080c080c08033c2a2c2995ec29d0d0a0d0a
% string length $data
23

There is a issue with length which must not be negative. This is because binary command returns signed integers.

I will upload a new version of the code above to convert signed to unsigned integer.

Can you confirm the length value with may be wrong (negative numbers means more than 32768, but the whole TLS handshake must not be larger than 16389)

Can you do a Wireshark capture on BigIP client side?