Please let me know what you think, if something isn't working, etc. New in this version is scanning for a small number of unsafe URLs, (i.e URLs that you don't necessarily want to expose to the outside world like IIS's "IIS Admin" site or the Apache mod_info server info page).
This script is getting kind of long so the next version will mostly be a cleanup.
An updated version of this script is now available on the downloads page. Source code is below:
#!/usr/bin/env perl
use Modern::Perl;
use Term::ANSIColor qw(:constants);
use Tie::Hash::Indexed;
use IO::Socket::SSL;
# The following three modules provide support for
# displaying server type and TRACE and TRACK requests.
use LWP::UserAgent;
use HTTP::Headers;
use HTTP::Request;
# Core Module introducing command line options
use Getopt::Long;
my ( $host, $port, $scheme );
my $useragent = 'cryptonark-pci-auditor/v0.3';
sub usage{
say "Usage: cnark.pl [--host HOSTNAME --port PORT]";
exit;
}
usage() if ( ! GetOptions("host=s" => \$host, "port=s" => \$port,) or ( ! defined $host) or ( ! defined $port) );
my $key;
my $value;
my $ssl2client;
my $ssl3client;
if ( $port eq 443) {
$scheme = 'https';
}
else {
$scheme = 'http';
}
# Populate arrays with OpenSSL ciphers
# Note: TLSv1 ciphers and SSLv3 ciphers are
# identical in OpenSSL
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'
);
# Populate a hash of unsafe URLs
# i.e. URLs you would not want exposed
# to the Internet
tie my %bad_urls, 'Tie::Hash::Indexed';
%bad_urls = (
'Apache mod_status page' => 'server-status',
'Apache mod_info page' => 'server-info',
'Apache mod_jk status page' => 'jk-status',
'Apache mod_proxy_balancer' => 'balancer-manager',
'IIS Samples' => 'IISsamples',
'IIS Scripts' => 'Scripts',
'IIS MSADC Directory' => 'MSADC',
'IIS Help' => 'IISHelp',
'IIS Admin' => 'IISAdmin',
'Tomcat Manager' => 'manager/html',
'Tomcat JSP Examples' => 'jsp-examples/index.html',
'Tomcat Servlet Examples' => 'servlets-examples/index.html',
'JBoss JMX Console' => 'jmx-console',
'JBoss Tomcat Status Page' => 'status',
'JBoss Web Console' => 'web-console',
'JBoss 5.x Admin Console' => 'admin-console',
);
sub get_server_type{
my $url = "$scheme://$host/";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent( $useragent );
my $header = $ua->head( $url );
print "\nWeb Server Type: " . $header->server . "\n\n";
}
sub test_for_trace {
my $url = "$scheme://$host/";
my $method = "TRACE";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent( $useragent );
my $request = HTTP::Request->new( $method => $url );
$request->header(Header0 => "TRACE");
$request->header(Header1 => "Test");
my $response = $ua->request($request);
given ( $response->code ) {
when (200) {
say "======this is what you sent======";
say $response->content;
say "=================================";
say $method, " is enabled and working.";
}
when (301) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (302) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (307) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (403) {
say $response->status_line;
say $method, " is forbidden.";
}
when (404) {
say $response->status_line;
say "This is an unexpected response";
}
when (405) {
say $response->status_line;
say $method, " is not permitted.";
}
when (501) {
say $response->status_line;
say $method, " is not implemented.";
}
default {
say $response->status_line;
}
}
}
sub test_for_track {
my $test = 'This is an HTTP TRACE test.';
my $url = "$scheme://$host/";
my $method = "TRACK";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent( $useragent );
my $request = HTTP::Request->new( $method => $url );
$request->header(Header0 => "TRACK");
$request->header(Header1 => "Test");
my $response = $ua->request($request);
given ( $response->code ) {
when (200) {
say "======this is what you sent======";
say $response->content;
say "=================================";
say $method, " is enabled and working.";
}
when (301) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (302) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (307) {
say "Redirect present. Retry request against ",
$response->header('Location');
}
when (403) {
say $response->status_line;
say $method, " is forbidden.";
}
when (404) {
say $response->status_line;
say "This is an unexpected response";
}
when (405) {
say $response->status_line;
say $method, " is not permitted.";
}
when (501) {
say $response->status_line;
say $method, " is not implemented.";
}
default {
say $response->status_line;
}
}
}
sub scan_for_unsafe_urls{
print "\n";
say "Scanning for 'Unsafe' URLs....";
sleep 2;
my $response;
my $request;
while (($key,$value) = each(%bad_urls)) {
my $url = "$scheme://$host/$value";
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent( $useragent );
my $request = HTTP::Request->new( GET => $url );
my $response = $ua->request($request);
if ($response->code == '200') {
print RED, " " . $key . " -- /". $value . " unsafe url found or false positive" . "\n", RESET;
}
else {
print GREEN, " " . $key . " -- /" . $value . " unsafe url not found." . "\n", RESET;
}
}
}
sub shameless_plug{
print "\n";
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();
say "Testing for HTTP TRACE...";
sleep 2;
test_for_trace();
say "\nTesting for HTTP TRACK...";
sleep 2;
test_for_track();
scan_for_unsafe_urls();
sub is_weak{
if ($key =~ /^EXP-|^NULL|^ADH-|DES-CBC-/) {
print RED, " " . $key . " -- " . $value . "\n", RESET;
}
else {
print GREEN, " " . $key . " -- " . $value . "\n", RESET;
}
}
sub test_ssl2_ciphers{
print "\nTesting SSLv2 Ciphers...\n";
sleep 2;
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();
}
}
sub test_tls_ciphers{
print "Testing SSLv3/TLSv1 Ciphers...\n";
sleep 2;
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();
}
}
if ($port eq 443) {
test_ssl2_ciphers();
test_tls_ciphers();
}
shameless_plug();
Advertisement: Webroot $10,000 Security Sweepstakes - Click here for up to 4 Entries!