« June 2009 | Main | August 2009 »

8 posts from July 2009

07/23/2009

Apache: Forcing the Server's SSL Cipher on the Client

Normally, in an SSL conversation, the client presents its preferred cipher to use and as long as the server that the client is negotiating a connection with supports it, that cipher will be used.  Suppose though that there are cases where you don't want to leave this up to the client to decide and you want the strongest encryption available between client and server.  A little-known apache configuration directive, and by little-known I mean I just started playing with it a few days ago, called SSLHonorCipherOrder will allow you to do just that.

Simply set the value of the directive to On and you are all set. Be wary however because Apache's preferred ssl cipher appears to be DHE-RSA-AE256-SHA; a 256 bit cipher could be costly in terms of cpu. 

When connecting with Safari 4 prior to making this change, the cipher Safari used was AES128-SHA—after making the change, I started using the 256 bit cipher. (Interestingly, with the 3.5 version of Firefox, the client's preferred cipher and apache's preferred cipher seem to be the same: DHE-RSA-AE256-SHA).

07/16/2009

cryptonark v0.1: My Perl Port of sslthing.sh

CryptoNark 0.4.1 is the most recent version. See the Release Announcement for more details.

I have posted before that sslthing.sh is a pretty useful tool for scanning and reporting on all available ciphers that a remote host supports.  Since it hasn't been updated, as far as I know, since 2004 and my shell scripting skills are not worth blogging about, I ported it over to Perl recently. My main reasons for doing this was two-fold,  the first being purely academic—to learn Perl better. The second reason for doing it was that I recently had to scan a site that required Windows Challenge Response authentication on the root of the site and since sslthing.sh connects to and performs an HTTP GET /, it wouldn't work. I've named my port 'cryptonark' (crypto nark).

Version 0.1 of cryptonark.pl is almost a direct port of sslthing, although you will need to install additional dependencies like Tie::Hash::Indexed and IO::Socket::SSL (and possibly the dependencies these modules depend on!) in order to get it working.   There are three additional features that I have added to my port that don't presently reside in sslthing (again, as far as I know).

  1. Scans for ALL ciphers available in OpenSSL including Null and Anonymous.  sslthing does not appear to do this today.
  2. Provides slightly expanded text detailing the strength level of encryption and whether or not that particular cipher supports ADH (anonymous) authentication.
  3. Since it doesn't send any data over the channel, it can even scan hosts that normally require a user id and password to retrieve content.

With this initial release, output is almost identical to sslthing's.  Here is some sample output from a recent scan of one of my highly insecure test sites running locally:
Testing SSLv2 Ciphers...
Testing SSLv3 Ciphers...
 ADH-AES256-SHA -- 256 bits, High Encryption, Anonymous Auth
 DHE-RSA-AES256-SHA -- 256 bits, High Encryption
 AES256-SHA -- 256 bits, High Encryption
 ADH-AES128-SHA -- 128 bits, High Encryption, Anonymous Auth
 DHE-RSA-AES128-SHA -- 128 bits, High Encryption
 AES128-SHA -- 128 bits, High Encryption
 EDH-RSA-DES-CBC3-SHA -- 168 bits, High Encryption
 DES-CBC3-SHA -- 168 bits, High Encryption
 ADH-DES-CBC3-SHA -- 168 bits, High Encryption, Anonymous Auth
 RC4-SHA -- 128 bits, Medium Encryption
 RC4-MD5 -- 128 bits, Medium Encryption
 ADH-RC4-MD5 -- 128 bits, Medium Encryption, Anonymous Auth
 EDH-RSA-DES-CBC-SHA -- 56 bits, Low Encryption
 DES-CBC-SHA -- 56 bits, Low Encryption
 ADH-DES-CBC-SHA -- 56 bits, Low Encryption, Anonymous Auth
 EXP-ADH-DES-CBC-SHA -- 40 bits, Export-Grade Encryption
 EXP-ADH-RC4-MD5 -- 40 bits, Export-Grade Encryption
 EXP-EDH-RSA-DES-CBC-SHA -- 40 bits, Export-Grade Encryption
 EXP-DES-CBC-SHA -- 40 bits, Export-Grade Encryption
 EXP-RC2-CBC-MD5 -- 40 bits, Export-Grade Encryption
 EXP-RC4-MD5 -- 40 bits, Export-Grade Encryption
As you can see from the above output, I've expanded the descriptions a little bit to include some additional information on encryption type and have marked those ciphers that also utilize ADH authentication (anonymous authentication).  I will probably tackle the display of this output in the next version because I'm not that happy with it yet.

As a PCI Compliance assessment tool, cryptonark.pl is good for when you need to remediate against vulnerabilities that show up in your assessment scans like: "SSL Server Allows Anonymous Authentication Vulnerability", "SSL Server Has SSLv2 Enabled Vulnerability", or "SSL Server Supports Weak Encryption Vulnerability".  Scanning your site with cryptonark.pl, you will get a list of all the ciphers that a server supports and as you remediate, you can re-run the scan quickly and easily to see the effect your remediation actions have had.  Ultimately, your mitigation goal is achieved when no ciphers other than the Medium and High grade encryption are reported back (excluding the anonymous auth ciphers) using this tool.

Below is the cryptonark.pl script (new release, v0.2, available here, main CryptoNark page available here):
#!/usr/bin/env perl -w
# Usage: ./cryptonark.pl host port
# based on sslthing.sh by blh [at] blh.se
# ported to perl by Chris M - techstacks.com
#
# cryptonark: version 0.1 - Initial Version
#
# almost a direct port, this version also tests
# null and anonymous ssl ciphers and reports 
# accordingly. A little more information is provided
# in the output. Used best if used to validate PCI-DSS
# compliance--to check that null, anonymous and weak ciphers
# are disabled. 
#
# It probably will not run straight out of the box--it requires
# IO::Socket::SSL. Tie::Hash::Indexed, although not strictly required
# is nice to have in order to order the hash lists from strongest to 
# weakest. Otherwise, the order could be random making the results a
# bit harder to read.

use strict;
use warnings;

use Tie::Hash::Indexed;

use IO::Socket::SSL;

my $host = $ARGV[0];
my $port = $ARGV[1];

my $help = "Usage: $0 <hostname> <port>";

my $key = "empty";
my $value = "empty";

my $ssl2client = "empty";
my $ssl3client = "empty";

if ( !@ARGV ) {
 print $help . "\n";
 exit 0;
}



# Populate array with OpenSSL ciphers
# Note: TLSv1 ciphers and SSLv3 ciphers are identical
# but I'm running separate checks any way.

tie my %ssl2_ciphers, 'Tie::Hash::Indexed';
tie my %ssl3_ciphers, 'Tie::Hash::Indexed';
tie my %tls1_ciphers, 'Tie::Hash::Indexed';

%ssl2_ciphers = (
 'DES-CBC3-MD5' => '168 bits, High Encryption',
 'RC2-CBC-MD5' => '128 bits, Medium Encryption',
 'RC4-MD5' => '128 bits, Medium Encryption',
 'DES-CBC-MD5' => '56 bits, Low Encryption',
 'EXP-RC2-CBC-MD5' => '40 bits, Export-Grade Encryption',
 'EXP-RC4-MD5' => '40 bits, Export-Grade Encryption'
);

%ssl3_ciphers = (
 'ADH-AES256-SHA' => '256 bits, High Encryption, Anonymous Auth',
 'DHE-RSA-AES256-SHA' => '256 bits, High Encryption',
 'DHE-DSS-AES256-SHA' => '256 bits, High Encryption',
 'AES256-SHA' => '256 bits, High Encryption',
 'ADH-AES128-SHA' => '128 bits, High Encryption, Anonymous Auth',
 'DHE-RSA-AES128-SHA' => '128 bits, High Encryption',
 'DHE-DSS-AES128-SHA' => '128 bits, High Encryption',
 'AES128-SHA' => '128 bits, High Encryption',
 'EDH-RSA-DES-CBC3-SHA' => '168 bits, High Encryption',
 'EDH-DSS-DES-CBC3-SHA' => '168 bits, High Encryption',
 'DES-CBC3-SHA' => '168 bits, High Encryption',
 'ADH-DES-CBC3-SHA' => '168 bits, High Encryption, Anonymous Auth',
 'RC4-SHA' => '128 bits, Medium Encryption',
 'RC4-MD5' => '128 bits, Medium Encryption',
 'ADH-RC4-MD5' => '128 bits, Medium Encryption, Anonymous Auth',
 'EDH-RSA-DES-CBC-SHA' => '56 bits, Low Encryption',
 'EDH-DSS-DES-CBC-SHA' => '56 bits, Low Encryption',
 'DES-CBC-SHA' => '56 bits, Low Encryption',
 'ADH-DES-CBC-SHA' => '56 bits, Low Encryption, Anonymous Auth',
 'EXP-ADH-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-ADH-RC4-MD5' => '40 bits, Export-Grade Encryption',
 'EXP-EDH-RSA-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-EDH-DSS-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-RC2-CBC-MD5' => '40 bits, Export-Grade Encryption',
 'EXP-RC4-MD5' => '40 bits, Export-Grade Encryption',
 'NULL-SHA' => 'Null cipher, No Encryption',
 'NULL-MD5' => 'Null cipher, No Encryption'
);

%tls1_ciphers = (
 'ADH-AES256-SHA' => '256 bits, High Encryption, Anonymous Auth',
 'DHE-RSA-AES256-SHA' => '256 bits, High Encryption',
 'DHE-DSS-AES256-SHA' => '256 bits, High Encryption',
 'AES256-SHA' => '256 bits, High Encryption',
 'ADH-AES128-SHA' => '128 bits, High Encryption, Anonymous Auth',
 'DHE-RSA-AES128-SHA' => '128 bits, High Encryption',
 'DHE-DSS-AES128-SHA' => '128 bits, High Encryption',
 'AES128-SHA' => '128 bits, High Encryption',
 'EDH-RSA-DES-CBC3-SHA' => '168 bits, High Encryption',
 'EDH-DSS-DES-CBC3-SHA' => '168 bits, High Encryption',
 'DES-CBC3-SHA' => '168 bits, High Encryption',
 'ADH-DES-CBC3-SHA' => '168 bits, High Encryption, Anonymous Auth',
 'RC4-SHA' => '128 bits, Medium Encryption',
 'RC4-MD5' => '128 bits, Medium Encryption',
 'ADH-RC4-MD5' => '128 bits, Medium Encryption, Anonymous Auth',
 'EDH-RSA-DES-CBC-SHA' => '56 bits, Low Encryption',
 'EDH-DSS-DES-CBC-SHA' => '56 bits, Low Encryption',
 'DES-CBC-SHA' => '56 bits, Low Encryption',
 'ADH-DES-CBC-SHA' => '56 bits, Low Encryption, Anonymous Auth',
 'EXP-ADH-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-ADH-RC4-MD5' => '40 bits, Export-Grade Encryption',
 'EXP-EDH-RSA-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-EDH-DSS-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-DES-CBC-SHA' => '40 bits, Export-Grade Encryption',
 'EXP-RC2-CBC-MD5' => '40 bits, Export-Grade Encryption',
 'EXP-RC4-MD5' => '40 bits, Export-Grade Encryption',
 'NULL-SHA' => 'Null cipher, No Encryption',
 'NULL-MD5' => 'Null cipher, No Encryption'
);

print "Testing SSLv2 Ciphers...\n";

while (($key,$value) = each(%ssl2_ciphers)) {
 my $ssl2client = IO::Socket::SSL->new(
 SSL_verify_mode => 0,
 SSL_version => 'SSLv2',
 SSL_cipher_list => $key,
 PeerAddr => $host,
 PeerPort => $port,
 Proto => 'tcp',
 Timeout => '5'
 )
 && print " " . $key . " -- " . $value . "\n";
}

print "Testing SSLv3 Ciphers...\n";

while (($key,$value) = each(%ssl3_ciphers)) {
 my $ssl3client = IO::Socket::SSL->new(
 SSL_verify_mode => 0,
 SSL_version => 'SSLv3',
 SSL_cipher_list => $key,
 PeerAddr => $host,
 PeerPort => $port,
 Proto => 'tcp',
 Timeout => '5'
 )
 && print " " . $key . " -- " . $value . "\n";
}

print "Testing TLSv1 Ciphers...\n";

while (($key,$value) = each(%tls1_ciphers)) {
 my $tls1client = IO::Socket::SSL->new(
 SSL_verify_mode => 0,
 SSL_version => 'TLSv1',
 SSL_cipher_list => $key,
 PeerAddr => $host,
 PeerPort => $port,
 Proto => 'tcp',
 Timeout => '5'
 )
 && print " " . $key . " -- " . $value . "\n";
}

07/10/2009

BigIP: Logging SSL Version and Cipher Information

One of the really cool things about mod_ssl in apache is that you can configure your apache logs to record the SSL protocol version and cipher used per connection. This provides some of the more arrogant web server administrators among us with even more reasons to scoff at some folks connecting to our sites. "Hey Chris! Look! Not only is this guy using IE4 with the FunWebProducts toolbar but he connected using SSL2 over a 40-bit channel! Ha-ha-ha-snort!"

If, however, you are front-ending your site with an F5 BigIP LTM and terminating SSL at the BigIP, you probably would not normally then connect with SSL to your apache servers as well. (Regardless, even if you did, the F5 would be acting as the ssl client and you'd only see the cipher the BigIP used to connect to your apache servers, not the actual client's info).

Because I don't want to let anyone down, I thought it would be fun getting the BigIP to forward this information through to the apache server, similar to how the F5 will forward an X-Forwarded-For header containing the client IP address. Since it's been a while since I've published an article on iRules, here we go. This particular solution is a hybrid bigip-apache solution, similar, as previously mentioned, to the X-Forwarded-For setup. With this solution, you'll insert a couple of custom HTTP headers that will be passed along to your apache servers and you'll modify your apache server's log format to append this data to your apache server's access logs. Below is the iRule used to insert these headers:

 when HTTP_REQUEST {
HTTP::header insert "SSL_PROTOCOL" [SSL::cipher version]
HTTP::header insert "SSL_CIPHER" [SSL::cipher name]
}

Then, you'll modify the LogFormat directive for your apache logs. For this particular test, I simply modified an existing combined logging format that had already been modified to include the X-Forwarded-For header as the client ip. I changed the format from this:

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Cookie}i\" \"%{Set-Cookie}o\"" common_bigip

to this (changes are bolded):

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Cookie}i\" \"%{Set-Cookie}o\" %{SSL_PROTOCOL}i %{SSL_CIPHER}i" common_bigip

Please note here that the additional values added to the LogFormat directive do not use mod_ssl's syntax of ending the value with an "x" but instead we're ending it with an "i"—this is an http header value that is getting inserted, not an environment variable extension.

An example that displays what is now stored in the access log follows:

XX.XX.XX.XXX - - [09/Jul/2009:23:32:11 -0400] "GET /WebApp/ HTTP/1.1" 200 4366 "JSESSIONID=C5E4444A9F324BCB9DEDE6BD4A711C59.jspRouteID" "-" TLSv1 RC4-MD5

What's nice about this is that with only 4 lines of code in an irule and some relatively minor changes to the LogFormat on your apache servers, you can regain some functionality that we might have lost when we rolled those BigIP's out.  Another nice thing about this is that if you're using tomcat instead of apache, you can make similar changes to the tomcat access logging valve and you get the same results.  My apologies though go out to the IIS administrators out there who might be interested in getting this kind of data.  I haven't figured out how to get that yet.  If anyone out there knows of a way to log custom http header data into the IIS web server logs, I'd appreciate it if you'd let me know.