diff --git a/CREDITS b/CREDITS index ab6edd06..71f0fa84 100644 --- a/CREDITS +++ b/CREDITS @@ -53,3 +53,8 @@ Hank Leininger - For iptables firewalls, suggested a check for the 'comment' match to ensure the local environment will properly support fwknopd operations. The result is the new ENABLE_IPT_COMMENT_CHECK functionality. + +Fernando Arnaboldi (IOActive) + - Found important buffer overflow conditions for authenticated SPA clients + in the fwknopd server (pre-2.0.3). These findings enabled fixes to be + developed along with a new fuzzing capability in the test suite. diff --git a/ChangeLog b/ChangeLog index 1568a1aa..00f3a372 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +fwknop-2.0.3 (08//2012): + - [server] Fernando Arnaboldi from IOActive found several DoS/code + execution vulnerabilities for malicious fwknop clients that manage to + get past the authentication stage (so a such a client must be in + possession of a valid access.conf encryption key). These vulnerbilities + manifested themselves in the handling of malformed access requests, and + both the fwknopd server code along with libfko now perform stronger input + validation of access request data. These vulnerabilities affect + pre-2.0.3 fwknop releases. + - [test suite] Added a new fuzzing capability to ensure proper server-side + input validation. Fuzzing data is constructed with modified fwknop + client code that is designed to emulate malicious behavior. + fwknop-2.0.2 (08/18/2012): - [server] For GPG mode, added a new access.conf variable "GPG_ALLOW_NO_PW" to make it possible to leverage a server-side GPG key diff --git a/Makefile.am b/Makefile.am index 9df6222e..7306bbc5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -149,6 +149,7 @@ EXTRA_DIST = \ test/conf/require_user_access.conf \ test/conf/subnet_source_match_access.conf \ test/conf/local_nat_fwknopd.conf \ + test/conf/disable_aging_fwknopd.conf \ test/hardening-check \ test/local_spa.key \ test/test-fwknop.pl \ diff --git a/lib/fko_message.c b/lib/fko_message.c index 061bb738..9148c2d3 100644 --- a/lib/fko_message.c +++ b/lib/fko_message.c @@ -200,6 +200,8 @@ validate_access_msg(const char *msg) do { ndx++; res = validate_proto_port_spec(ndx); + if(res != FKO_SUCCESS) + break; } while((ndx = strchr(ndx, ','))); return(res); @@ -208,14 +210,13 @@ validate_access_msg(const char *msg) int validate_proto_port_spec(const char *msg) { - int startlen = strnlen(msg, MAX_SPA_MESSAGE_SIZE); + int startlen = strnlen(msg, MAX_SPA_MESSAGE_SIZE), port_str_len = 0; const char *ndx = msg; if(startlen == MAX_SPA_MESSAGE_SIZE) return(FKO_ERROR_INVALID_DATA); - /* Now check for proto/port string. Currenly we only allow protos - * 'tcp', 'udp', and 'icmp'. + /* Now check for proto/port string. */ if(strncmp(ndx, "tcp", 3) && strncmp(ndx, "udp", 3) @@ -224,19 +225,25 @@ validate_proto_port_spec(const char *msg) return(FKO_ERROR_INVALID_SPA_ACCESS_MSG); ndx = strchr(ndx, '/'); - if(ndx == NULL || (1+(ndx - msg)) >= startlen) + if(ndx == NULL || ((1+(ndx - msg)) > MAX_PROTO_STR_LEN)) return(FKO_ERROR_INVALID_SPA_ACCESS_MSG); - /* Skip over the ',' and make sure we only have digits. + /* Skip over the '/' and make sure we only have digits. */ ndx++; - while(*ndx != '\0') + + /* Must have at least one digit for the port number + */ + if(isdigit(*ndx) == 0) + return(FKO_ERROR_INVALID_SPA_ACCESS_MSG); + + while(*ndx != '\0' && *ndx != ',') { - if(isdigit(*ndx) == 0) + port_str_len++; + if((isdigit(*ndx) == 0) || (port_str_len > MAX_PORT_STR_LEN)) return(FKO_ERROR_INVALID_SPA_ACCESS_MSG); ndx++; } - return(FKO_SUCCESS); } diff --git a/lib/fko_message.h b/lib/fko_message.h index f56e33ff..8350460e 100644 --- a/lib/fko_message.h +++ b/lib/fko_message.h @@ -32,6 +32,9 @@ #ifndef FKO_MESSAGE_H #define FKO_MESSAGE_H 1 +#define MAX_PROTO_STR_LEN 4 /* tcp, udp, icmp for now */ +#define MAX_PORT_STR_LEN 5 + /* SPA message format validation functions. */ int validate_cmd_msg(const char *msg); diff --git a/server/access.c b/server/access.c index c81fb936..91feb0b7 100644 --- a/server/access.c +++ b/server/access.c @@ -168,7 +168,7 @@ add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *v /* Take an IP or Subnet/Mask and convert it to mask for later * comparisons of incoming source IPs against this mask. */ -static void +static int add_source_mask(acc_stanza_t *acc, const char *ip) { char *ndx; @@ -237,7 +237,7 @@ add_source_mask(acc_stanza_t *acc, const char *ip) free(new_sle); new_sle = NULL; - return; + return 0; } /* Store our mask converted from CIDR to a 32-bit value. @@ -249,15 +249,17 @@ add_source_mask(acc_stanza_t *acc, const char *ip) */ new_sle->maddr = ntohl(in.s_addr) & new_sle->mask; } + return 1; } /* Expand the access SOURCE string to a list of masks. */ -void +static int expand_acc_source(acc_stanza_t *acc) { char *ndx, *start; - char buf[32]; + char buf[ACCESS_BUF_LEN]; + int res = 1; start = acc->source; @@ -270,8 +272,13 @@ expand_acc_source(acc_stanza_t *acc) while(isspace(*start)) start++; + if(((ndx-start)+1) >= ACCESS_BUF_LEN) + return 0; + strlcpy(buf, start, (ndx-start)+1); - add_source_mask(acc, buf); + res = add_source_mask(acc, buf); + if(res == 0) + return res; start = ndx+1; } } @@ -281,15 +288,20 @@ expand_acc_source(acc_stanza_t *acc) while(isspace(*start)) start++; + if(((ndx-start)+1) >= ACCESS_BUF_LEN) + return 0; + strlcpy(buf, start, (ndx-start)+1); - add_source_mask(acc, buf); + res = add_source_mask(acc, buf); + + return res; } static int parse_proto_and_port(char *pstr, int *proto, int *port) { char *ndx; - char proto_str[32]; + char proto_str[ACCESS_BUF_LEN]; /* Parse the string into its components. */ @@ -301,10 +313,24 @@ parse_proto_and_port(char *pstr, int *proto, int *port) return(-1); } - strlcpy(proto_str, pstr, (ndx - pstr)+1); + if(((ndx - pstr)+1) >= ACCESS_BUF_LEN) + { + log_msg(LOG_ERR, + "Parse error on access port entry: %s", pstr); + return(-1); + } + + strlcpy(proto_str, pstr, (ndx - pstr)+1); *port = atoi(ndx+1); + if((*port < 0) || (*port > MAX_PORT)) + { + log_msg(LOG_ERR, + "Invalid port in access request: %s", pstr); + return(-1); + } + if(strcasecmp(proto_str, "tcp") == 0) *proto = PROTO_TCP; else if(strcasecmp(proto_str, "udp") == 0) @@ -313,7 +339,6 @@ parse_proto_and_port(char *pstr, int *proto, int *port) { log_msg(LOG_ERR, "Invalid protocol in access port entry: %s", pstr); - return(-1); } @@ -416,15 +441,15 @@ add_string_list_ent(acc_string_list_t **stlist, const char *str_str) /* Expand a proto/port access string to a list of access proto-port struct. */ -void +int expand_acc_port_list(acc_port_list_t **plist, char *plist_str) { char *ndx, *start; - char buf[32]; + char buf[ACCESS_BUF_LEN]; start = plist_str; - for(ndx = start; *ndx; ndx++) + for(ndx = start; *ndx != '\0'; ndx++) { if(*ndx == ',') { @@ -433,6 +458,9 @@ expand_acc_port_list(acc_port_list_t **plist, char *plist_str) while(isspace(*start)) start++; + if(((ndx-start)+1) >= ACCESS_BUF_LEN) + return 0; + strlcpy(buf, start, (ndx-start)+1); add_port_list_ent(plist, buf); start = ndx+1; @@ -444,9 +472,14 @@ expand_acc_port_list(acc_port_list_t **plist, char *plist_str) while(isspace(*start)) start++; + if(((ndx-start)+1) >= ACCESS_BUF_LEN) + return 0; + strlcpy(buf, start, (ndx-start)+1); add_port_list_ent(plist, buf); + + return 1; } /* Expand a comma-separated string into a simple acc_string_list. @@ -602,7 +635,11 @@ expand_acc_ent_lists(fko_srv_options_t *opts) { /* Expand the source string to 32-bit integer masks foreach entry. */ - expand_acc_source(acc); + if(expand_acc_source(acc) == 0) + { + acc = acc->next; + continue; + } /* Now expand the open_ports string. */ @@ -1086,9 +1123,9 @@ compare_port_list(acc_port_list_t *in, acc_port_list_t *ac, const int match_any) int acc_check_port_access(acc_stanza_t *acc, char *port_str) { - int res = 1; + int res = 1, ctr = 0; - char buf[32]; + char buf[ACCESS_BUF_LEN]; char *ndx, *start; acc_port_list_t *o_pl = acc->oport_list; @@ -1101,14 +1138,34 @@ acc_check_port_access(acc_stanza_t *acc, char *port_str) /* Create our own internal port_list from the incoming SPA data * for comparison. */ - for(ndx = start; *ndx; ndx++) + for(ndx = start; *ndx != '\0'; ndx++) { if(*ndx == ',') { + if((ctr >= ACCESS_BUF_LEN) + || (((ndx-start)+1) >= ACCESS_BUF_LEN)) + { + log_msg(LOG_ERR, + "Unable to create acc_port_list from incoming data: %s", + port_str + ); + return(0); + } strlcpy(buf, start, (ndx-start)+1); add_port_list_ent(&in_pl, buf); start = ndx+1; + ctr = 0; } + ctr++; + } + if((ctr >= ACCESS_BUF_LEN) + || (((ndx-start)+1) >= ACCESS_BUF_LEN)) + { + log_msg(LOG_ERR, + "Unable to create acc_port_list from incoming data: %s", + port_str + ); + return(0); } strlcpy(buf, start, (ndx-start)+1); add_port_list_ent(&in_pl, buf); diff --git a/server/access.h b/server/access.h index b6501014..4ecae19d 100644 --- a/server/access.h +++ b/server/access.h @@ -34,6 +34,8 @@ #define PROTO_TCP 6 #define PROTO_UDP 17 +#define ACCESS_BUF_LEN 32 + /* Function Prototypes */ void parse_access_file(fko_srv_options_t *opts); @@ -41,7 +43,7 @@ int compare_addr_list(acc_int_list_t *source_list, const uint32_t ip); int acc_check_port_access(acc_stanza_t *acc, char *port_str); int acc_check_gpg_remote_id(acc_stanza_t *acc, const char *gpg_id); void dump_access_list(const fko_srv_options_t *opts); -void expand_acc_port_list(acc_port_list_t **plist, char *plist_str); +int expand_acc_port_list(acc_port_list_t **plist, char *plist_str); void free_acc_stanzas(fko_srv_options_t *opts); void free_acc_port_list(acc_port_list_t *plist); diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index ad1648a5..e1b39888 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -582,7 +582,8 @@ process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_ /* Parse and expand our access message. */ - expand_acc_port_list(&port_list, spadat->spa_message_remain); + if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1) + return res; /* Start at the top of the proto-port list... */ diff --git a/test/conf/disable_aging_fwknopd.conf b/test/conf/disable_aging_fwknopd.conf new file mode 100644 index 00000000..195a85be --- /dev/null +++ b/test/conf/disable_aging_fwknopd.conf @@ -0,0 +1,5 @@ +# +# The default fwknopd.conf contains only comments since defaults are defined in +# code and modified via the config file +# +ENABLE_SPA_PACKET_AGING N; diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 0fd8adf0..2f78bb44 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -48,6 +48,7 @@ my %cf = ( 'multi_src_access' => "$conf_dir/multi_source_match_access.conf", 'ip_src_match' => "$conf_dir/ip_source_match_access.conf", 'subnet_src_match' => "$conf_dir/ip_source_match_access.conf", + 'disable_aging' => "$conf_dir/disable_aging_fwknopd.conf", ); my $default_digest_file = "$run_dir/digest.cache"; @@ -508,7 +509,7 @@ my @tests = ( { 'category' => 'basic operations', 'subcategory' => 'client', - 'detail' => '-A / specification', + 'detail' => '-A / specification (proto)', 'err_msg' => 'permitted invalid -A /', 'function' => \&generic_exec, 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i], @@ -517,6 +518,19 @@ my @tests = ( "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip", 'fatal' => $NO }, + { + 'category' => 'basic operations', + 'subcategory' => 'client', + 'detail' => '-A / specification (port)', + 'err_msg' => 'permitted invalid -A /', + 'function' => \&generic_exec, + 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i], + 'exec_err' => $YES, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/600001 -a $fake_ip -D $loopback_ip", + 'fatal' => $NO + }, + { 'category' => 'basic operations', 'subcategory' => 'client', @@ -1069,6 +1083,52 @@ my @tests = ( 'fw_rule_removed' => $NEW_RULE_REMOVED, 'fatal' => $NO }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/60001)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'multi port (tcp/60001,udp/60001)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/60001,udp/60001 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'multi port (tcp/22,udp/53,tcp/1234)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/22,udp/53,tcp/1234 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { 'category' => 'Rijndael SPA', 'subcategory' => 'client+server', @@ -1154,6 +1214,64 @@ my @tests = ( 'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/], 'fatal' => $NO }, + + ### fuzzing tests + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'FUZZING', + 'detail' => 'overly long port value', + 'err_msg' => 'server crashed or did not detect error condition', + 'function' => \&overly_long_port, + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'FUZZING', + 'detail' => 'overly long proto value', + 'err_msg' => 'server crashed or did not detect error condition', + 'function' => \&overly_long_proto, + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'FUZZING', + 'detail' => 'negative port value', + 'err_msg' => 'server crashed or did not detect error condition', + 'function' => \&negative_port, + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'FUZZING', + 'detail' => 'null port value', + 'err_msg' => 'server crashed or did not detect error condition', + 'function' => \&null_port, + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'FUZZING', + 'detail' => 'null proto value', + 'err_msg' => 'server crashed or did not detect error condition', + 'function' => \&null_proto, + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fatal' => $NO + }, + { 'category' => 'Rijndael SPA', 'subcategory' => 'server', @@ -1287,6 +1405,24 @@ my @tests = ( 'fw_rule_removed' => $NEW_RULE_REMOVED, 'fatal' => $NO }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/60001)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { 'category' => 'GPG (no pw) SPA', 'subcategory' => 'client+server', @@ -1450,6 +1586,24 @@ my @tests = ( 'fw_rule_removed' => $NEW_RULE_REMOVED, 'fatal' => $NO }, + { + 'category' => 'GnuPG (GPG) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/60001)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir", + 'fwknopd_cmdline' => $default_server_gpg_args, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { 'category' => 'GnuPG (GPG) SPA', 'subcategory' => 'client+server', @@ -2112,6 +2266,261 @@ sub altered_non_base64_spa_data() { return $rv; } +sub null_proto() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ### this packet was generated with a modified fwknop client via the + ### following command line: + # + # LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A /22 \ + # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \ + # --verbose --verbose + # + my $spa_pkt = + '/JT14qxh9P4iy+CuUZahThaQjoEuL2zd46a+jL6sTrBZJSa6faUX4dH5fte/4ZJv+9f' . + 'd/diWYKAUvdQ4DydPGlR7mwQa2W+obKpqrsTBz7D4054z6ATAOGpCtifakEVl1XRc2+' . + 'hW04WpY8mdUNu9i+PrfPr7/KxqU'; + + my @packets = ( + { + 'proto' => 'udp', + 'port' => $default_spa_port, + 'dst_ip' => $loopback_ip, + 'data' => $spa_pkt, + }, + ); + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); + + $rv = 0 unless $server_was_stopped; + + if ($fw_rule_created) { + &write_test_file("[-] new fw rule created.\n", $current_test_file); + $rv = 0; + } else { + &write_test_file("[+] new fw rule not created.\n", $current_test_file); + } + + unless (&file_find_regex([qr/Args\scontain\sinvalid\sdata/], + $MATCH_ALL, $server_test_file)) { + $rv = 0; + } + + return $rv; +} + +sub null_port() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ### this packet was generated with a modified fwknop client via the + ### following command line: + # + # LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/ \ + # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \ + # --verbose --verbose + # + my $spa_pkt = + '94nu7hvq6V/3A27GzjHwfPnPCQfs44ySlraIFYHOAqy5YqjkrBS67nH35tX55N1BrYZ' . + '07zvcT03keUhLE1Uo7Wme1nE7BfTOG5stmIK1UQI85sL52//lDHu+xCqNcL7GUKbVRz' . + 'ekw+EUscVvUkrsRcVtSvOm+fCNo'; + + my @packets = ( + { + 'proto' => 'udp', + 'port' => $default_spa_port, + 'dst_ip' => $loopback_ip, + 'data' => $spa_pkt, + }, + ); + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); + + $rv = 0 unless $server_was_stopped; + + if ($fw_rule_created) { + &write_test_file("[-] new fw rule created.\n", $current_test_file); + $rv = 0; + } else { + &write_test_file("[+] new fw rule not created.\n", $current_test_file); + } + + unless (&file_find_regex([qr/Args\scontain\sinvalid\sdata/], + $MATCH_ALL, $server_test_file)) { + $rv = 0; + } + + return $rv; +} + +sub negative_port() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ### this packet was generated with a modified fwknop client via the + ### following command line: + # + # LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \ + # tcp/-33 -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \ + # --verbose --verbose + # + my $spa_pkt = + '/weoc+pEuQknZo8ImWTQBB+/PwSJ2/TcrmFoSkxpRXX4+jlUxoJakHrioxh8rhLmAD9' . + '8E4lMnq+EbM2XYdhs2alpZ5bovAFojMsYRWwr/BvRO4Um4Fmo9z9sY3DR477TXNYXBR' . + 'iGXWxSL4u+AWSSePK3qiiYoRQVw'; + + my @packets = ( + { + 'proto' => 'udp', + 'port' => $default_spa_port, + 'dst_ip' => $loopback_ip, + 'data' => $spa_pkt, + }, + ); + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); + + $rv = 0 unless $server_was_stopped; + + if ($fw_rule_created) { + &write_test_file("[-] new fw rule created.\n", $current_test_file); + $rv = 0; + } else { + &write_test_file("[+] new fw rule not created.\n", $current_test_file); + } + + unless (&file_find_regex([qr/Args\scontain\sinvalid\sdata/], + $MATCH_ALL, $server_test_file)) { + $rv = 0; + } + + return $rv; +} + +sub overly_long_proto() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ### this packet was generated with a modified fwknop client via the + ### following command line: + # + # LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \ + # "tcp`perl -e '{print "A"x"28"}'`/1" -a 127.0.0.2 -D 127.0.0.1 \ + # --get-key local_spa.key --verbose --verbose + # + # This problem was found by Fernando Arnaboldi of IOActive and exploits + # a buffer overflow in the fwknopd servers prior to 2.0.3 from + # authenticated clients. + # + my $spa_pkt = + '/im5MiJQmOdzqrdWXv+AjEtAm/HsLrdaTFcSw3ZskqpGOdDIrSCz3VXbFfv7qDkc5Y4' . + 'q/k1mRXl9SGzpug87U5dZSyCdAr30z7/2kUFEPTGOQBi/x+L1t1pvdkm4xg13t09ldm' . + '5OD8KiV6qzqLOvN4ULJjvvJJWBZ9qvo/f2Q9Wf67g2KHiwS6EeCINAuMoUw/mNRQMa4' . + 'oGnOXu3/DeWHJAwtSeh7EAr4'; + + my @packets = ( + { + 'proto' => 'udp', + 'port' => $default_spa_port, + 'dst_ip' => $loopback_ip, + 'data' => $spa_pkt, + }, + ); + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); + + $rv = 0 unless $server_was_stopped; + + if ($fw_rule_created) { + &write_test_file("[-] new fw rule created.\n", $current_test_file); + $rv = 0; + } else { + &write_test_file("[+] new fw rule not created.\n", $current_test_file); + } + + unless (&file_find_regex([qr/Args\scontain\sinvalid\sdata/], + $MATCH_ALL, $server_test_file)) { + $rv = 0; + } + + return $rv; +} + +sub overly_long_port() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ### this packet was generated with a modified fwknop client via the + ### following command line: + # + # LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \ + # "tcp/`perl -e '{print "1"x"40"}'`" -a 127.0.0.2 -D 127.0.0.1 \ + # --get-key local_spa.key --verbose --verbose + # + # This problem was found by Fernando Arnaboldi of IOActive and exploits + # a buffer overflow in the fwknopd servers prior to 2.0.3 from + # authenticated clients. + # + my $spa_pkt = + '+JzxeTGlc6lwwzbJSrYChKx8bonWBIPajwGfEtGOaoglcMLbTY/GGXo/nxqiN1LykFS' . + 'lDFXgrkyx2emJ7NGzYqQPUYZxLdZRocR9aRIptvXLLIPBcIpJASi/TUiJlw7CDFMcj0' . + 'ptSBJJUZi0tozpKHETp3AgqfzyOy5FNs38aZsV5/sDl3Pt+kF7fTZJ+YLbmYY4yCUz2' . + 'ZUYoCaJ7X78ULyJTi5eT7nug'; + + my @packets = ( + { + 'proto' => 'udp', + 'port' => $default_spa_port, + 'dst_ip' => $loopback_ip, + 'data' => $spa_pkt, + }, + ); + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS); + + $rv = 0 unless $server_was_stopped; + + if ($fw_rule_created) { + &write_test_file("[-] new fw rule created.\n", $current_test_file); + $rv = 0; + } else { + &write_test_file("[+] new fw rule not created.\n", $current_test_file); + } + + unless (&file_find_regex([qr/Args\scontain\sinvalid\sdata/], + $MATCH_ALL, $server_test_file)) { + $rv = 0; + } + + return $rv; +} + sub altered_base64_spa_data() { my $test_hr = shift; @@ -2357,7 +2766,7 @@ sub server_ignore_small_packets() { } sub client_server_interaction() { - my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_; + my ($test_hr, $pkts_hr, $spa_client_flag) = @_; my $rv = 1; my $server_was_stopped = 1;