Saturday, September 12, 2009

In this post, we will write our bare bones Ruby based SSL cipher enumerator to enumerate SSL cipher suites supported by a webserver. Without further delay, lets get started.

Basics:
The first step of every SSL communication is SSL handshake. During SSL handshake, both client and server settle on a common cipher suite to be used for communication. Client initiated "Client Hello" provides server with all the cipher suites it supports. The server responds with the cipher suite it wants to use for communication in the Server Hello message. Image below shows list of cipher suites sent out to the webserver during Client Hello request.

To successfully enumerate supported SSL ciphers, we need to initiate SSL connection with only one cipher suite (for one protocol version) at a time and observe its response.

Extending the HTTP Class
===============================
We will now extend the HTTP class to include two methods that will help us request application home page with one cipher suite at a time. Since classes in Ruby are not closed, we will extend the existing HTTP class.

module Net

class HTTP

def set_context=(value)

@ssl_context = OpenSSL::SSL::SSLContext.new

@ssl_context &&= OpenSSL::SSL::SSLContext.new(value)

end

ssl_context_accessor :ciphers

end

end

Lines 3-6:def set_context=(value)@ssl_context = OpenSSL::SSL::SSLContext.new
@ssl_context &&= OpenSSL::SSL::SSLContext.new(value)
endThe set_context= method helps us set context for one HTTP request. By setting context for a HTTP request, we enforce use of cipher suites and protocol version of our choice.

Line 8:ssl_context_accessor :ciphers
It creates two methods:

ciphers : Return the cipher suite values used.

ciphers= : Set cipher suite for current request.

For more information about ssl_context_accessor, please refer to please refer to net/https.rb in you ruby installation directory.

Line 1:protocol_versions.each do |version|
Loop through the cipher versions we are testing and pass on the value to the code block.Line 2:cipher_set = OpenSSL::SSL::SSLContext.new(version).ciphers
Create new context for a give protocol version and return all the ciphers supported by OpenSSL version with which your ruby installation was compiled. The returned value is an array of array. Each element of the returned array is of following format: [name, version, bits, alg_bits]. Here name is cipher suite name, version is the protocol version (SSLv2, TLSv1/SSLv3), bits is key length in bits and alg_bits is the supported key length for the encryption algorithm.

Line 7 and 8:request = Net::HTTP.new(target_url, port)request.use_ssl = true
Creates a new HTTP object and enables use of SSL for communication.Line 9: request.set_context = version
Sets context of current request to protocol vesion provided. It is very important to set the right context when we want to restrict the cipher suites used. An example should be able to demonstrate it with more clarity.

Consider following two code snips and corresponding packet capture in wireshark. For purpse of experimentation, a connection request was initiated to mail.google.com and "Client Hello" was observed using Wireshark for both the snips. It can be clearly seen in the screenshots that when context is not provided, it is possible that multiple cipher suites for a given cipher name can be chosen. In this case, "RC4-MD5" cipher suite is present in both TLSv1/SSLv3 and SSLv2. When context is not set to SSLv2 or TLSv1/SSLv3, the "Client Hello" will include two cipher suites; one for TLSv1/SSLv3 and other for SSLv2. This results in incorrect enumeration.

For example, certain websites may not allow use of SSLv2. When connection attemps are made using "RC4-MD5" cipher without setting proper context, connection attempts might be successful because the "Client Hello" now contains an additional cipher suite for SSLv3/TLSv1.

Attempt connection to the remote host and fetch the home page. OpenSSL::SSL::SSLError exception is raised when connection attempts fail due to cipher suite mismatch. All other exceptions are ignored. Success and failure of connection combined with exception decides if the cipher suite was supported or rejected.