CryptoNark v0.3.5 Released
CryptoNark 0.4.1 is the most recent version. See the Release Announcement for more details.
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();
