From c68358eabd7b2d1d21d82f35200dcc24f920edc5 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Mon, 6 Feb 2012 15:12:31 -0500 Subject: [PATCH] added the ability to encrypt fwknop client plaintext data with openssl --- extras/spa-entropy/spa-entropy.pl | 375 +++++++++++++++++++++--------- 1 file changed, 271 insertions(+), 104 deletions(-) diff --git a/extras/spa-entropy/spa-entropy.pl b/extras/spa-entropy/spa-entropy.pl index 0f375cd7..24962c76 100755 --- a/extras/spa-entropy/spa-entropy.pl +++ b/extras/spa-entropy/spa-entropy.pl @@ -1,10 +1,33 @@ #!/usr/bin/perl -w +# +# Author: Michael Rash +# use MIME::Base64; use IPC::Open2; use Data::Password::Entropy; +use Getopt::Long 'GetOptions'; use strict; +my $use_ent = 1; +my $use_pw_entropy = 0; +my $base64_decode = 1; +my $no_base64_decode = 0; +my $packets = 0; +my $prefix = 'entropy'; +my $file_to_measure = ''; +my $run_fwknop_client = 0; +my $min_len = 0; +my $lib_dir = '../../lib/.libs'; +my $fwknop_client_path = '../../client/.libs/fwknop'; +my $enc_mode = 'ecb'; +my $spa_key_file = 'local_spa.key'; +my $help = 0; + +my $use_openssl = 0; +my $openssl_salt = '0000000000000000'; +my $openssl_mode = '-aes-256-cbc'; + my %min_max_entropy = ( 'min' => { 'val' => 0, @@ -15,61 +38,50 @@ my %min_max_entropy = ( 'pos' => 0, } ); + my @encrypted_data = (); +my @plaintext_data = (); my @cross_pkt_data = (); -while () { - next unless $_ =~ /\S/; - chomp; +Getopt::Long::Configure('no_ignore_case'); +die "[*] See '$0 -h' for usage information" unless (GetOptions( + 'file-to-measure=s' => \$file_to_measure, + 'no-base64-decode' => \$no_base64_decode, + 'count=i' => \$packets, + 'prefix=s' => \$prefix, + 'run-fwknop-client' => \$run_fwknop_client, + 'enc-mode=s' => \$enc_mode, + 'lib-dir=s' => \$lib_dir, + 'Client-path=s' => \$fwknop_client_path, + 'use-pw-entropy' => \$use_pw_entropy, + 'use-openssl' => \$use_openssl, + 'openssl-salt=s' => \$openssl_salt, + 'openssl-mode=s' => \$openssl_mode, + 'help' => \$help, +)); +&usage() if $help; - push @encrypted_data, $_; - next; +$base64_decode = 0 if $no_base64_decode; +$use_ent = 0 if $use_pw_entropy; +die "[*] Must execute --run-fwknop-client in --use-openssl mode" + if $use_openssl and not $run_fwknop_client; - my $base64_str = $_; - unless ($base64_str =~ /^U2FsdGVkX1/) { - $base64_str = 'U2FsdGVkX1' . $base64_str; - } - my ($equals_rv, $equals_padding) = &base64_equals_padding($base64_str); - if ($equals_padding) { - $base64_str .= $equals_padding; - } - push @encrypted_data, decode_base64($base64_str); -} +&run_fwknop_client() if $run_fwknop_client; -### calculate minimum length -my $min_len = 0; -for my $line (@encrypted_data) { - chomp $line; - next unless $line =~ /\S/; - my $len = length($line); - if ($min_len == 0) { - $min_len = $len; - } else { - if ($len < $min_len) { - $min_len = $len; - } - } -} +&read_data(); -my $l_ctr = 0; -for my $line (@encrypted_data) { - my @chars = split //, $line; - my $c_ctr = 0; - for my $char (@chars) { - $cross_pkt_data[$c_ctr] .= $char; - last if $c_ctr == $min_len; - $c_ctr++; - } - $l_ctr++; -} +&get_min_len(); -open F, "> entropy.dat" or die $!; +&build_data_slices(); + +open F, "> $prefix.dat" or die $!; my $pos = 0; for my $str (@cross_pkt_data) { my $entropy = &get_entropy($str); - print F "$pos $entropy\n"; +# print F "$pos $entropy\n"; + print F "$pos $entropy ### " . &hex_dump($str) . "\n"; if ($min_max_entropy{'min'}{'val'} == 0 and $min_max_entropy{'max'}{'val'} == 0) { @@ -93,57 +105,210 @@ close F; my $min = $min_max_entropy{'min'}{'val'}; my $max = $min_max_entropy{'max'}{'val'}; -print "Min entropy: $min at position: $min_max_entropy{'min'}{'pos'}\n"; -print "Max entropy: $max at position: $min_max_entropy{'max'}{'pos'}\n"; -open F, "> entropy.gnu" or die $!; -print F <<_GNUPLOT_; -set title "entropy measurement" -set terminal gif nocrop enhanced -set output "entropy.gif" -set grid -plot 'entropy.dat' using 1:2 with lines title 'min: $min, max: $max' -_GNUPLOT_ -close F; +print "[+] Min entropy: $min at byte: $min_max_entropy{'min'}{'pos'}\n"; +print "[+] Max entropy: $max at byte: $min_max_entropy{'max'}{'pos'}\n"; -system "gnuplot entropy.gnu"; +&run_gnuplot(); exit 0; +sub read_data() { + + if ($use_openssl) { + + ### we've already gotten plaintext information from the fwknop client, + ### so encrypt this data with openssl and use it to re-write the + ### $file_to_measure + unlink $file_to_measure if -e $file_to_measure; + + my @openssl_encrypted_data = (); + + ### encrypt the plaintext and use it to re-write the -f file + for my $line (@plaintext_data) { + + my $ptext_file = 'ptext.tmp'; + my $enc_file = 'ptext.enc'; + + open F, "> $ptext_file" or die $!; + print F $line; + close F; + + unlink $enc_file if -e $enc_file; + + system "openssl enc $openssl_mode -a -S $openssl_salt " . + "-in ptext.tmp -out ptext.enc -k fwknoptest000000"; + + my $base64_enc_data = ''; + open F, "< $enc_file" or die $!; + while () { + chomp; + $base64_enc_data .= $_; + } + close F; + + push @openssl_encrypted_data, $base64_enc_data; + + } + + open F, "> $file_to_measure" or die $!; + for my $line (@openssl_encrypted_data) { + print F $line, "\n"; + } + close F; + } + + my $fh = *STDIN; + if ($file_to_measure) { + open IN, "< $file_to_measure" or die "[*] Could not open $file_to_measure: $!"; + $fh = *IN; + } + + my $l_ctr = 0; + while (<$fh>) { + next unless $_ =~ /\S/; + chomp; + + if ($base64_decode) { + if (&is_base64($_)) { + my $base64_str = $_; + ### base64-encoded "Salted__" prefix + unless ($base64_str =~ /^U2FsdGVkX1/) { + $base64_str = 'U2FsdGVkX1' . $base64_str; + } + my ($equals_rv, $equals_padding) = &base64_equals_padding($base64_str); + if ($equals_padding) { + $base64_str .= $equals_padding; + } + push @encrypted_data, decode_base64($base64_str); + } else { + push @encrypted_data, $_; + } + } else { + push @encrypted_data, $_; + } + + $l_ctr++; + if ($packets > 0) { + last if $l_ctr == $packets; + } + } + + ### hex dump encrypted data + open HEX, "> hex_dump.data" or die $!; + for my $line (@encrypted_data) { + print HEX &hex_dump($line), "\n"; + } + close HEX; + + print "[+] Read in $l_ctr SPA packets...\n"; + return; +} + +sub run_fwknop_client() { + die "[*] Must set packets file with -f " unless $file_to_measure; + die "[*] Must set packet count with -c " unless $packets; + + if (-e $file_to_measure) { + unlink $file_to_measure or die $!; + } + + my $cmd = "LD_LIBRARY_PATH=$lib_dir $fwknop_client_path -A tcp/22 " . + "-a 127.0.0.2 -D 127.0.0.1 --get-key $spa_key_file -M $enc_mode " . + "-B $file_to_measure -b -v --test 2> /dev/null"; + + print "[+] Running fwknop client via the following command:\n\n$cmd\n\n"; + + for (my $i=0; $i < $packets; $i++) { + open C, "$cmd |" or die $!; + while () { + if (/Plaintext\:\s+(\S+)/) { + push @plaintext_data, $1; + last; + } + } + close C; + } + + return; +} + +sub get_min_len() { + + ### calculate minimum length + for my $line (@encrypted_data) { + chomp $line; + next unless $line =~ /\S/; + my $len = length($line); + if ($min_len == 0) { + $min_len = $len; + } else { + if ($len < $min_len) { + $min_len = $len; + } + } + } + return; +} + +sub build_data_slices() { + for my $line (@encrypted_data) { + my @chars = split //, $line; + my $c_ctr = 0; + for my $char (@chars) { + $cross_pkt_data[$c_ctr] .= $char; + last if $c_ctr == $min_len; + $c_ctr++; + } + } + return; +} + +sub run_gnuplot() { + open F, "> $prefix.gnu" or die $!; + print F <<_GNUPLOT_; +set title "entropy measurement" +set terminal gif nocrop enhanced +set output "$prefix.gif" +set grid +plot '$prefix.dat' using 1:2 with lines title 'min: $min, max: $max' +_GNUPLOT_ + close F; + + print "[+] Creating $prefix.gif gnuplot graph...\n\n"; + system "gnuplot $prefix.gnu"; + + return; +} + sub get_entropy() { my $data = shift; - #return password_entropy($data); - my $entropy = ''; - ### Entropy = 5.637677 bits per byte. -# system "echo -n $data | ent | grep Entropy > tmp.ent"; -# open ENT, "< tmp.ent" or die $!; -# my $line = ; -# if ($line =~ /\s=\s(\d\S+)/) { -# $entropy = $1; -# } -# close ENT; -# return $entropy; + if ($use_ent) { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'ent'); - my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'ent'); + print CHLD_IN $data; + close CHLD_IN; - print CHLD_IN $data; - close CHLD_IN; - - while () { - if (/Entropy\s=\s(\d\S+)/) { - $entropy = $1; - last; + while () { + ### Entropy = 5.637677 bits per byte. + if (/Entropy\s=\s(\d\S+)/) { + $entropy = $1; + last; + } } + + close CHLD_OUT; + + waitpid( $pid, 0 ); + my $child_exit_status = $? >> 8; + + } else { + $entropy = password_entropy($data); } - close CHLD_OUT; - - waitpid( $pid, 0 ); - my $child_exit_status = $? >> 8; - return $entropy; } @@ -172,38 +337,40 @@ sub hex_dump() { my @chars = split //, $data; my $ctr = 0; - my $ascii_str = ''; - for my $char (@chars) { - if ($ctr % 16 == 0) { - print STDOUT " $ascii_str\n" if $ascii_str; - printf STDOUT " 0x%.4x: ", $ctr; - $ascii_str = ''; - } - printf STDOUT "%.2x", ord($char); - if ((($ctr+1) % 2 == 0) and ($ctr % 16 != 0)) { - print STDOUT ' '; - } + my $hex_part = ''; + my $ascii_part = ''; + + for my $char (@chars) { + + $hex_part .= sprintf "%.2x", ord($char); if ($char =~ /[^\x20-\x7e]/) { - $ascii_str .= '.'; + $ascii_part .= '.'; } else { - $ascii_str .= $char; + $ascii_part .= $char; } $ctr++; } - if ($ascii_str) { - my $remainder = 1; - if ($ctr % 16 != 0) { - $remainder = 16 - $ctr % 16; - if ($remainder % 2 == 0) { - $remainder = 2*$remainder + int($remainder/2) + 1; - } else { - $remainder = 2*$remainder + int($remainder/2) + 2; - } - } - print STDOUT ' 'x$remainder, $ascii_str; + return "$hex_part $ascii_part"; +# return "$ascii_part"; +} + +sub is_base64() { + my $data = shift; + + ### check to make sure the packet data only contains base64 encoded + ### characters per RFC 3548: 0-9, A-Z, a-z, +, /, = + if ($data =~ /[^\x30-\x39\x41-\x5a\x61-\x7a\x2b\x2f\x3d]/) { + return 0; } - print STDOUT "\n"; - return; + if ($data =~ /=[^=]/) { + return 0; + } + return 1; +} + +sub usage() { + print "$0 [options]\n"; + exit 0; }