This version represents an experimental change in direction for cryptonark, as I begin to expand the scope of what this tool does. There are two versions packaged inside the downloadable files now, an unabridged version and the main version which has had the SSLv3 related items removed. This is purely based on OpenSSL using the same ciphers regardless of whether you test with SSLv3 or TLSv1, so the tests were redundant. The unabridged version of the script still performs the SSLv3 tests.
As a reminder, if you scan for ciphers and do not get a response back on a particular cipher, this indicates that the cipher is not enabled on your web site. Alternatively, any cipher displayed in the output is enabled on your site.
Many vulnerabilities reported in PCI Compliance scans are derived purely based off of the server type and version number supplied in the Server HTTP Response header. The fairness of this particular method is best left for a different type of article but you can modify your web server's configuration to not provide this information in the Server response header. New in this version of my script is the reporting of the Server type and version. It is performed using a simple HTTP Head request and utilizes two additional perl modules, which may already be installed as part of your base perl distribution: LWP::UserAgent and HTTP::Headers. I decided to utilize these two modules because in the future, there will be additional types of checks that will make use of them.
The main CryptoNark page contains fairly complete documentation on this script and downloadable versions are available on the Downloads page. Updated source is included below as well.
#!/usr/bin/env perl
# 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 right "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.
#
# version 0.2 -
# + Added Color Coded output. Good ciphers are green,
# bad ones are red.
#
# version 0.2.1 -
# + Removed the SSLv3 Tests. (Actually, just commented
# them out for now.) SSLv3 and TLSv1 utilize the same
# ciphers so the tests are redundant.
# + Added a message after the display of cipher levels
# providing links to my site so that you can get
# information on disabling ciphers
#
# version 0.2.5 -
# + Added a HEAD request to display web server type.
# Uses some additional modules: LWP::UserAgent and
# HTTP::Headers
#
use strict;
use warnings;
use Term::ANSIColor qw(:constants);
use Tie::Hash::Indexed;
use IO::Socket::SSL;
# The following two modules provide support for
# displaying server type.
use LWP::UserAgent;
use HTTP::Headers;
my $host = $ARGV[0];
my $port = $ARGV[1];
my $help = "Usage: $0 <hostname> <port>";
my $key;
my $value;
my $ssl2client;
my $ssl3client;
if ( !@ARGV ) {
print $help . "\n";
exit 0;
}
# Populate array with OpenSSL ciphers
# Note: TLSv1 ciphers and SSLv3 ciphers are identical
tie my %ssl2_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'
);
%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-DES-CBC3-SHA' => '168 bits, High Encryption, Anonymous Auth',
'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-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',
'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'
);
sub is_weak{
if ($key =~ /^EXP-|^NULL|^ADH-|DES-CBC-/) {
print RED, " " . $key . " -- " . $value . "\n", RESET;
}
else {
print GREEN, " " . $key . " -- " . $value . "\n", RESET;
}
}
sub get_server_type{
my $url = "http://$host/";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent('cryptonark-pci-auditor/v0.3');
my $header = $ua->head( $url );
$header->is_success or
die RED, "\nFailed to get server type for $host " . $header->status_line . "\n\n", RESET;
print "\nWeb Server Type: " . $header->server . "\n\n";
}
sub shameless_plug{
print "For tips on PCI Remediation, visit my blog at: \n";
print " http://blog.techstacks.com/pci-compliance/\n";
print "Thanks for using cnark!\n";
}
get_server_type();
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'
)
&& is_weak();
}
print "Testing SSLv3/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'
)
&& is_weak();
}
shameless_plug();
As a final note, this script is intended to be used by a systems admin and/or web site engineer who needs to verify the existence and remediation of certain vulnerabilities identified through a third party PCI scan. Your ASV is not required to disclose how they determined that a vulnerability exists nor are they required to inform you how to assess the vulnerability yourself. This script is for those individuals who do not want to wait for the next quarterly scan to see if remediation actions have been effective. It is for those individuals who are required to validate any change performed as part of their typical change and configuration management procedures.