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:
David Cooper 2018-12-04 14:07:39 -05:00 committed by GitHub
parent e9c5435c0a
commit d3c29f24e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 68 additions and 84 deletions

View File

@ -4186,12 +4186,22 @@ run_cipher_per_proto() {
} }
# arg1 is an ASCII-HEX encoded SSLv3 or TLS ClientHello. # arg1 is an ASCII-HEX encoded SSLv3 or TLS ClientHello.
# If the ClientHello contains a server name extension, then # arg2: new key_share extension (only present to response to HelloRetryRequest)
# either: # 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 # 1) replace it with one corresponding to $SNI; or
# 2) remove it, if $SNI is empty # 2) remove it, if $SNI is empty
create_client_simulation_tls_clienthello() { modify_clienthello() {
local tls_handshake_ascii="$1" 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 offset tls_handshake_ascii_len len_all len_clienthello
local -i len_extensions len_extension local -i len_extensions len_extension
local tls_content_type tls_version_reclayer handshake_msg_type tls_clientversion local tls_content_type tls_version_reclayer handshake_msg_type tls_clientversion
@ -4231,19 +4241,15 @@ create_client_simulation_tls_clienthello() {
fi fi
len_extensions=2*$(hex2dec "${tls_handshake_ascii:$offset:4}") len_extensions=2*$(hex2dec "${tls_handshake_ascii:$offset:4}")
offset=$offset+4 offset+=4
for (( 1; offset < tls_handshake_ascii_len; 1 )); do for (( 1; offset < tls_handshake_ascii_len; 1 )); do
extension_type="${tls_handshake_ascii:$offset:4}" extension_type="${tls_handshake_ascii:$offset:4}"
offset=$offset+4 offset+=+4
len_extension=2*$(hex2dec "${tls_handshake_ascii:$offset:4}") len_extension=2*$(hex2dec "${tls_handshake_ascii:$offset:4}")
if [[ "$extension_type" != "0000" ]]; then if [[ "$extension_type" == 0000 ]] && [[ -z "$key_share" ]]; then
# The extension will just be copied into the revised ClientHello # If this is an initial ClientHello, then either remove
offset=$offset-4 # the SNI extension or replace it with the correct server name.
len=$len_extension+8
tls_extensions+="${tls_handshake_ascii:$offset:$len}"
offset=$offset+$len
else
sni_extension_found=true sni_extension_found=true
if [[ -n "$SNI" ]]; then if [[ -n "$SNI" ]]; then
# Create a server name extension that corresponds to $SNI # 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_listlen=$(printf "%02x\n" $((len_servername+3)))
len_sni_ext=$(printf "%02x\n" $((len_servername+5))) 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}" 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 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 fi
done 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" tm_out "$tls_handshake_ascii"
return 0 return 0
fi fi
@ -4294,7 +4311,7 @@ client_simulation_sockets() {
local -i sid_len offset1 offset2 local -i sid_len offset1 offset2
if [[ "${1:0:4}" == "1603" ]]; then if [[ "${1:0:4}" == "1603" ]]; then
clienthello="$(create_client_simulation_tls_clienthello "$1")" clienthello="$(modify_clienthello "$1")"
TLS_CLIENT_HELLO="${clienthello:10}" TLS_CLIENT_HELLO="${clienthello:10}"
else else
clienthello="$1" clienthello="$1"
@ -4338,7 +4355,7 @@ client_simulation_sockets() {
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}" tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
# Check if the response is a HelloRetryRequest. # 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=$? ret=$?
if [[ $ret -eq 2 ]]; then if [[ $ret -eq 2 ]]; then
tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE") 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 # ARG4: (optional) additional request extensions
# ARG5: (optional): "true" if ClientHello should advertise compression methods other than "NULL" # 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 # 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() { prepare_tls_clienthello() {
local tls_low_byte="$1" tls_legacy_version="$1" local tls_low_byte="$1" tls_legacy_version="$1"
local process_full="$3" 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 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 # -- except: SSLv3 and second ClientHello after HelloRetryRequest
local servername_hexstr len_servername len_servername_hex 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. # TLSv1.3 ClientHello messages MUST specify only the NULL compression method.
[[ "$5" == "true" ]] && [[ "0x$tls_low_byte" -le "0x03" ]] && offer_compression=true [[ "$5" == "true" ]] && [[ "0x$tls_low_byte" -le "0x03" ]] && offer_compression=true
[[ "$6" == "false" ]] && new_socket=false [[ "$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 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} 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, # 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. # 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" [[ $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" [[ 0x$tls_legacy_version -ge 0x04 ]] && tls_legacy_version="03"
@ -12303,37 +12317,34 @@ prepare_tls_clienthello() {
return 0 return 0
} }
# arg1: The server's response # arg1: The original ClientHello
# arg2: CIPHER_SUITES string (lowercase, and in the format output by code2network()) # arg2: The server's response
# 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)
# Return 0 if the response is not a HelloRetryRequest. # 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 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 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. # Return 6 if the response is a HelloRetryRequest, and sending a new ClientHello failed.
resend_if_hello_retry_request() { resend_if_hello_retry_request() {
local tls_hello_ascii="$1" local original_clienthello="$1"
local cipher_list_2send="$2" local tls_hello_ascii="$2"
local process_full="$4" local msg_type tls_low_byte server_version cipher_suite rfc_cipher_suite
local msg_type tls_low_byte server_version cipher_suite rfc_cipher_suite key_share="" local key_share="" new_key_share="" cookie="" second_clienthello data=""
local -i i j msg_len tls_hello_ascii_len sid_len 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 -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" local sha256_hrr="CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C"
tls_hello_ascii_len=${#tls_hello_ascii} tls_hello_ascii_len=${#tls_hello_ascii}
# A HelloRetryRequest is at least 13 bytes long # A HelloRetryRequest is at least 13 bytes long
[[ $tls_hello_ascii_len -lt 26 ]] && return 0 [[ $tls_hello_ascii_len -lt 26 ]] && return 0
# A HelloRetryRequest is a handshake message (16) with a major record version of 03. # 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}" 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 # A HRR is a ServerHello with a Random value equal to the
# SHA-256 hash of "HelloRetryRequest" # SHA-256 hash of "HelloRetryRequest"
[[ $tls_hello_ascii_len -lt 76 ]] && return 0 [[ $tls_hello_ascii_len -lt 76 ]] && return 0
[[ "${tls_hello_ascii:22:64}" != "$sha256_hrr" ]] && return 0 [[ "${tls_hello_ascii:22:64}" != $sha256_hrr ]] && return 0
elif [[ "$msg_type" != "06" ]]; then elif [[ "$msg_type" != 06 ]]; then
# The handshake type for hello_retry_request in draft versions was 06. # The handshake type for hello_retry_request in draft versions was 06.
return 0 return 0
fi fi
@ -12364,7 +12375,7 @@ resend_if_hello_retry_request() {
return 1 return 1
fi fi
if [[ "$msg_type" == "06" ]]; then if [[ "$msg_type" == 06 ]]; then
server_version="${tls_hello_ascii:18:4}" server_version="${tls_hello_ascii:18:4}"
if [[ 0x$server_version -ge 0x7f13 ]]; then if [[ 0x$server_version -ge 0x7f13 ]]; then
# Starting with TLSv1.3 draft 19, a HelloRetryRequest is at least 15 bytes long # 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" debugme echo "malformed HelloRetryRequest"
return 1 return 1
fi fi
if [[ "$extn_type" == "002C" ]]; then if [[ "$extn_type" == 002C ]]; then
# If the HRR includes a cookie extension, then it needs to be # If the HRR includes a cookie extension, then it needs to be
# included in the next ClientHello. # included in the next ClientHello.
j=8+$len_extn j=8+$len_extn
new_extra_extns+="${tls_hello_ascii:i:j}" cookie="${tls_hello_ascii:i:j}"
elif [[ "$extn_type" == "00$KEY_SHARE_EXTN_NR" ]]; then elif [[ "$extn_type" == 00$KEY_SHARE_EXTN_NR ]]; then
# If the HRR includes a key_share extension, then it specifies the # 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 # group to be used in the next ClientHello. So, create a key_share
# extension that specifies this group. # extension that specifies this group.
@ -12413,11 +12424,11 @@ resend_if_hello_retry_request() {
return 1 return 1
fi fi
key_share="${tls_hello_ascii:j:4}" 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 [[ $? -ne 0 ]] && return 1
[[ -z "$new_key_share" ]] && return 1 [[ -z "$new_key_share" ]] && return 1
new_extra_extns+="${new_key_share//,/}" new_key_share="${new_key_share//,/}"
elif [[ "$extn_type" == "002B" ]]; then elif [[ "$extn_type" == 002B ]]; then
if [[ $len_extn -ne 4 ]]; then if [[ $len_extn -ne 4 ]]; then
debugme echo "malformed supported versions extension in HelloRetryRequest" debugme echo "malformed supported versions extension in HelloRetryRequest"
return 1 return 1
@ -12425,31 +12436,6 @@ resend_if_hello_retry_request() {
server_version="${tls_hello_ascii:j:4}" server_version="${tls_hello_ascii:j:4}"
fi fi
done 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 if [[ $DEBUG -ge 3 ]]; then
echo "TLS message fragments:" echo "TLS message fragments:"
@ -12467,7 +12453,7 @@ resend_if_hello_retry_request() {
echo echo
echo "TLS hello retry request message:" echo "TLS hello retry request message:"
echo " server version: $server_version" 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" echo -n " cipher suite: $cipher_suite"
if [[ $TLS_NR_CIPHERS -ne 0 ]]; then if [[ $TLS_NR_CIPHERS -ne 0 ]]; then
if [[ "${cipher_suite:0:2}" == "00" ]]; then if [[ "${cipher_suite:0:2}" == "00" ]]; then
@ -12485,30 +12471,27 @@ resend_if_hello_retry_request() {
fi fi
fi fi
[[ -n "$key_share" ]] && echo " key share: 0x$key_share" [[ -n "$key_share" ]] && echo " key share: 0x$key_share"
[[ -n "$cookie" ]] && echo " cookie: $cookie"
fi fi
if [[ "${server_version:0:2}" == "7F" ]]; then # Starting with TLSv1.3 draft 24, the second ClientHello should specify a record layer version of 0x0303
tls_low_byte="04" if [[ "$server_version" == 0304 ]] || [[ 0x$server_version -ge 0x7f18 ]]; then
else original_clienthello="160303${original_clienthello:6}"
tls_low_byte="${server_version:2:2}"
fi 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. # Send a dummy change cipher spec for middlebox compatibility.
debugme echo -en "\nsending dummy change cipher spec... " debugme echo -en "\nsending dummy change cipher spec... "
socksend ", x14, x03, x03 ,x00, x01, x01" 0 socksend ", x14, x03, x03 ,x00, x01, x01" 0
fi fi
debugme echo -en "\nsending second client hello... " debugme echo -en "\nsending second client hello... "
# Starting with TLSv1.3 draft 24, the second ClientHello should specify a record layer version of 0x0303 second_clienthello="$(modify_clienthello "$original_clienthello" "$new_key_share" "$cookie")"
if [[ "$server_version" == "0304" ]] || [[ 0x$server_version -ge 0x7f18 ]]; then msg_len=${#second_clienthello}
prepare_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$new_extra_extns" "" "false" "true" for (( i=0; i < msg_len; i=i+2 )); do
else data+=", ${second_clienthello:i:2}"
prepare_tls_clienthello "$tls_low_byte" "$cipher_list_2send" "$process_full" "$new_extra_extns" "" "false" done
fi debugme echo -n "sending client hello... "
if [[ $? -ne 0 ]]; then socksend_clienthello "$data" $USLEEP_SND
debugme echo "stuck on sending: $ret"
return 6
fi
sockread_serverhello 32768 sockread_serverhello 32768
return 2 return 2
} }
@ -12533,7 +12516,7 @@ tls_sockets() {
local cipher_list_2send local cipher_list_2send
local sock_reply_file2 sock_reply_file3 local sock_reply_file2 sock_reply_file3
local tls_hello_ascii next_packet local tls_hello_ascii next_packet
local clienthello1 hrr="" local clienthello1 original_clienthello hrr=""
local process_full="$3" offer_compression=false skip=false local process_full="$3" offer_compression=false skip=false
local close_connection=true local close_connection=true
local -i hello_done=0 local -i hello_done=0
@ -12568,7 +12551,8 @@ tls_sockets() {
tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}" tls_hello_ascii="${tls_hello_ascii%%[!0-9A-F]*}"
# Check if the response is a HelloRetryRequest. # 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=$? ret=$?
if [[ $ret -eq 2 ]]; then if [[ $ret -eq 2 ]]; then
hrr="${tls_hello_ascii:10}" hrr="${tls_hello_ascii:10}"