Fix #1165
This PR fixes #1165 by changing resend_if_hello_retry_request() to modify the initial ClientHello rather than having it call prepare_tls_clienthello() to try to generate a new ClientHello that is almost the same as the first. The modification is done using a revised version of create_client_simulation_tls_clienthello(), which is now renamed as modify_clienthello(). Since prepare_tls_clienthello() is no longer used to create a second ClientHello message, argument 7 to that function is no longer needed.
This commit is contained in:
parent
e9c5435c0a
commit
d3c29f24e4
152
testssl.sh
152
testssl.sh
|
@ -4186,12 +4186,22 @@ run_cipher_per_proto() {
|
|||
}
|
||||
|
||||
# arg1 is an ASCII-HEX encoded SSLv3 or TLS ClientHello.
|
||||
# If the ClientHello contains a server name extension, then
|
||||
# either:
|
||||
# arg2: new key_share extension (only present to response to HelloRetryRequest)
|
||||
# arg3: cookie extension (if needed for response to HelloRetryRequest)
|
||||
#
|
||||
# This function may be used to either modify a ClientHello for client simulation
|
||||
# or to create a second ClientHello in response to a HelloRetryRequest.
|
||||
# If arg2 is present, then this is a response to a HelloRetryRequest, so the
|
||||
# function replaces the key_share extension with arg2 and adds the cookie
|
||||
# extension, if present.
|
||||
# If arg2 is not present, then this is an initial ClientHello for client simulation.
|
||||
# In this case, if the provided ClientHello contains a server name extension,
|
||||
# then either:
|
||||
# 1) replace it with one corresponding to $SNI; or
|
||||
# 2) remove it, if $SNI is empty
|
||||
create_client_simulation_tls_clienthello() {
|
||||
modify_clienthello() {
|
||||
local tls_handshake_ascii="$1"
|
||||
local new_key_share="$2" cookie="$3"
|
||||
local -i len offset tls_handshake_ascii_len len_all len_clienthello
|
||||
local -i len_extensions len_extension
|
||||
local tls_content_type tls_version_reclayer handshake_msg_type tls_clientversion
|
||||
|
@ -4231,19 +4241,15 @@ create_client_simulation_tls_clienthello() {
|
|||
fi
|
||||
|
||||
len_extensions=2*$(hex2dec "${tls_handshake_ascii:$offset:4}")
|
||||
offset=$offset+4
|
||||
offset+=4
|
||||
for (( 1; offset < tls_handshake_ascii_len; 1 )); do
|
||||
extension_type="${tls_handshake_ascii:$offset:4}"
|
||||
offset=$offset+4
|
||||
offset+=+4
|
||||
len_extension=2*$(hex2dec "${tls_handshake_ascii:$offset:4}")
|
||||
|
||||
if [[ "$extension_type" != "0000" ]]; then
|
||||
# The extension will just be copied into the revised ClientHello
|
||||
offset=$offset-4
|
||||
len=$len_extension+8
|
||||
tls_extensions+="${tls_handshake_ascii:$offset:$len}"
|
||||
offset=$offset+$len
|
||||
else
|
||||
if [[ "$extension_type" == 0000 ]] && [[ -z "$key_share" ]]; then
|
||||
# If this is an initial ClientHello, then either remove
|
||||
# the SNI extension or replace it with the correct server name.
|
||||
sni_extension_found=true
|
||||
if [[ -n "$SNI" ]]; then
|
||||
# Create a server name extension that corresponds to $SNI
|
||||
|
@ -4255,12 +4261,23 @@ create_client_simulation_tls_clienthello() {
|
|||
len_sni_listlen=$(printf "%02x\n" $((len_servername+3)))
|
||||
len_sni_ext=$(printf "%02x\n" $((len_servername+5)))
|
||||
tls_extensions+="000000${len_sni_ext}00${len_sni_listlen}0000${len_servername_hex}${servername_hexstr}"
|
||||
offset=$offset+$len_extension+4
|
||||
offset+=$len_extension+4
|
||||
fi
|
||||
elif [[ "$extension_type" != 00$KEY_SHARE_EXTN_NR ]] || [[ -z "$key_share" ]]; then
|
||||
# If this is in response to a HelloRetryRequest, then do
|
||||
# not copy over the old key_share extension, but
|
||||
# all other extensions should be copied into the new ClientHello.
|
||||
offset=$offset-4
|
||||
len=$len_extension+8
|
||||
tls_extensions+="${tls_handshake_ascii:$offset:$len}"
|
||||
offset+=$len
|
||||
else
|
||||
offset+=$len_extension+4
|
||||
fi
|
||||
done
|
||||
tls_extensions+="$new_key_share$cookie"
|
||||
|
||||
if ! $sni_extension_found; then
|
||||
if ! "$sni_extension_found" && [[ -z "$key_share" ]]; then
|
||||
tm_out "$tls_handshake_ascii"
|
||||
return 0
|
||||
fi
|
||||
|
@ -4294,7 +4311,7 @@ client_simulation_sockets() {
|
|||
local -i sid_len offset1 offset2
|
||||
|
||||
if [[ "${1:0:4}" == "1603" ]]; then
|
||||
clienthello="$(create_client_simulation_tls_clienthello "$1")"
|
||||
clienthello="$(modify_clienthello "$1")"
|
||||
TLS_CLIENT_HELLO="${clienthello:10}"
|
||||
else
|
||||
clienthello="$1"
|
||||
|
@ -4338,7 +4355,7 @@ client_simulation_sockets() {
|
|||
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
|
||||
|
||||
# Check if the response is a HelloRetryRequest.
|
||||
resend_if_hello_retry_request "$tls_hello_ascii" "$cipher_list_2send" "$4" "$process_full"
|
||||
resend_if_hello_retry_request "$clienthello" "$tls_hello_ascii"
|
||||
ret=$?
|
||||
if [[ $ret -eq 2 ]]; then
|
||||
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE")
|
||||
|
@ -11886,12 +11903,11 @@ generate_key_share_extension() {
|
|||
# ARG4: (optional) additional request extensions
|
||||
# ARG5: (optional): "true" if ClientHello should advertise compression methods other than "NULL"
|
||||
# ARG6: (optional): "false" if prepare_tls_clienthello() should not open a new socket
|
||||
# ARG7: (optional): "true" if this is a second ClientHello that follows receipt of a HelloRetryRequest
|
||||
#
|
||||
prepare_tls_clienthello() {
|
||||
local tls_low_byte="$1" tls_legacy_version="$1"
|
||||
local process_full="$3"
|
||||
local new_socket=true is_second_clienthello=false
|
||||
local new_socket=true
|
||||
local tls_word_reclayer="03, 01" # the first TLS version number is the record layer and always 0301
|
||||
# -- except: SSLv3 and second ClientHello after HelloRetryRequest
|
||||
local servername_hexstr len_servername len_servername_hex
|
||||
|
@ -11912,7 +11928,6 @@ prepare_tls_clienthello() {
|
|||
# TLSv1.3 ClientHello messages MUST specify only the NULL compression method.
|
||||
[[ "$5" == "true" ]] && [[ "0x$tls_low_byte" -le "0x03" ]] && offer_compression=true
|
||||
[[ "$6" == "false" ]] && new_socket=false
|
||||
[[ "$7" == "true" ]] && is_second_clienthello=true
|
||||
|
||||
cipher_suites="$2" # we don't have the leading \x here so string length is two byte less, see next
|
||||
len_ciph_suites_byte=${#cipher_suites}
|
||||
|
@ -12242,7 +12257,6 @@ prepare_tls_clienthello() {
|
|||
# if we have SSLv3, the first occurrence of TLS protocol -- record layer -- is SSLv3, otherwise TLS 1.0,
|
||||
# except in the case of a second ClientHello in TLS 1.3, in which case it is TLS 1.2.
|
||||
[[ $tls_low_byte == "00" ]] && tls_word_reclayer="03, 00"
|
||||
"$is_second_clienthello" && tls_word_reclayer="03, 03"
|
||||
|
||||
[[ 0x$tls_legacy_version -ge 0x04 ]] && tls_legacy_version="03"
|
||||
|
||||
|
@ -12303,37 +12317,34 @@ prepare_tls_clienthello() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# arg1: The server's response
|
||||
# arg2: CIPHER_SUITES string (lowercase, and in the format output by code2network())
|
||||
# arg3: (optional) additional request extensions
|
||||
# arg4: "all" or "all+" - process full response (including Certificate and certificate_status handshake messages)
|
||||
# "ephemeralkey" - extract the server's ephemeral key (if any)
|
||||
# arg1: The original ClientHello
|
||||
# arg2: The server's response
|
||||
# Return 0 if the response is not a HelloRetryRequest.
|
||||
# Return 1 if the response is a malformed HelloRetryRequest or if a new ClientHello cannot be sent.
|
||||
# Return 2 if the response is a HelloRetryRequest, and sending a new ClientHello succeeded.
|
||||
# Return 6 if the response is a HelloRetryRequest, and sending a new ClientHello failed.
|
||||
resend_if_hello_retry_request() {
|
||||
local tls_hello_ascii="$1"
|
||||
local cipher_list_2send="$2"
|
||||
local process_full="$4"
|
||||
local msg_type tls_low_byte server_version cipher_suite rfc_cipher_suite key_share=""
|
||||
local original_clienthello="$1"
|
||||
local tls_hello_ascii="$2"
|
||||
local msg_type tls_low_byte server_version cipher_suite rfc_cipher_suite
|
||||
local key_share="" new_key_share="" cookie="" second_clienthello data=""
|
||||
local -i i j msg_len tls_hello_ascii_len sid_len
|
||||
local -i extns_offset hrr_extns_len extra_extensions_len len_extn
|
||||
local extra_extensions extn_type part2 new_extra_extns="" new_key_share temp
|
||||
local extra_extensions extn_type part2 new_extra_extns=""
|
||||
local sha256_hrr="CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C"
|
||||
|
||||
tls_hello_ascii_len=${#tls_hello_ascii}
|
||||
# A HelloRetryRequest is at least 13 bytes long
|
||||
[[ $tls_hello_ascii_len -lt 26 ]] && return 0
|
||||
# A HelloRetryRequest is a handshake message (16) with a major record version of 03.
|
||||
[[ "${tls_hello_ascii:0:4}" != "1603" ]] && return 0
|
||||
[[ "${tls_hello_ascii:0:4}" != 1603 ]] && return 0
|
||||
msg_type="${tls_hello_ascii:10:2}"
|
||||
if [[ "$msg_type" == "02" ]]; then
|
||||
if [[ "$msg_type" == 02 ]]; then
|
||||
# A HRR is a ServerHello with a Random value equal to the
|
||||
# SHA-256 hash of "HelloRetryRequest"
|
||||
[[ $tls_hello_ascii_len -lt 76 ]] && return 0
|
||||
[[ "${tls_hello_ascii:22:64}" != "$sha256_hrr" ]] && return 0
|
||||
elif [[ "$msg_type" != "06" ]]; then
|
||||
[[ "${tls_hello_ascii:22:64}" != $sha256_hrr ]] && return 0
|
||||
elif [[ "$msg_type" != 06 ]]; then
|
||||
# The handshake type for hello_retry_request in draft versions was 06.
|
||||
return 0
|
||||
fi
|
||||
|
@ -12364,7 +12375,7 @@ resend_if_hello_retry_request() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$msg_type" == "06" ]]; then
|
||||
if [[ "$msg_type" == 06 ]]; then
|
||||
server_version="${tls_hello_ascii:18:4}"
|
||||
if [[ 0x$server_version -ge 0x7f13 ]]; then
|
||||
# Starting with TLSv1.3 draft 19, a HelloRetryRequest is at least 15 bytes long
|
||||
|
@ -12399,12 +12410,12 @@ resend_if_hello_retry_request() {
|
|||
debugme echo "malformed HelloRetryRequest"
|
||||
return 1
|
||||
fi
|
||||
if [[ "$extn_type" == "002C" ]]; then
|
||||
if [[ "$extn_type" == 002C ]]; then
|
||||
# If the HRR includes a cookie extension, then it needs to be
|
||||
# included in the next ClientHello.
|
||||
j=8+$len_extn
|
||||
new_extra_extns+="${tls_hello_ascii:i:j}"
|
||||
elif [[ "$extn_type" == "00$KEY_SHARE_EXTN_NR" ]]; then
|
||||
cookie="${tls_hello_ascii:i:j}"
|
||||
elif [[ "$extn_type" == 00$KEY_SHARE_EXTN_NR ]]; then
|
||||
# If the HRR includes a key_share extension, then it specifies the
|
||||
# group to be used in the next ClientHello. So, create a key_share
|
||||
# extension that specifies this group.
|
||||
|
@ -12413,11 +12424,11 @@ resend_if_hello_retry_request() {
|
|||
return 1
|
||||
fi
|
||||
key_share="${tls_hello_ascii:j:4}"
|
||||
new_key_share="$(generate_key_share_extension "000a00040002$key_share" "$process_full")"
|
||||
new_key_share="$(generate_key_share_extension "000a00040002$key_share" "ephemeralkey")"
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
[[ -z "$new_key_share" ]] && return 1
|
||||
new_extra_extns+="${new_key_share//,/}"
|
||||
elif [[ "$extn_type" == "002B" ]]; then
|
||||
new_key_share="${new_key_share//,/}"
|
||||
elif [[ "$extn_type" == 002B ]]; then
|
||||
if [[ $len_extn -ne 4 ]]; then
|
||||
debugme echo "malformed supported versions extension in HelloRetryRequest"
|
||||
return 1
|
||||
|
@ -12425,31 +12436,6 @@ resend_if_hello_retry_request() {
|
|||
server_version="${tls_hello_ascii:j:4}"
|
||||
fi
|
||||
done
|
||||
if [[ -n "$new_extra_extns" ]]; then
|
||||
temp="$new_extra_extns"
|
||||
extra_extensions_len=${#temp}
|
||||
new_extra_extns=""
|
||||
for (( i=0 ; i < extra_extensions_len; i=i+2 )); do
|
||||
new_extra_extns+=",${temp:i:2}"
|
||||
done
|
||||
new_extra_extns="${new_extra_extns:1}"
|
||||
fi
|
||||
|
||||
# Include any extra extensions that were included in the first ClientHello,
|
||||
# except key_share and cookie.
|
||||
extra_extensions="$(strip_spaces "$(tolower "$3")")"
|
||||
extra_extensions_len=${#extra_extensions}
|
||||
for (( i=0; i < extra_extensions_len; i=i+12+len_extn )); do
|
||||
part2=$i+3
|
||||
extn_type="${extra_extensions:i:2}${extra_extensions:part2:2}"
|
||||
j=$i+6
|
||||
part2=$j+3
|
||||
len_extn=3*$(hex2dec "${extra_extensions:j:2}${extra_extensions:part2:2}")
|
||||
if [[ "$extn_type" != "00$KEY_SHARE_EXTN_NR" ]] && [[ "$extn_type" != "002c" ]]; then
|
||||
j=11+$len_extn
|
||||
new_extra_extns+=",${extra_extensions:i:j}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $DEBUG -ge 3 ]]; then
|
||||
echo "TLS message fragments:"
|
||||
|
@ -12467,7 +12453,7 @@ resend_if_hello_retry_request() {
|
|||
echo
|
||||
echo "TLS hello retry request message:"
|
||||
echo " server version: $server_version"
|
||||
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f13 ]]; then
|
||||
if [[ "$server_version" == 0304 ]] || [[ 0x$server_version -ge 0x7f13 ]]; then
|
||||
echo -n " cipher suite: $cipher_suite"
|
||||
if [[ $TLS_NR_CIPHERS -ne 0 ]]; then
|
||||
if [[ "${cipher_suite:0:2}" == "00" ]]; then
|
||||
|
@ -12485,30 +12471,27 @@ resend_if_hello_retry_request() {
|
|||
fi
|
||||
fi
|
||||
[[ -n "$key_share" ]] && echo " key share: 0x$key_share"
|
||||
[[ -n "$cookie" ]] && echo " cookie: $cookie"
|
||||
fi
|
||||
|
||||
if [[ "${server_version:0:2}" == "7F" ]]; then
|
||||
tls_low_byte="04"
|
||||
else
|
||||
tls_low_byte="${server_version:2:2}"
|
||||
# Starting with TLSv1.3 draft 24, the second ClientHello should specify a record layer version of 0x0303
|
||||
if [[ "$server_version" == 0304 ]] || [[ 0x$server_version -ge 0x7f18 ]]; then
|
||||
original_clienthello="160303${original_clienthello:6}"
|
||||
fi
|
||||
|
||||
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f16 ]]; then
|
||||
if [[ "$server_version" == 0304 ]] || [[ 0x$server_version -ge 0x7f16 ]]; then
|
||||
# Send a dummy change cipher spec for middlebox compatibility.
|
||||
debugme echo -en "\nsending dummy change cipher spec... "
|
||||
socksend ", x14, x03, x03 ,x00, x01, x01" 0
|
||||
fi
|
||||
debugme echo -en "\nsending second client hello... "
|
||||
# Starting with TLSv1.3 draft 24, the second ClientHello should specify a record layer version of 0x0303
|
||||
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f18 ]]; then
|
||||
prepare_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$new_extra_extns" "" "false" "true"
|
||||
else
|
||||
prepare_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$new_extra_extns" "" "false"
|
||||
fi
|
||||
if [[ $? -ne 0 ]]; then
|
||||
debugme echo "stuck on sending: $ret"
|
||||
return 6
|
||||
fi
|
||||
second_clienthello="$(modify_clienthello "$original_clienthello" "$new_key_share" "$cookie")"
|
||||
msg_len=${#second_clienthello}
|
||||
for (( i=0; i < msg_len; i=i+2 )); do
|
||||
data+=", ${second_clienthello:i:2}"
|
||||
done
|
||||
debugme echo -n "sending client hello... "
|
||||
socksend_clienthello "$data" $USLEEP_SND
|
||||
sockread_serverhello 32768
|
||||
return 2
|
||||
}
|
||||
|
@ -12533,7 +12516,7 @@ tls_sockets() {
|
|||
local cipher_list_2send
|
||||
local sock_reply_file2 sock_reply_file3
|
||||
local tls_hello_ascii next_packet
|
||||
local clienthello1 hrr=""
|
||||
local clienthello1 original_clienthello hrr=""
|
||||
local process_full="$3" offer_compression=false skip=false
|
||||
local close_connection=true
|
||||
local -i hello_done=0
|
||||
|
@ -12568,7 +12551,8 @@ tls_sockets() {
|
|||
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
|
||||
|
||||
# Check if the response is a HelloRetryRequest.
|
||||
resend_if_hello_retry_request "$tls_hello_ascii" "$cipher_list_2send" "$4" "$process_full"
|
||||
original_clienthello="160301$(printf "%04x" "${#clienthello1}")$clienthello1"
|
||||
resend_if_hello_retry_request "$original_clienthello" "$tls_hello_ascii"
|
||||
ret=$?
|
||||
if [[ $ret -eq 2 ]]; then
|
||||
hrr="${tls_hello_ascii:10}"
|
||||
|
|
Loading…
Reference in New Issue