3 #############################################################################
7 # URL: http://www.cipherdyne.org/fwknop/
9 # Purpose: fwknop implements an authorization scheme known as Single Packet
10 # Authorization (SPA) that requires only a single encrypted packet to
11 # communicate various pieces of information including desired access
12 # through an iptables/ipfw policy and/or specific commands to execute
13 # on the target system. The main application of this program is to
14 # protect services such as SSH with an additional layer of security
15 # in order to make the exploitation of vulnerabilities (both 0-day
16 # and unpatched code) much more difficult. fwknop also supports
17 # encrypted port knocking, but this is a legacy authentication mode
18 # when compared to SPA.
20 # More information can be found in the fwknop(8) and fwknopd(8) man
21 # pages, and also online here:
23 # http://www.cipherdyne.org/fwknop/docs/
25 # Author: Michael Rash (mbr@cipherdyne.org)
29 # Copyright (C) 2004-2009 Michael Rash (mbr@cipherdyne.org)
31 # License - GNU Public License version 2 (GPLv2):
33 # This program is distributed in the hope that it will be useful,
34 # but WITHOUT ANY WARRANTY; without even the implied warranty of
35 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 # GNU General Public License for more details.
38 # You should have received a copy of the GNU General Public License
39 # along with this program; if not, write to the Free Software
40 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
43 #############################################################################
45 # $Id: fwknop 1533 2009-09-08 02:44:02Z mbr $
57 my $version = '1.9.12';
58 my $revision_svn = '$Revision: 1533 $';
60 ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|;
62 my $lib_dir = '/usr/lib/fwknop';
63 my $print_version = 0;
65 my $run_last_args = 0;
70 my $cmdl_homedir = '';
71 my $knock_sleep = 1; ### default to 1 second difference between port knocks
73 my $knock_dst_pre_resolve = '';
77 my $icmp_type = 8; ### type/code 8/0 => echo request
80 my $server_mode = 'pcap';
81 my $user_rc_file = '';
82 my $server_proto = '';
83 my $run_last_host = '';
84 my $total_digest = '';
85 my $show_last_host_cmd = '';
86 my $show_last_cmd = 0;
87 my $time_offset_plus = '';
88 my $time_offset_minus = '';
89 my $skip_fko_module = 0;
90 my $test_fko_exists = 0;
91 my $use_fko_module = 0;
93 my $http_proxy_host = '';
95 ### the variable is declared, but not defined. This is necessary for the
96 ###--HTTP_proxy cli option to work as expected.
98 my $http_proxy_user = '';
99 my $http_proxy_pass = '';
100 my $gpg_home_dir = '';
101 my $gpg_recipient = '';
102 my $use_gpg_agent = 0;
103 my $max_msg_len = 1500;
104 my $max_resolve_http_recv = 1500;
106 my $gpg_no_options = 0;
107 my $gpg_agent_info = '';
108 my $include_salted = 0;
109 my $client_src_port = 0;
110 my $gpg_default_key = 0;
111 my $gpg_use_options = 0;
112 my $err_wait_timer = 30; ### seconds
113 my $resolve_ip_url = 'http://www.whatismyip.com/automation/n09230945.asp';
114 my $gpg_signing_key = '';
115 my $save_packet_mode = 0;
116 my $save_packet_file = '';
117 my $save_packet_append = 0;
118 my $cmdline_pcap_cmd = '';
119 my $no_save_last_args = 0;
120 my $save_destination = 0;
121 my $server_auth_method = '';
122 my $spa_established_tcp = 0;
123 my $spa_over_http = 0;
124 my $resolve_external_ip = 0;
125 my $server_auth_crypt_pw = '';
126 my $pcap_sleep_interval = 1; ### seconds
127 my $selected_random_nat_port = 0;
128 my $include_base64_trailing_equals = 0;
129 my $include_base64_gnupg_prefix = 0;
130 my $rand_port = 0; ### for SPA packet destination port
131 my $NAT_rand_port = 0; ### for randomized access based on
132 ### NAT rules (e.g. ssh -p <randport>).
133 my $NAT_local = 0; ### Flag for forwarding a port to local socket.
135 my $locale = 'C'; ### default LC_ALL env variable
137 my $gpg_prefix = 'hQ'; ### base64 encoded version of 0x8502
140 ### User agent for contacting http://www.whatismyip.org/, (can
141 ### override with --User-agent)
142 my $ext_resolve_user_agent = "Fwknop/$version";
143 $ext_resolve_user_agent =~ s|-pre\d+||;
146 ### random data : user : client_timestamp : client_version : \
147 ### type (1) : access_request : message digest
148 my $SPA_ACCESS_MODE = 1; ### default
151 ### random data : user : client_timestamp : client_version : \
152 ### type (0) : command : message digest
153 my $SPA_COMMAND_MODE = 0;
155 ### NAT ACCESS message:
156 ### random data : user : client_timestamp : client_version : \
157 ### type (2) : access_request : NAT_info : message digest
158 my $SPA_NAT_ACCESS_MODE = 2;
160 ### ACCESS message with client-defined firewall timeout:
161 ### random data : user : client_timestamp : client_version : \
162 ### type (3) : access_request : timeout : message digest
163 my $SPA_CLIENT_TIMEOUT_ACCESS_MODE = 3;
165 ### NAT ACCESS message with client-defined firewall timeout:
166 ### random data : user : client_timestamp : client_version : \
167 ### type (4) : access_request : NAT_info : timeout : message digest
168 my $SPA_CLIENT_TIMEOUT_NAT_ACCESS_MODE = 4;
170 ### local NAT ACCESS message:
171 ### random data : user : client_timestamp : client_version : \
172 ### type (5) : access_request : NAT_info : message digest
173 my $SPA_LOCAL_NAT_ACCESS_MODE = 5;
175 ### local NAT ACCESS message with client-defined firewall timeout:
176 ### random data : user : client_timestamp : client_version : \
177 ### type (6) : access_request : NAT_info : timeout : message digest
178 my $SPA_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MODE = 6;
180 ### default time values
181 my $knock_interval = 60;
182 my $cmdl_fw_timeout = -1;
184 ### Digest types and command argument flags
187 my $SHA256_DIGEST = 3;
188 my $digest_type = $SHA256_DIGEST; ### default
189 my $cmdl_digest_alg = '';
191 ### default destination port; you can change with --Server-port,
192 ### --rand-port, or by appending the ":<port>" syntax to the
194 my $DEFAULT_PORT = 62201;
196 ### default to root (client must run as root in this mode)
197 my $spoof_username = '';
198 my $spoof_proto = '';
200 ### encrypted port knock vars (these are only used in the legacy
201 ### port knocking mode).
202 my $cmdline_offset = 0;
203 my $enc_port_offset = 61000; ### default offset
205 my $enc_alg = 'Rijndael';
206 my $enc_blocksize = 32;
208 ### there is a constant "RIJNDAEL_KEYSIZE" in the Crypt::Rijndael sources, but
209 ### it is not used; a 16 byte key size is fine.
210 my $enc_keysize = 16;
212 my $enc_shared_secret = '';
213 my $enc_allow_ip = '';
214 my $enc_source_ip = '';
215 my $enc_rotate_proto = 0;
216 my $get_key_file = ''; ### get key from file
217 my $enc_pcap_port = $DEFAULT_PORT;
218 my $cmdl_spa_port = 0;
220 my $NAT_access_str = ''; ### for access through the iptables FORWARD chain
228 my $tcp_nop_type = 1;
229 my $tcp_mss_type = 2;
230 my $tcp_win_scale_type = 3;
231 my $tcp_sack_type = 4;
232 my $tcp_timestamp_type = 8;
234 my %tcp_p0f_opt_types = (
235 'N' => $tcp_nop_type,
236 'M' => $tcp_mss_type,
237 'W' => $tcp_win_scale_type,
238 'S' => $tcp_sack_type,
239 'T' => $tcp_timestamp_type
242 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|;
246 ### run GetOpt() to get command line args
247 &handle_command_line();
249 &usage(0) if $print_help;
251 if ($print_version) {
252 print "[+] fwknop v$version (file revision: $rev_num)\n",
253 " by Michael Rash <mbr\@cipherdyne.org>\n";
257 ### set LC_ALL env variable
258 $ENV{'LC_ALL'} = $locale unless $no_locale;
260 &set_digest_type() if $cmdl_digest_alg;
262 ### import fwknop perl modules
263 &import_perl_modules();
265 ### this is only necessary for older versions of perl (newer versions
266 ### call srand() automatically at the first usage of rand() if srand()
267 ### was not already called).
272 ### save a copy of the SPA destination
273 $knock_dst_pre_resolve = $knock_dst;
277 if ($run_last_args or $show_last_cmd) {
279 ### run fwknop with same command line args as the previous
283 } elsif ($run_last_host or $show_last_host_cmd) {
285 $run_last_host = $show_last_host_cmd if $show_last_host_cmd;
287 ### run fwknop with the last args for this particular knock destination
288 &run_last_host_cmdline();
291 die "[*] Must specify a destination server with -D <IP|Host>"
294 if ($cmdl_fw_timeout != -1) {
295 die "[*] Must specify a firewall timeout >= 0"
296 unless $cmdl_fw_timeout >= 0;
300 if (lc($server_mode) eq 'pcap') {
302 } elsif (lc($server_mode) eq 'knock') {
303 $print_mode = 'encrypted port knocking';
304 } elsif (lc($server_mode) eq 'shared') {
305 $print_mode = 'shared sequence port knocking';
307 die "[*] Unknown server mode: $server_mode ",
308 qq|(must be "pcap", "knock", or "shared").|;
312 print "\n[+] ***DEBUG*** Starting fwknop client ($print_mode mode)...\n";
314 print "\n[+] Starting fwknop client ($print_mode mode)...\n"
319 print "[+] fwknop Command line: @args_cp\n";
322 unless ($knock_dst =~ /$ip_re/ or $http_proxy) {
323 print "[+] Resolving hostname: $knock_dst\n" unless $quiet;
325 my $iaddr = inet_aton($knock_dst)
326 or die "[*] Could not resolve $knock_dst to an IP.";
327 my $addr = inet_ntoa($iaddr)
328 or die "[*] Could not resolve $knock_dst to an IP.";
332 if ($NAT_local and not $NAT_access_str) {
333 if ($NAT_rand_port) {
334 my $rand_port = &rand_port();
335 $NAT_access_str = "$knock_dst,$rand_port";
336 print "[+] Requesting NAT access for randomized port: $rand_port\n";
337 $selected_random_nat_port = 1;
339 $NAT_access_str = "$knock_dst,55000";
341 "[+] Requesting NAT support for port 55,000; use --NAT-rand-port for a\n",
346 &validate_access_str() if $access_str;
347 &validate_NAT_access_str() if $NAT_access_str;
349 if (lc($server_mode) eq 'pcap' or lc($server_mode) eq 'knock') {
350 die "[*] Must also specify: -D <destination>\n"
354 $< == 0 && $> == 0 or
355 die '[*] You must be root (or equivalent ',
356 "UID 0 account) to spoof the source address.\n";
359 unless ($enc_allow_ip
361 or $resolve_external_ip) {
362 die "[*] Must either specify: --allow-IP <IP>, ",
363 "--source-IP, or --Resolve-external-IP\n";
366 ### make fwknop server see "0.0.0.0" in the encrypted sequence.
367 ### This will instruct the server to open the port for whatever
368 ### source IP the sequence comes from. This is useful for
369 ### clients that are behind a NAT device.
370 $enc_allow_ip = '0.0.0.0' if $enc_source_ip;
372 ### resolve the extenal IP via http://www.whatismyip.org
373 $enc_allow_ip = &resolve_external_ip() if $resolve_external_ip;
375 unless ($enc_allow_ip =~ /$ip_re/) {
377 my $iaddr = inet_aton($enc_allow_ip)
378 or die "[*] Could not resolve $enc_allow_ip to IP.";
379 my $addr = inet_ntoa($iaddr)
380 or die "[*] Could not resolve $enc_allow_ip to IP.";
381 $enc_allow_ip = $addr;
384 if ($cmdline_offset) {
385 if (lc($server_mode) eq 'pcap') {
386 die "[*] Port offset is meaningless in pcap mode ",
387 "(only a single packet is sent).";
389 unless ($cmdline_offset < 65280 and $cmdline_offset > 0) {
390 die "[*] Port offset must be 0 < port < 65280";
392 $enc_port_offset = $cmdline_offset;
394 if (lc($server_mode) eq 'pcap') {
395 unless ($enc_pcap_port < 65535 and $enc_pcap_port > 0) {
396 die "[*] Port offset must be 0 < port < 65535";
400 if ($enc_rotate_proto) {
401 die '[*] Can only specify --rotate-proto with ',
402 'encrypted sequences.';
406 if ($save_packet_mode) {
407 ### save of copy of the packet
408 unless ($save_packet_file) {
409 $save_packet_file = "$homedir/fwknop_save_packet.$$";
411 unless ($save_packet_append) {
412 unlink $save_packet_file if -e $save_packet_file;
416 ### save our command line args (so -l can be used next time)
417 unless ($run_last_args or $run_last_host or $no_save_last_args
418 or $show_last_cmd or $show_last_host_cmd) {
422 if (lc($server_mode) eq 'pcap' or lc($server_mode) eq 'knock') {
424 ### get the encryption key from the --get-key file
425 ### or from STDIN if it's not in the file.
428 &handle_server_auth_method() if $server_auth_method;
430 if (lc($server_mode) eq 'pcap') {
432 ### construct and send the encrypted message to the server
433 ### (sends a single packet).
434 &pcap_send_encrypted_msg(&pcap_build_enc_msg());
437 ### we are running in port knocking mode, so get the
438 ### encrypted port sequence (16 ports)
439 &knock_ports(&encrypt_sequence());
442 ### we are running in non-encrypted port knocking mode, so get
443 ### the port sequence
444 &knock_ports(&import_shared_sequence());
447 #============================ end main ==============================
449 sub pcap_build_enc_msg() {
451 ### message format (all fields are separated by ":" characters
453 # random number (16 bytes)
457 # message type and content:
458 # 0 => command mode / command to execute
459 # 1 => access mode / IP,proto,port
460 # 2 => nat access mode / IP,proto,port / internalIP,externalNATPort
461 # (optional) server_auth (post 0.9.2 release)
462 # message digest (SHA256 / SHA1 / MD5 )
466 ### initialize the FKO object if we are using the FKO module
467 if ($use_fko_module) {
468 $fko_obj = FKO->new()
469 or die "[*] Could not acquire FKO object: ", FKO->error_str;
471 print "[+] Using libfko functions via the FKO module.\n";
476 print "\n[+] Building encrypted Single Packet Authorization (SPA) ",
478 print "[+] Packet fields:\n\n";
481 ### start the SPA message with 16 bytes of random data
482 $msg = &SPA_random_number();
484 ### append the username
487 ### append the timestamp
488 $msg .= &SPA_timestamp();
490 ### append the fwknop client version
491 $msg .= &SPA_version();
493 ### append the message type (integer)
494 $msg .= &SPA_message_type();
496 ### append the SPA message (this is usually just a request for
497 ### access to a port/protocol combination)
498 $msg .= &SPA_message();
500 ### append any client defined fw timeout (optional)
501 $msg .= &fko_SPA_client_timeout() if $use_fko_module;
503 ### append NAT access requirement (optional)
504 $msg .= &SPA_nat_access();
506 ### append server authentication method (optional)
507 $msg .= &SPA_server_auth();
509 ### append any client defined fw timeout (optional)
510 $msg .= &no_fko_SPA_client_timeout() unless $use_fko_module;
512 ### append Message Digest
514 $msg .= &SPA_digest($msg);
516 my $encrypted_msg = '';
518 ### encrypt the SPA packet using the FKO module
519 if ($use_fko_module) {
520 $encrypted_msg = &fko_encrypt();
522 return $encrypted_msg;
526 print "\n[+] Clear text message (some fields base64 encoded): $msg\n",
527 " Digest: $total_digest\n";
530 if ($gpg_signing_key or $gpg_recipient) {
531 $encrypted_msg = &pcap_GPG_encrypt_msg($msg);
533 $encrypted_msg = &pcap_Rijndael_encrypt_msg($msg);
536 unless ($include_base64_trailing_equals and $encrypted_msg =~ /=$/) {
537 print "[+] Stripping trailing equals chars from base64 encoding.\n"
539 $encrypted_msg =~ s/=*$//;
542 return $encrypted_msg;
545 sub SPA_random_number() {
549 if ($use_fko_module) {
550 $random_num = $fko_obj->rand_value();
552 $random_num = int(rand(100000000000000));
553 $random_num .= int(rand(10)) while (length($random_num) < 16);
555 print " Random data: $random_num\n" unless $quiet;
557 return '' if $use_fko_module;
564 if ($spoof_username) {
565 $user = $spoof_username;
568 ### getlogin() is better than using ENV{'USER'}, which is
569 ### easily manipulated, so only use as a last resort.
570 if ($spoof_username) {
571 $user = $spoof_username;
573 $user = getlogin() || getpwuid($<) ||
574 die "[*] Could not determine user; try using the ",
575 "--Spoof-user option";
579 if ($use_fko_module) {
580 my $err = $fko_obj->username($user);
582 die "[*] FKO error setting username: ",
583 $fko_obj->errstr($err), "\n";
585 $user = $fko_obj->username();
587 print " Username: $user\n" unless $quiet;
589 return '' if $use_fko_module;
590 return ':' . encode_base64($user, '');
593 sub SPA_timestamp() {
597 $timestamp = time() unless $use_fko_module;
599 if ($time_offset_plus) {
600 my $offset = &time_offset($time_offset_plus);
601 $timestamp += $offset;
602 if ($use_fko_module) {
603 my $err = $fko_obj->timestamp($offset);
605 die "[*] FKO error setting timestamp offset: ",
606 $fko_obj->errstr($err), "\n";
611 if ($time_offset_minus) {
612 my $offset = &time_offset($time_offset_minus);
613 $timestamp -= $offset;
614 if ($use_fko_module) {
615 my $err = $fko_obj->timestamp(-$offset);
617 die "[*] FKO error setting timestamp offset: ",
618 $fko_obj->errstr($err), "\n";
623 $timestamp = $fko_obj->timestamp() if $use_fko_module;
625 print " Timestamp: $timestamp\n" unless $quiet;
627 return '' if $use_fko_module;
628 return ':' . $timestamp;
633 if ($use_fko_module) {
634 print " Version: ", $fko_obj->version(), "\n"
637 print " Version: $version\n" unless $quiet;
640 return '' if $use_fko_module;
641 return ':' . $version;
644 sub SPA_message_type() {
651 if ($cmdline_pcap_cmd) {
652 $print_str = 'command mode';
653 $spa_type = $SPA_COMMAND_MODE;
654 if ($use_fko_module) {
655 $fko_err = $fko_obj->spa_message_type(FKO->FKO_COMMAND_MSG);
656 $spa_type = $fko_obj->spa_message_type();
658 } elsif ($NAT_access_str) {
660 if ($cmdl_fw_timeout >= 0) {
661 $print_str = 'Local NAT client-timeout access mode';
662 $spa_type = $SPA_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MODE;
663 if ($use_fko_module) {
664 $fko_err = $fko_obj->
665 spa_message_type(FKO->FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG);
666 $spa_type = $fko_obj->spa_message_type();
669 $print_str = 'Local NAT access mode';
670 $spa_type = $SPA_LOCAL_NAT_ACCESS_MODE;
671 if ($use_fko_module) {
672 $fko_err = $fko_obj->
673 spa_message_type(FKO->FKO_LOCAL_NAT_ACCESS_MSG);
674 $spa_type = $fko_obj->spa_message_type();
678 if ($cmdl_fw_timeout >= 0) {
679 $print_str = 'NAT client-timeout access mode';
680 $spa_type = $SPA_CLIENT_TIMEOUT_NAT_ACCESS_MODE;
681 if ($use_fko_module) {
682 $fko_err = $fko_obj->
683 spa_message_type(FKO->FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG);
684 $spa_type = $fko_obj->spa_message_type();
687 $print_str = 'NAT access mode';
688 $spa_type = $SPA_NAT_ACCESS_MODE;
689 if ($use_fko_module) {
690 $fko_err = $fko_obj->
691 spa_message_type(FKO->FKO_NAT_ACCESS_MSG);
692 $spa_type = $fko_obj->spa_message_type();
697 if ($cmdl_fw_timeout >= 0) {
698 $print_str = 'access client-timeout mode';
699 $spa_type = $SPA_CLIENT_TIMEOUT_ACCESS_MODE;
700 if ($use_fko_module) {
701 $fko_err = $fko_obj->spa_message_type(FKO->FKO_CLIENT_TIMEOUT_ACCESS_MSG);
702 $spa_type = $fko_obj->spa_message_type();
705 $print_str = 'access mode';
706 $spa_type = $SPA_ACCESS_MODE;
707 if ($use_fko_module) {
708 $fko_err = $fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
709 $spa_type = $fko_obj->spa_message_type();
714 if ($use_fko_module) {
716 die "[*] FKO error setting message type: ",
717 $fko_obj->errstr($fko_err), "\n";
722 print " Type: $spa_type ($print_str)\n";
725 return '' if $use_fko_module;
726 return ':' . $spa_type ;
731 $access_str = 'none/0' unless $access_str;
732 my $print_str = "$enc_allow_ip,$access_str";
734 if ($cmdline_pcap_cmd) {
735 ### a specific command will be executed on the server. Note we
736 ### prepend the command string with the $enc_allow_ip so that the
737 ### fwknopd server can apply the REQUIRE_SOURCE_ADDRESS criteria.
739 $print_str = $cmdline_pcap_cmd;
741 if ($use_fko_module) {
742 my $err = $fko_obj->spa_message("$enc_allow_ip,$cmdline_pcap_cmd");
744 die "[*] FKO error setting command string: ",
745 $fko_obj->errstr($err), "\n";
747 $print_str = $fko_obj->spa_message();
749 print " Cmd: $print_str\n" unless $quiet;
750 return '' if $use_fko_module;
751 return ':' . encode_base64("$enc_allow_ip,$cmdline_pcap_cmd", '');
754 if ($use_fko_module) {
755 my $err = $fko_obj->spa_message("$enc_allow_ip,$access_str");
757 die "[*] FKO error setting access string: ",
758 $fko_obj->errstr($err), "\n";
760 $print_str = $fko_obj->spa_message();
763 ### access to port(s)/protocol(s) will be granted on the
765 print " Access: $print_str\n"
768 return '' if $use_fko_module;
769 return ':' . encode_base64("$enc_allow_ip,$access_str", '');
772 sub fko_SPA_client_timeout() {
773 return '' unless $cmdl_fw_timeout >= 0;
775 if ($use_fko_module) {
776 my $err = $fko_obj->spa_client_timeout($cmdl_fw_timeout);
778 die "[*] FKO error setting timeout: ",
779 $fko_obj->errstr($err), "\n";
785 sub no_fko_SPA_client_timeout() {
786 return '' unless $cmdl_fw_timeout >= 0;
787 return ':' . $cmdl_fw_timeout;
790 sub SPA_server_auth() {
791 if (lc($server_auth_method) eq 'crypt') {
793 print " Server auth: $server_auth_method,";
794 for (my $i=0; $i<length($server_auth_crypt_pw); $i++) {
799 return ':' . encode_base64("crypt,$server_auth_crypt_pw", '');
804 sub SPA_nat_access() {
806 return '' unless $NAT_access_str;
808 my $print_str = $NAT_access_str;
810 if ($use_fko_module) {
811 my $err = $fko_obj->spa_nat_access($NAT_access_str);
813 die "[*] FKO error setting NAT access string: ",
814 $fko_obj->errstr($err), "\n";
816 $print_str = $fko_obj->spa_nat_access();
819 print " NAT access: $print_str\n" unless $quiet;
821 return '' if $use_fko_module;
822 return ':' . encode_base64($NAT_access_str, '');
830 if ($digest_type == $MD5_DIGEST) {
831 if ($use_fko_module) {
832 my $err = $fko_obj->digest_type(FKO->FKO_DIGEST_MD5);
834 die "[*] FKO error setting digest: ",
835 $fko_obj->errstr($err), "\n";
839 Digest::MD5->import(qw(md5_base64));
841 print "[+] Digest::MD5 $Digest::MD5::VERSION\n";
843 $digest = md5_base64($msg);
845 $total_digest = md5_base64("$msg:$digest");
847 print " MD5 digest: $digest\n" unless $quiet;
849 } elsif ($digest_type == $SHA1_DIGEST) {
850 if ($use_fko_module) {
851 my $err = $fko_obj->digest_type(FKO->FKO_DIGEST_SHA1);
853 die "[*] FKO error setting digest: ",
854 $fko_obj->errstr($err), "\n";
858 Digest::SHA->import(qw(sha1_base64));
860 print "[+] Digest::SHA::VERSION $Digest::SHA::VERSION\n";
862 $digest = sha1_base64($msg);
864 $total_digest = sha1_base64("$msg:$digest");
866 print " SHA1 digest: $digest\n" unless $quiet;
868 } elsif ($digest_type == $SHA256_DIGEST) {
869 if ($use_fko_module) {
870 my $err = $fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
872 die "[*] FKO error setting digest: ",
873 $fko_obj->errstr($err), "\n";
877 Digest::SHA->import(qw(sha256_base64));
879 print "[+] Digest::SHA::VERSION $Digest::SHA::VERSION\n";
881 $digest = sha256_base64($msg);
883 $total_digest = sha256_base64("$msg:$digest");
885 print " SHA256 digest: $digest\n" unless $quiet;
888 die "[*] Improper digest algorithm, use --help";
890 return '' if $use_fko_module;
891 return ':' . $digest;
898 if ($gpg_signing_key or $gpg_recipient) {
899 $fko_err = $fko_obj->encryption_type(FKO->FKO_ENCRYPTION_GPG);
901 die "[*] FKO gpg encryption error: ", $fko_obj->errstr($fko_err), "\n"
904 $fko_err = $fko_obj->gpg_home_dir($gpg_home_dir);
905 die "[*] FKO could not set gpg home dir: ",
906 $fko_obj->errstr($fko_err), "\n" if $fko_err;
908 if ($gpg_signing_key) {
909 $fko_err = $fko_obj->gpg_signer($gpg_signing_key);
910 die "[*] FKO could not set gpg signing key: ",
911 $fko_obj->errstr($fko_err), "\n" if $fko_err;
914 if ($gpg_recipient) {
915 $fko_err = $fko_obj->gpg_recipient($gpg_recipient);
916 die "[*] FKO could not set gpg recipient key: ",
917 $fko_obj->errstr($fko_err), "\n" if $fko_err;
921 $fko_err = $fko_obj->spa_data_final($enc_key);
923 die "[*] FKO encryption error: ", $fko_obj->errstr($fko_err), "\n";
927 if ($digest_type == $SHA256_DIGEST) {
928 print " SHA256 digest: ", $fko_obj->spa_digest(), "\n";
929 } elsif ($digest_type == $SHA1_DIGEST) {
930 print " SHA1 digest: ", $fko_obj->spa_digest(), "\n";
931 } elsif ($digest_type == $MD5_DIGEST) {
932 print " MD5 digest: ", $fko_obj->spa_digest(), "\n";
936 print "[+] FKO digest type: ", $fko_obj->digest_type(), "\n";
939 my $encrypted_msg = $fko_obj->spa_data();
941 if ($spa_over_http) {
942 ### change "+" chars to "-", and "/" to "_"
943 $encrypted_msg =~ s|\+|-|g;
944 $encrypted_msg =~ s|\/|_|g;
947 if ($gpg_signing_key or $gpg_recipient) {
948 if ($encrypted_msg =~ /^$gpg_prefix/) {
949 unless ($include_base64_gnupg_prefix) {
950 print qq|[+] Stripping encoded "$gpg_prefix" prefix from |,
951 "outgoing encoded SPA packet.\n" if $debug;
952 ### perl -MMIME::Base64 -e 'print encode_base64("\x85\x02\n")'
953 ### The 'magic' database (via the 'file') command identifies GnuPG
954 ### encrypted files as starting with 0x8502
955 $encrypted_msg =~ s/^$gpg_prefix//;
959 "[-] Warning: GnuPG encrypted SPA packet does not begin with: $gpg_prefix\n",
960 " It is recommend to set GPG_NO_PREFIX_ADD in access.conf on the fwknopd\n",
964 if ($include_salted and $encrypted_msg !~ /^U2FsdGVkX1/) {
965 ### the FKO module does not include the U2FsdGVkX1 string
966 ### so add it if necessary
967 print "[+] Added encoded 'Salted__' prefix (U2FsdGVkX1) to ",
968 "outgoing encoded SPA packet.\n" if $debug;
969 $encrypted_msg = 'U2FsdGVkX1' . $encrypted_msg;
973 unless ($include_base64_trailing_equals and $encrypted_msg =~ /=$/) {
974 print "[+] Stripping trailing equals chars from base64 encoding.\n"
976 $encrypted_msg =~ s/=*$//;
979 return $encrypted_msg;
982 sub assign_spa_port() {
984 if ($rand_port and $cmdl_spa_port != 0) {
985 die "[*] Cannot use --Server-port and --rand-port at the same time";
988 if ($rand_port and $spa_over_http) {
989 die "[*] Cannot use --HTTP and --rand-port at the same time";
992 ### allow for ":<port>" extension to -D arg
993 if ($knock_dst =~ /(.*):(\d+)/) {
997 die "[*] Cannot use --rand-port with a manually ",
998 "specified -D <host>:<port>" if $rand_port;
999 die "[*] Cannot use --Server-port with -D <host>:<port>"
1003 if ($spa_over_http) {
1005 ### default to port 80 if the port has not already been updated
1006 ### (via --Server-port or the above IP:PORT notation).
1007 $enc_pcap_port = 80 unless $knock_dst =~ /.*:\d+/;
1009 ### if using an HTTP proxy, allow the http://HOST:PORT notation
1010 ### to determine the port
1011 ### parses all the potential forms of http_proxy
1012 ###FIXME: Is this the best place to parse this?
1014 if ($http_proxy =~ m|http://(\S+):(\S+)@(\S+):(\d+)|) {
1015 if ($http_proxy_user eq '') {
1016 $http_proxy_user = $1;
1018 if ($http_proxy_pass eq '') {
1019 $http_proxy_pass = $2;
1021 $http_proxy_host = $3;
1022 $enc_pcap_port = $4;
1023 } elsif ($http_proxy =~ m|http://(\S+):(\S+)@(\S+)|) {
1024 if ($http_proxy_user eq '') {
1025 $http_proxy_user = $1;
1027 if ($http_proxy_pass eq '') {
1028 $http_proxy_pass = $2;
1030 $http_proxy_host = $3;
1031 } elsif ($http_proxy =~ m|http://(\S+):(\d+)|) {
1032 $http_proxy_host = $1;
1033 $enc_pcap_port = $2;
1034 } elsif ($http_proxy =~ m|http://(\S+)|) {
1035 $http_proxy_host = $1;
1037 die "[*] Proxy must begin with 'http://'";
1039 if ($http_proxy_host =~ m|/|) {
1040 die "[*] Proxy host must be a valid hostname";
1046 ### send the SPA packet over a random port between 10,000 and 65535
1047 $enc_pcap_port = &rand_port();
1050 $enc_pcap_port = $cmdl_spa_port if $cmdl_spa_port;
1055 sub pcap_GPG_encrypt_msg() {
1058 my $gnupg = GnuPG::Interface->new();
1060 my %gnupg_options = (
1062 'homedir' => $gpg_home_dir,
1066 delete $gnupg_options{'batch'} if $gpg_verbose;
1067 delete $gnupg_options{'no_options'} if $gpg_use_options;
1069 $gnupg->options->hash_init(%gnupg_options);
1071 ### if --gpg-default-key is given, then we trust that the user has
1072 ### set the default key with the default-key variable in ~/.gnupg/options
1073 ### and we need to enable options
1074 if ($gpg_default_key) {
1075 delete $gnupg_options{'no_options'}
1076 if defined delete $gnupg_options{'no_options'};
1078 $gnupg->options->default_key($gpg_signing_key);
1081 $gnupg->options->push_recipients($gpg_recipient);
1084 ### normally gpg is in the local path, but if not --gpg-path can
1085 ### provide a custom path
1086 $gnupg->call($gpg_path);
1089 my ($input, $output, $error, $pw, $status) =
1096 my $handles = GnuPG::Handles->new(
1106 if ($use_gpg_agent or $gpg_agent_info) {
1107 if ($gpg_agent_info) {
1108 $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info;
1110 $pid = $gnupg->sign_and_encrypt('handles' => $handles,
1111 'command_args' => [ qw( --use-agent ) ]);
1113 $pid = $gnupg->sign_and_encrypt('handles' => $handles);
1122 my @ciphertext = <$output>;
1125 my @errors = <$error>;
1132 $ctext = join '', @ciphertext;
1136 print "[*] GnuPG encrypt failed.\n";
1137 unless ($gpg_verbose) {
1138 print " GnuPG errors:\n";
1145 print "[+] Encrypted msg hex dump (" .
1146 length($ctext) . " bytes):\n";
1150 my $encoded_msg = encode_base64($ctext, '');
1152 if ($verbose and $debug) {
1153 print "[+] base64-encoded message before stripping identifying chars:\n",
1157 if ($spa_over_http) {
1158 ### change "+" chars to "-", and "/" to "_"
1159 $encoded_msg =~ s|\+|-|g;
1160 $encoded_msg =~ s|\/|_|g;
1163 if ($encoded_msg =~ /^$gpg_prefix/) {
1164 unless ($include_base64_gnupg_prefix) {
1165 print qq|[+] Stripping encoded "$gpg_prefix" prefix from |,
1166 "outgoing encoded SPA packet.\n" if $debug;
1167 ### perl -MMIME::Base64 -e 'print encode_base64("\x85\x02\n")'
1168 ### The 'magic' database (via the 'file') command identifies GnuPG
1169 ### encrypted files as starting with 0x8502
1170 $encoded_msg =~ s/^$gpg_prefix//;
1174 "[-] Warning: GnuPG encrypted SPA packet does not begin with: $gpg_prefix\n",
1175 " It is recommend to set GPG_NO_PREFIX_ADD in access.conf on the fwknopd\n",
1179 print "[+] Encrypted message: $encoded_msg\n" if $debug;
1180 return $encoded_msg;
1183 sub pcap_Rijndael_encrypt_msg() {
1189 print "[+] Crypt::CBC::VERSION $Crypt::CBC::VERSION\n";
1192 my $cipher = Crypt::CBC->new({
1194 'cipher' => $enc_alg,
1197 my $encrypted_msg = $cipher->encrypt($msg);
1200 print "\n[+] Encrypted msg hex dump before base64 encoding (" .
1201 length($encrypted_msg) . " bytes):\n";
1202 &hex_dump($encrypted_msg);
1205 my $encoded_msg = encode_base64($encrypted_msg, '');
1207 if ($verbose and $debug) {
1208 print "[+] base64-encoded message before stripping identifying chars:\n",
1212 if ($spa_over_http) {
1213 ### change "+" chars to "-", and "/" to "_"
1214 $encoded_msg =~ s|\+|-|g;
1215 $encoded_msg =~ s|\/|_|g;
1218 ### Crypt::CBC adds the string "Salted__" to the beginning of the
1219 ### encrypted text (at least for how we create the cipher object
1220 ### above), so delete the encoded version of this string ("U2FsdGVkX1")
1221 ### before sending on the wire. The fwknopd server will add this
1222 ### string back in before decrypting. This makes it harder to write
1223 ### an IDS signature that looks for fwknop traffic (e.g. look for the
1224 ### string "U2FsdGVkX1" over UDP port 62201).
1225 unless ($include_salted) {
1226 print "[+] Stripping encoded 'Salted__' prefix (U2FsdGVkX1) from ",
1227 "outgoing encoded SPA packet.\n" if $debug;
1228 $encoded_msg =~ s/^U2FsdGVkX1//; ### encoded "Salted__" string
1231 print "[+] Encrypted message: $encoded_msg\n" if $debug;
1232 return $encoded_msg;
1235 sub pcap_send_encrypted_msg() {
1238 if ($spa_over_http) {
1239 ### make sure that the request begins with "/"
1240 $msg = '/' . $msg unless $msg =~ m|^/|;
1243 my $msg_len = length($msg);
1245 if ($msg_len > $max_msg_len) {
1246 die "[*] Message length is too long ($msg_len bytes), ",
1247 "must be less than $max_msg_len bytes";
1251 print "\n[+] Packet data:\n\n", $msg, "\n\n" unless $quiet;
1254 if ($save_packet_mode) {
1255 print " Saving packet data to: $save_packet_file\n" unless $quiet;
1256 if ($save_packet_append) {
1257 open F, ">> $save_packet_file" or die "[*] Could not open ",
1258 "$save_packet_file: $!";
1260 open F, "> $save_packet_file" or die "[*] Could not open ",
1261 "$save_packet_file: $!";
1267 if ($spa_over_http) {
1270 &send_spa_packet_over_http($msg, $msg_len);
1272 } elsif ($spa_established_tcp
1273 or ($server_proto =~ /tcp/i and not $spoof_src)) {
1275 ### SPA over established TCP socket - useful for Tor
1276 &send_spa_packet_over_tcp($msg, $msg_len);
1280 ### check to see if we are supposed to spoof our source address,
1281 ### or use a raw socket. If not, then we default to sending the
1282 ### SPA packet over a normal UDP socket
1283 my $send_over_raw_socket = '';
1285 if ($server_proto and $server_proto =~ /icmp/i) {
1286 $send_over_raw_socket = 'icmp';
1290 $send_over_raw_socket = lc($spoof_proto);
1293 ### note that a spoofed source address is not required for sending
1294 ### over a raw socket - if not specified, then the OS will assign
1295 ### the IP of the outgoing interface.
1298 unless ($send_over_raw_socket) {
1299 if ($server_proto =~ /tcp/i) {
1300 $send_over_raw_socket = 'tcp';
1302 $send_over_raw_socket = 'udp';
1306 unless ($spoof_src =~ /$ip_re/) {
1307 ### resolve to an IP
1308 my $iaddr = inet_aton($spoof_src)
1309 or die "[*] Could not resolve $spoof_src to IP.";
1310 my $addr = inet_ntoa($iaddr)
1311 or die "[*] Could not resolve $spoof_src to IP.";
1316 if ($send_over_raw_socket) {
1318 ### use Net::RawIP to spoof the packets
1322 print "[+] Net::RawIP::VERSION $Net::RawIP::VERSION\n";
1325 if ($send_over_raw_socket eq 'tcp') {
1326 &send_spa_over_raw_tcp($msg, $msg_len, $spoof_src);
1327 } elsif ($send_over_raw_socket eq 'udp') {
1328 &send_spa_over_raw_udp($msg, $msg_len, $spoof_src);
1329 } elsif ($send_over_raw_socket eq 'icmp') {
1330 &send_spa_over_raw_icmp($msg, $msg_len, $spoof_src);
1332 die "[*] Unrecognized protocol: $send_over_raw_socket ",
1338 ### default communication of SPA packet over UDP socket
1339 &send_spa_packet_over_udp($msg, $msg_len);
1345 if ($NAT_access_str and $NAT_access_str =~ /($ip_re),(\d+)/) {
1346 my $internal_ip = $1;
1348 print " Requesting NAT access to $access_str on $internal_ip via ",
1349 "port $nat_port\n\n";
1354 print " --Test-mode enabled, SPA packet not sent.\n\n";
1360 sub send_spa_packet_over_http() {
1361 my ($msg, $msg_len) = @_;
1363 ### default to use the pre-resolution host as the HTTP server to
1364 ### send the SPA packet to.
1365 my $http_host = $knock_dst_pre_resolve;
1366 my $http_host_ip = $knock_dst;
1367 my $http_proxy_auth_string = '';
1368 if ($http_proxy_host) {
1370 ### if we are sending the SPA packet through a proxy, set the
1371 ### SPA destination IP as the IP of the proxy host, and use the
1372 ### -D arg as part of the end host URL
1374 $http_host = $http_proxy_host;
1376 $knock_dst_pre_resolve =~ s|/$|| if $knock_dst_pre_resolve =~ m|/$|;
1377 $knock_dst_pre_resolve =~ s|http://||
1378 if $knock_dst_pre_resolve =~ m|^http://|;
1379 $knock_dst_pre_resolve =~ s|(.*?)/.*|$1|;
1381 ### this is used as the GET request
1382 $msg = "http://${knock_dst_pre_resolve}${msg}"; ### FIXME, include http?
1384 $http_host = $http_proxy_host;
1385 $http_host_ip = $http_proxy_host;
1387 unless ($http_host_ip =~ /$ip_re/) {
1388 ### resolve to an IP
1389 my $iaddr = inet_aton($http_host_ip)
1390 or die "[*] Could not resolve $http_host_ip to an IP.";
1391 my $addr = inet_ntoa($iaddr)
1392 or die "[*] Could not resolve $http_host_ip to an IP.";
1393 $http_host_ip = $addr;
1395 if ($http_proxy_user) {
1396 my $proxy_auth = encode_base64($http_proxy_user . ':' . $http_proxy_pass);
1397 $http_proxy_auth_string = 'Proxy-Authorization: Basic ' . $proxy_auth . "\r\n";
1401 print "\n[+] Sending SPA packet over HTTP to ",
1402 "$http_host:$enc_pcap_port...\n Sending $msg_len ",
1403 "byte message to $http_host over established ",
1404 "tcp/$enc_pcap_port socket...\n"
1407 my $http_request = "GET $msg HTTP/1.0\r\n" .
1408 "User-Agent: $ext_resolve_user_agent\r\n" .
1410 "Host: $http_host\r\n" . ### FIXME?
1411 "Connection: Keep-Alive\r\n" .
1412 "$http_proxy_auth_string" .
1415 print "[+] Sending SPA HTTP request:\n\n$http_request" if $debug;
1417 unless ($test_mode) {
1418 my $sock = IO::Socket::INET->new(
1419 PeerAddr => $http_host_ip,
1420 PeerPort => $enc_pcap_port,
1423 ) or die "[*] Could not acquire TCP/$enc_pcap_port socket ",
1424 "with $http_host_ip: $!";
1425 if (defined($sock)) {
1427 print $sock $http_request;
1428 recv($sock, my $web_data, $max_resolve_http_recv, 0);
1431 print "\n[+] Closing connection...\n";
1433 die "[*] Could not build TCP socket.";
1439 sub send_spa_packet_over_tcp() {
1440 my ($msg, $msg_len) = @_;
1442 print "\n[+] Establishing tcp connection to ",
1443 "$knock_dst:$enc_pcap_port...\n Sending $msg_len ",
1444 "byte message to $knock_dst over established ",
1445 "tcp/$enc_pcap_port socket...\n"
1448 unless ($test_mode) {
1449 my $socket = IO::Socket::INET->new(
1450 PeerAddr => $knock_dst,
1451 PeerPort => $enc_pcap_port,
1454 ) or die "[*] Could not acquire TCP/$enc_pcap_port socket ",
1455 "with $knock_dst: $!";
1457 $socket->send($msg);
1459 print "\n[+] Closing connection...\n";
1466 sub send_spa_packet_over_udp() {
1467 my ($msg, $msg_len) = @_;
1469 print "\n[+] Sending $msg_len byte message to $knock_dst ",
1470 "over udp/$enc_pcap_port...\n" unless $quiet;
1472 unless ($client_src_port) {
1473 $client_src_port = &rand_port();
1475 unless ($test_mode) {
1476 my $socket = IO::Socket::INET->new(
1477 PeerAddr => $knock_dst,
1478 PeerPort => $enc_pcap_port,
1479 LocalPort => $client_src_port,
1482 ) or die "[*] Could not acquire UDP socket: $!";
1484 $socket->send($msg);
1490 sub send_spa_over_raw_tcp() {
1491 my ($msg, $msg_len) = @_;
1495 "\n[+] Sending $msg_len byte message to $knock_dst over TCP/$enc_pcap_port";
1497 print "\n (spoofed src IP: $spoof_src)";
1502 my $rand_src_port = int(rand(65535));
1503 $rand_src_port = 65001 if $rand_src_port > 65535;
1504 $rand_src_port += 1024 if $rand_src_port < 1024;
1506 my $rawpkt = new Net::RawIP({
1508 saddr => $spoof_src,
1512 $rawpkt->set({ ip => {
1513 saddr => $spoof_src,
1518 source => $rand_src_port,
1519 dest => $enc_pcap_port,
1523 $rawpkt->send() unless $test_mode;
1528 sub send_spa_over_raw_udp() {
1529 my ($msg, $msg_len, $spoof_src) = @_;
1533 "\n[+] Sending $msg_len byte message to $knock_dst over UDP/$enc_pcap_port";
1535 print "\n (spoofed src IP: $spoof_src)";
1540 my $rand_src_port = int(rand(65535));
1541 $rand_src_port = 65001 if $rand_src_port > 65535;
1542 $rand_src_port += 1024 if $rand_src_port < 1024;
1544 my $rawpkt = new Net::RawIP({
1546 saddr => $spoof_src,
1550 $rawpkt->set({ ip => {
1551 saddr => $spoof_src,
1555 source => $rand_src_port,
1556 dest => $enc_pcap_port,
1560 $rawpkt->send() unless $test_mode;
1565 sub send_spa_over_raw_icmp() {
1566 my ($msg, $msg_len) = @_;
1569 print "\n[+] Sending $msg_len byte message to $knock_dst over ICMP";
1571 print "\n (spoofed src IP: $spoof_src)";
1576 my $rawpkt = new Net::RawIP({
1578 saddr => $spoof_src,
1582 $rawpkt->set({ ip => {
1583 saddr => $spoof_src,
1593 $rawpkt->send() unless $test_mode;
1599 my $ports_aref = shift;
1602 print "[+] --Test-mode enabled, not sending sequence.\n";
1606 print "[+] Sending port knocking sequence to knock server: $knock_dst\n"
1609 for my $href (@$ports_aref) {
1610 my $proto = $href->{'proto'};
1611 my $port = $href->{'port'};
1612 ### note that we never care if the destination replies with a
1613 ### RST or icmp echo reply (or anything else). In fact, hopefully
1614 ### the remote firewall is configued to not reply at all
1615 if ($proto eq 'icmp') {
1616 require Net::Ping::External;
1617 Net::Ping::External->import(qw/ping/);
1620 print "[+] Net::Ping::External::VERSION ",
1621 "$Net::Ping::External::VERSION\n";
1624 print " icmp echo request -> $knock_dst\n";
1625 ping(hostname => "$knock_dst", count => 1, timeout => 1);
1628 print " -> $knock_dst $proto/$port (packet: $packet_ctr)\n";
1629 my $socket = IO::Socket::INET->new(
1630 PeerAddr => $knock_dst,
1634 ); ### note there is no "or die" here since we just want to throw
1635 ### packets on the network
1636 if (defined $socket and $proto eq 'udp') {
1637 $socket->send('0'); ### have to actually send something for udp
1640 if ($proto eq 'tcp' and $knock_sleep >= 1) {
1643 undef $socket if defined $socket;
1647 print "[+] Finished knock sequence.\n";
1651 sub encrypt_sequence() {
1654 my @encrypted_seq = ();
1659 print "[+] Crypt::CBC::VERSION $Crypt::CBC::VERSION\n";
1662 my $cipher = Crypt::CBC->new({
1664 'cipher' => $enc_alg,
1667 my @octets = split /\./, $enc_allow_ip;
1669 $clear_txt .= chr($_) for @octets;
1670 $checksum += $_ for @octets;
1673 my $enc_allow_port = 0;
1674 if ($access_str =~ /tcp/i) {
1676 if ($access_str =~ /(\d+)/) {
1677 $enc_allow_port = $1;
1679 } elsif ($access_str =~ /udp/i) {
1681 if ($access_str =~ /(\d+)/) {
1682 $enc_allow_port = $1;
1684 } elsif ($access_str =~ /icmp/i) {
1686 $enc_allow_port = 0;
1689 unless ($enc_allow_port) {
1690 die "[*] Must specify port to open."
1693 my $port_upper_bits = $enc_allow_port;
1694 my $port_lower_bits = $enc_allow_port;
1696 if ($enc_allow_port == 0) {
1697 $port_upper_bits = 0;
1698 $port_lower_bits = 0;
1700 $port_upper_bits = $port_upper_bits >> 8;
1701 $port_lower_bits = $port_lower_bits % 256;
1704 $clear_txt .= chr($port_upper_bits);
1705 $clear_txt .= chr($port_lower_bits);
1707 $checksum += $port_upper_bits;
1708 $checksum += $port_lower_bits;
1710 $clear_txt .= chr($proto_num);
1711 $checksum += $proto_num;
1713 $checksum = $checksum % 256;
1715 $clear_txt .= chr($checksum);
1718 ### FIXME: either the checksum should be removed, or it should
1719 ### be applied to the username as well.
1720 my $username = getlogin() || getpwuid($<) || die "[*] Could not ",
1721 "get process username.";
1724 my @chars = split //, $username;
1725 for my $char (@chars) {
1726 if (length($clear_txt) < $enc_blocksize-1) {
1727 $clear_txt .= $char;
1732 my @tmp_chars = split //, $clear_txt;
1733 print '[+] Clear-text sequence (' . length($clear_txt) . ' bytes): ';
1734 print ord($_) . ' ' for @tmp_chars;
1737 my $cipher_txt = $cipher->encrypt($clear_txt);
1740 @tmp_chars = split //, $cipher_txt;
1741 print '[+] Cipher-text sequence (' . length($cipher_txt) . ' bytes): ';
1742 print ord($_) . ' ' for @tmp_chars;
1744 print "\n Port offset: $enc_port_offset\n";
1746 my @chars = split //, $cipher_txt;
1748 for my $char (@chars) {
1750 if ($enc_rotate_proto) {
1751 ### alternate between tcp and udp protocols
1752 if ($char_ctr % 2 == 0) {
1753 %hsh = ('port' => ord($char) + $enc_port_offset,
1756 %hsh = ('port' => ord($char) + $enc_port_offset,
1760 ### hardcode knock sequence proto as tcp
1761 %hsh = ('port' => ord($char) + $enc_port_offset,
1764 push @encrypted_seq, \%hsh;
1767 return \@encrypted_seq;
1770 sub resolve_external_ip() {
1772 my $external_ip = '';
1776 if ($resolve_ip_url) {
1777 die "[*] $resolve_ip_url does not begin with http://"
1778 unless $resolve_ip_url =~ m|http://|i;
1780 if ($resolve_ip_url =~ m|http://(\S+?)/(\S+)|i) {
1783 } elsif ($resolve_ip_url =~ m|http://(\S+?)/|i) {
1785 } elsif ($resolve_ip_url =~ m|http://(\S+)|i) {
1786 ### there is no trailing slash
1789 die "[*] Could not get external hostname from $resolve_ip_url";
1793 print " Resolving external IP via: $resolve_ip_url\n"
1795 my $w_ip_tmp = inet_aton($site_host)
1796 or die "[*] Could not resolve $site_host to an IP.";
1797 my $w_ip = inet_ntoa($w_ip_tmp)
1798 or die "[*] Could not resolve $site_host to an IP.";
1800 my $sock = new IO::Socket::INET(
1805 or die "[*] Could not open tcp/80 socket with $resolve_ip_url";
1807 if (defined($sock)) {
1808 print $sock "GET $url HTTP/1.0\r\n",
1809 "Host: $site_host\r\n",
1810 "User-Agent: $ext_resolve_user_agent\r\n",
1812 "Connection: Keep-Alive\r\n\r\n";
1813 recv($sock, my $web_data, $max_resolve_http_recv, 0);
1815 $web_data =~ s/[^\w\.]/ /g;
1817 print "[+] Web server data from: $resolve_ip_url\n",
1820 if ($resolve_ip_url =~ /whatismyip/i) {
1821 if ($web_data =~ /WhatIsMyIP\.com\s+-\s+($ip_re)\b/i) {
1825 unless ($external_ip) {
1826 ### greedy match to the last instance of a matching
1827 ### IP regex so that we get past any HTTP header info
1828 ### that might happen to match the IP regex
1829 if ($web_data =~ /\b($ip_re)\b/) {
1834 unless ($external_ip) {
1835 print "[*] Could not extract external IP from $resolve_ip_url\n";
1838 " You might try running with --debug and looking at the response from\n",
1839 " the webserver. Maybe it is trying to set a cookie?\n";
1844 print " Got external address: $external_ip\n\n" unless $quiet;
1845 return $external_ip;
1850 if ($gpg_signing_key or $gpg_default_key) {
1852 ### load the GnuPG::Interface module
1853 require GnuPG::Interface;
1856 print "[+] GnuPG::Interface::VERSION ",
1857 "$GnuPG::Interface::VERSION\n";
1860 ### we don't need a password if we are going to acquire
1861 ### a password from gpg-agent
1862 return if $use_gpg_agent;
1865 if ($get_key_file) {
1866 ### get the encryption key from file
1867 open F, "< $get_key_file" or die "[*] Could not open ",
1868 "$get_key_file: $!";
1871 for my $line (@lines) {
1873 if ($line =~ /$knock_dst:\s*(.*)/) {
1876 } elsif ($line =~ /$knock_dst_pre_resolve:\s*(.*)/) {
1883 "[*] Could not read encryption key/password for $knock_dst_pre_resolve\n",
1884 " from $get_key_file; fwknop expects the following format:\n",
1885 "$knock_dst_pre_resolve: <KEY/password>\n";
1888 if ($gpg_signing_key or $gpg_default_key) {
1890 "[+] Enter the GnuPG password for signing key: $gpg_signing_key\n\n"
1894 "[+] Enter an encryption key. This key must match a key in the file\n",
1895 " /etc/fwknop/access.conf on the remote system.\n\n" unless $quiet;
1902 if ($try >= $max_tries) {
1904 die "[*] Exceeded $max_tries tries to read valid password.";
1906 if ($gpg_signing_key or $gpg_default_key) {
1907 print "GnuPG signing password: ";
1909 print "Encryption Key: ";
1911 my $ans = ReadLine(0);
1912 next KEY unless defined $ans;
1913 next KEY unless $ans =~ /\S/;
1915 if ($gpg_signing_key or $gpg_default_key) {
1919 if (length($ans) >= 8) {
1924 die "\n[-] The symmetric key must be at least ",
1925 "8 characters long.\n";
1932 die "[*] Could not read encryption key from STDIN. Exiting."
1935 unless ($gpg_signing_key or $gpg_default_key) {
1936 unless (length($enc_key) >= 8) {
1937 die "\n[-] The symmetric key must be at least ",
1938 "8 characters long.\n";
1940 ### pad out to the key size
1941 while (length($enc_key) < $enc_keysize) {
1948 sub import_perl_modules() {
1950 my $mod_paths_ar = &get_mod_paths();
1952 if ($#$mod_paths_ar > -1) { ### /usr/lib/fwknop/ exists
1953 push @$mod_paths_ar, @INC;
1954 splice @INC, 0, $#$mod_paths_ar+1, @$mod_paths_ar;
1958 print "[+] import_perl_modules(): The \@INC array:\n";
1959 print "$_\n" for @INC;
1962 ### see if the FKO module is installed
1963 unless ($skip_fko_module) {
1964 eval { require FKO };
1966 $use_fko_module = 1;
1967 if ($debug or $test_fko_exists) {
1968 print "[+] Using FKO module.\n";
1970 exit 0 if $test_fko_exists;
1974 require Term::ReadKey;
1975 Term::ReadKey->import(qw/ReadMode ReadLine/);
1977 print "[+] Term::ReadKey::VERSION $Term::ReadKey::VERSION\n",
1983 sub get_mod_paths() {
1987 unless (-d $lib_dir) {
1988 my $dir_tmp = $lib_dir;
1989 $dir_tmp =~ s|lib/|lib64/|;
1991 $lib_dir = $dir_tmp;
1997 opendir D, $lib_dir or die "[*] Could not open $lib_dir: $!";
1998 my @dirs = readdir D;
2001 push @paths, $lib_dir;
2003 for my $dir (@dirs) {
2004 ### get directories like "/usr/lib/fwknop/x86_64-linux"
2005 next unless -d "$lib_dir/$dir";
2006 push @paths, "$lib_dir/$dir"
2007 if $dir =~ m|linux| or $dir =~ m|thread|
2008 or (-d "$lib_dir/$dir/auto");
2013 sub import_shared_sequence() {
2014 my $connect_file = '';
2016 if ($user_rc_file and -e $user_rc_file) {
2017 $connect_file = $user_rc_file;
2018 } elsif (-e "$homedir/.fwknoprc") { ### this is the default unless -f was given
2019 $connect_file = "$homedir/.fwknoprc";
2021 unless ($user_rc_file) {
2022 print "[+] Creating fwknop rc file: $homedir/.fwknoprc\n",
2023 " This file is used only to define shared knock sequences. ",
2024 "If you want\n to send an encrypted sequence, use the ",
2025 "--encrypt argument.\n\n[+] To send a shared sequence you will ",
2026 "first need to define\n the sequence in $homedir/.fwknoprc\n";
2027 open F, "> $homedir/.fwknoprc" or
2028 die "[*] Could not open $homedir/.fwknoprc: $!";
2029 print F "# Shared knock sequence config file for fwknop. This file adheres to the\n",
2030 "# following format:\n# <knockdst>: <proto/port>, ..., <proto/port>. See the example ",
2031 "# below:\n\n# 192.168.10.2: tcp/5501, tcp/5502, udp/1001, tcp/5504\n\n";
2037 open F, "< $connect_file" or die "[*] Could not open ",
2038 "$connect_file: $!";
2042 ### parse out the knock sequence
2043 my @knock_sequence = ();
2046 for my $line (@lines) {
2048 next unless $line =~ /\S/;
2049 next if $line =~ /^\s*#/;
2050 if ($line =~ /^\s*(\S+):\s*(.*)/) {
2054 next unless $dst eq $knock_dst;
2055 my @ports_arr = split /\s*\,\s*/, $ports;
2056 next unless @ports_arr and $#ports_arr > 0;
2058 for my $port (@ports_arr) {
2060 if ($port =~ m|tcp/(\d+)|) {
2061 %hsh = ('port' => $1, 'proto' => 'tcp');
2062 } elsif ($port =~ m|udp/(\d+)|) {
2063 %hsh = ('port' => $1, 'proto' => 'udp');
2064 } elsif ($port =~ m|icmp|) {
2065 %hsh = ('port' => -1, 'proto' => 'icmp');
2068 push @knock_sequence, \%hsh;
2072 die "[*] Could not find destination: $knock_dst in $connect_file"
2074 die "[*] No port sequence defined for $knock_dst in $connect_file"
2075 unless @knock_sequence;
2076 return \@knock_sequence;
2081 if ($cmdl_homedir) {
2082 $homedir = $cmdl_homedir;
2084 ### prefer homedir specified in /etc/passwd (if it exists)
2085 if (-e '/etc/passwd') {
2086 open P, "< /etc/passwd" or die "[*] Could not open /etc/passwd. ",
2090 for my $line (@lines) {
2091 ### mbr:x:222:222:Michael Rash:/home/mbr:/bin/bash
2093 if ($line =~ /^(?:.*:){2}$uid:(?:.*:){2}(\S+):/) {
2099 unless ($homedir and -d $homedir) {
2100 $homedir = $ENV{'HOME'} if defined $ENV{'HOME'};
2103 die '[*] Could not determine homedir, use --Home option.'
2104 unless ($homedir and -d $homedir);
2106 if ($gpg_signing_key or $gpg_recipient) {
2107 $gpg_home_dir = "$homedir/.gnupg" unless $gpg_home_dir;
2113 sub handle_server_auth_method() {
2114 if (lc($server_auth_method) eq 'crypt') {
2117 $quiet == 1 ? print "UNIX crypt() password: "
2118 : print " UNIX crypt() password: ";
2119 my $ans = ReadLine(0);
2121 next unless $ans =~ /\S/;
2122 $server_auth_crypt_pw = $ans;
2129 die "[*] --Server-auth must be 'crypt'";
2133 my $save_file = "$homedir/.fwknop.run";
2134 my $hosts_file = "$homedir/.fwknop.hosts";
2136 open S, "> $save_file" or die "[*] Could not open $save_file";
2137 print S "@args_cp\n";
2140 if ($save_destination) {
2141 open D, "> $homedir/.fwknop.save"
2142 or die "[*] Could not open $homedir/.fwknop.save";
2143 print D "@args_cp\n";
2147 my @host_lines = ();
2148 my $matched_dst = 0;
2149 if (-e $hosts_file) {
2150 open F, "< $hosts_file" or die "[*] Could not open $hosts_file";
2152 if (/-(k|D)\S*\s+$knock_dst_pre_resolve/) {
2153 ### if an older command is for the same knock destination
2154 ### then substitute the current command (doesn't yet support
2155 ### multiple commands per knock destination since we would
2156 ### need a way to select among them)
2157 push @host_lines, "@args_cp\n";
2160 push @host_lines, $_;
2165 push @host_lines, "@args_cp\n" unless $matched_dst;
2167 open H, "> $hosts_file" or die "[*] Could not open $hosts_file";
2168 print H for @host_lines;
2173 sub handle_command_line() {
2175 ### make Getopts case sensitive
2176 Getopt::Long::Configure('no_ignore_case');
2178 die "[*] Use --help for usage information.\n" unless GetOptions(
2179 'Server-port=i' => \$cmdl_spa_port,
2180 'Server-mode=s' => \$server_mode,
2181 'Server-cmd=s' => \$cmdline_pcap_cmd,
2182 'Server-proto=s' => \$server_proto,
2183 'Server-auth=s' => \$server_auth_method,
2184 'Spoof-src=s' => \$spoof_src,
2185 'icmp-type=i' => \$icmp_type,
2186 'icmp-code=i' => \$icmp_code,
2187 'rand-port' => \$rand_port,
2188 'NAT-rand-port' => \$NAT_rand_port,
2189 'NAT-local' => \$NAT_local,
2190 'NAT-access=s' => \$NAT_access_str,
2191 'Max-packet-size=i' => \$max_msg_len,
2192 'Max-resolve-http-size=i' => \$max_resolve_http_recv,
2193 'Source-port=i' => \$client_src_port,
2194 'Spoof-user=s' => \$spoof_username,
2195 'Spoof-proto=s' => \$spoof_proto,
2196 'Save-packet' => \$save_packet_mode,
2197 'Save-packet-file=s' => \$save_packet_file,
2198 'Save-packet-append' => \$save_packet_append,
2199 'Save-dst' => \$save_destination,
2200 'user-rc=s' => \$user_rc_file,
2201 'knock-dst=s' => \$knock_dst,
2202 'Destination=s' => \$knock_dst,
2203 'time-offset-plus=s' => \$time_offset_plus,
2204 'time-offset-minus=s' => \$time_offset_minus,
2205 'gpg-signing-key=s' => \$gpg_signing_key,
2206 'gpg-recipient=s' => \$gpg_recipient,
2207 'gpg-default-key' => \$gpg_default_key,
2208 'gpg-home-dir=s' => \$gpg_home_dir,
2209 'gpg-verbose' => \$gpg_verbose,
2210 'gpg-agent' => \$use_gpg_agent,
2211 'gpg-agent-info=s' => \$gpg_agent_info,
2212 'gpg-no-options' => \$gpg_no_options,
2213 'gpg-use-options' => \$gpg_use_options,
2214 'gpg-prefix=s' => \$gpg_prefix,
2215 'gpg-path=s' => \$gpg_path,
2217 'Forward-access=s' => \$NAT_access_str,
2218 'TCP-sock' => \$spa_established_tcp,
2219 'HTTP' => \$spa_over_http,
2220 'HTTP-proxy:s' => \$http_proxy, # the :s indicates that the argument is optional
2221 'HTTP-proxy-user=s' => \$http_proxy_user,
2222 'HTTP-proxy-password=s' => \$http_proxy_pass,
2223 'HTTP-user-agent=s' => \$ext_resolve_user_agent,
2224 'Access=s' => \$access_str,
2225 'fw-timeout=i' => \$cmdl_fw_timeout,
2226 'allow-IP=s' => \$enc_allow_ip,
2227 'digest-alg=s' => \$cmdl_digest_alg,
2228 'source-IP' => \$enc_source_ip,
2229 'rotate-proto' => \$enc_rotate_proto,
2230 'offset=i' => \$cmdline_offset,
2231 'time-delay=i' => \$knock_sleep,
2232 'test-FKO-exists' => \$test_fko_exists,
2233 'last-cmd' => \$run_last_args,
2234 'no-save-args' => \$no_save_last_args,
2235 'no-FKO-module' => \$skip_fko_module,
2236 'Last-host=s' => \$run_last_host,
2237 'Show-last-cmd' => \$show_last_cmd,
2238 'Show-host-cmd=s' => \$show_last_host_cmd,
2239 'Resolve-external-IP' => \$resolve_external_ip,
2240 'whatismyip' => \$resolve_external_ip, # for backwards compatibility
2241 'URL=s' => \$resolve_ip_url,
2242 'User-agent=s' => \$ext_resolve_user_agent,
2243 'get-key=s' => \$get_key_file,
2244 'Home-dir=s' => \$cmdl_homedir,
2245 'Include-salted' => \$include_salted,
2246 'Include-equals' => \$include_base64_trailing_equals,
2247 'Include-gpg-prefix' => \$include_base64_gnupg_prefix,
2248 'Test-mode' => \$test_mode,
2249 'Lib-dir=s' => \$lib_dir,
2250 'LC_ALL=s' => \$locale,
2251 'locale=s' => \$locale,
2252 'no-LC_ALL' => \$no_locale,
2253 'no-locale' => \$no_locale,
2255 'verbose' => \$verbose,
2256 'Version' => \$print_version,
2257 'help' => \$print_help
2260 ### run a few minor checks against the supplied args
2261 &validate_command_line();
2263 ### if HTTP_proxy is specified, but not explicitly set, get it from the env variable
2264 if (defined $http_proxy and $http_proxy eq ''){
2265 $http_proxy = $ENV{'http_proxy'};
2271 sub run_last_cmdline() {
2275 for my $save_file ("$homedir/.fwknop.save", "$homedir/.fwknop.run") {
2276 next unless -e $save_file;
2279 open S, "< $save_file" or die "[*] Could not open $save_file: $!";
2284 if ($show_last_cmd) {
2285 print "[+] Last fwknop client command line: $arg_line\n";
2288 print "[+] Running with last command line args: $arg_line\n"
2290 @ARGV = split /\s+/, $arg_line;
2292 ### run GetOpt() to get command line args
2293 &handle_command_line();
2298 unless ($found_file) {
2299 die "[*] fwknop argument save files (~/.fwknop.save and ",
2300 "~/.fwknop.run) not found.";
2305 sub run_last_host_cmdline() {
2309 for my $save_file ("$homedir/.fwknop.save", "$homedir/.fwknop.hosts") {
2310 next unless -e $save_file;
2314 open H, "< $save_file" or die "[*] Could not open $save_file: $!";
2316 if (/-(k|D)\S*\s+$run_last_host/) {
2326 if ($show_last_host_cmd) {
2327 print "[+] Last command run for host: $show_last_host_cmd\n",
2331 print "[+] Running with last command line args: $arg_line\n"
2333 @ARGV = split /\s+/, $arg_line;
2335 ### run GetOpt() to get comand line args
2336 &handle_command_line();
2343 unless ($found_file) {
2344 die "[*] fwknop argument save files (~/.fwknop.save and ",
2345 "~/.fwknop.hosts) not found.";
2348 unless ($found_host) {
2349 print "[-] No matching destination host in ~/.fwknop.save ",
2350 "or ~/.fwknop.hosts\n";
2355 sub validate_access_str() {
2356 $access_str = lc($access_str);
2357 my @ports = split /,/, $access_str;
2358 for my $str (@ports) {
2359 unless ($str =~ m|(\D+)/(\d+)|) {
2360 die "[*] -A format is: <proto>/<port>,...,<proto>/<port>\n",
2361 " e.g.: tcp/22,udp/53,icmp/0";
2367 sub validate_NAT_access_str() {
2368 $NAT_access_str = lc($NAT_access_str);
2370 if ($NAT_rand_port) {
2372 unless ($selected_random_nat_port) {
2373 $NAT_access_str =~ s/,\d+$//;
2374 $NAT_access_str =~ s/:\d+$//;
2376 unless ($NAT_access_str =~ /^$ip_re$/) {
2377 die "[*] Must specify '<internal_IP>'";
2380 ### append a random destination port (between 10,000
2381 ### and 65535); this is the port number that will be
2382 ### used on the SSH command line
2383 $NAT_access_str .= ',' . &rand_port();
2387 unless ($NAT_access_str =~ /^$ip_re,\d+$/
2388 or $NAT_access_str =~ /^$ip_re:\d+$/) {
2389 die "[*] Must specify '<internal_IP>:<external_port>'";
2393 ### change ":" to "," for the fwknopd server (which uses colons
2394 ### to separate SPA packet fields, but colons are a better
2395 ### syntax for the fwknop command line)
2396 $NAT_access_str =~ s/:/,/;
2400 sub set_digest_type() {
2401 if ($cmdl_digest_alg =~ /sha256/i) {
2402 $digest_type = $SHA256_DIGEST;
2403 } elsif ($cmdl_digest_alg =~ /sha1/i) {
2404 $digest_type = $SHA1_DIGEST;
2405 } elsif ($cmdl_digest_alg =~ /md5/i) {
2406 $digest_type = $MD5_DIGEST;
2408 die "[*] --digest-alg can accept one of MD5, SHA1, or SHA256";
2417 if ($str =~ /(\d+)/) {
2420 die "[*] Must specify a value like 60min";
2422 if ($str =~ /min/i) {
2424 } elsif ($str =~ /hour/i) {
2426 } elsif ($str =~ /day/i) {
2427 $offset *= 60 * 60 * 24;
2428 } elsif ($str =~ /sec/i) {
2431 ### default to minutes
2440 my @chars = split //, $data;
2443 for my $char (@chars) {
2444 if ($ctr % 16 == 0) {
2445 print " $ascii_str\n" if $ascii_str;
2446 printf " 0x%.4x: ", $ctr;
2449 printf "%.2x", ord($char);
2451 if ((($ctr+1) % 2 == 0) and ($ctr % 16 != 0)) {
2455 if ($char =~ /[^\x20-\x7e]/) {
2458 $ascii_str .= $char;
2464 if ($ctr % 16 != 0) {
2465 $remainder = 16 - $ctr % 16;
2466 if ($remainder % 2 == 0) {
2467 $remainder = 2*$remainder + int($remainder/2) + 1;
2469 $remainder = 2*$remainder + int($remainder/2) + 2;
2472 print ' 'x$remainder, $ascii_str;
2479 return int(rand($max_port - $min_port)) + $min_port;
2482 sub validate_command_line() {
2483 die "[*] Cannot run in both --quiet and --verbose modes simultaneously"
2484 if $quiet and $verbose;
2486 die "[*] Must also specify a GnuPG signing key with --gpg-signing-key or\n",
2487 " use --gpg-default-key to use a default key (specified in\n",
2488 " ~/.gnupg/options with the default-key variable).\n"
2489 if ($gpg_recipient and (not $gpg_default_key and not $gpg_signing_key));
2491 die "[*] Must specify a GnuPG recipient key (on the fwknopd side) with\n",
2493 if (($gpg_default_key or $gpg_signing_key) and not $gpg_recipient);
2495 die "[*] Cannot spoof source address for a real TCP socket."
2496 if ($spoof_src and $spa_established_tcp);
2498 die "[*] Server auth method not supported in NAT access mode.\n"
2499 if $server_auth_method and $NAT_access_str;
2502 die "[*] $gpg_path does not exist." unless -e $gpg_path;
2503 die "[*] $gpg_path not executable." unless -x $gpg_path;
2506 if ($gpg_no_options) {
2507 print "[-] Options are disabled by default, so --gpg-no-options ",
2511 ### if $ENV{'http_proxy'} is to be used, $http_proxy will be '' at this point
2512 $spa_over_http = 1 if defined $http_proxy;
2518 my $exit_status = shift;
2521 fwknop; Single Packet Authorization client
2523 [+] Version: $version (file revision: $rev_num)
2524 By Michael Rash (mbr\@cipherdyne.org)
2525 URL: http://www.cipherdyne.org/fwknop/
2527 Usage: fwknop -A <port list> [-s|-R|-a] -D <spa_server> [options]
2530 -A, --Access <port list> - Provide a list of ports/protocols to open
2531 on the server. The format is
2532 "<proto>/<port>...<proto>/<port>". E.g.
2534 -D, --Destination <IP> - The IP address of the fwknopd server (the
2535 IP want to connect to).
2536 --last-cmd - Run the fwknop with the same command line
2537 arguments as in the previous invocation.
2538 The args are stored in ~/fwknop.run.
2539 --Last-host <host> - Run last command line arguments for <host>.
2540 --gpg-signing-key <key ID> - ID for key used to sign GnuPG encrypted
2541 message (e.g. "0xABCD1234").
2542 --gpg-recipient <recip> - Recipient of GnuPG encrypted message.
2543 --gpg-default-key - Use the key that GnuPG defines as the
2544 default (i.e. the key that is specified
2545 by the default-key option in
2547 --gpg-home-dir <dir> - Path to GnuPG home dir (e.g.
2549 --gpg-agent - Acquire GnuPG signing password from a
2551 --gpg-agent-info <info> - Specify the value for the GPG_AGENT_INFO
2552 environment variable as returned by
2553 'gpg-agent --daemon'.
2554 --gpg-verbose - Display all output from GnuPG process.
2555 --gpg-use-options - In GnuPG mode, instruct GnuPG to use the
2556 local ~/.gnupg/options file for config
2557 parameters (this is disabled by default).
2558 --gpg-prefix <bytes> - Change the bytes for the expected GnuPG
2559 prefix from $gpg_prefix to the specified string.
2560 --gpg-path <path> - Specify the path to the gpg command (not
2561 usually necessary if gpg is in your path).
2562 -a, --allow-IP <IP> - IP to instruct the remote fwknop server to
2563 allow through the firewall ruleset.
2564 -s, --source-IP - Inform the destination fwknop server to use
2565 the source address from which the SPA
2566 packet originates (useful for
2567 authenticating to the SPA server from
2568 behind a NAT device). Note that the -w
2569 option should really be used instead.
2570 -F, --Forward-access <NAT> - Access an internal server (say, SSH) by
2571 instructing the remote fwknopd instance to
2572 build inbound DNAT rules. The format of the
2573 argument is <InternalIP>,<Port> where
2574 InternalIP is the internal system and Port
2575 is the port number that will be forwarded.
2576 -R, --Resolve-external-IP - Resolve client IP via the
2577 http://www.whatismyip.org/ website. This is
2578 useful if fwknop is deployed on an internal
2579 -w, --whatismyip - (Synonym for --Resolve-external-IP option).
2580 --URL <external IP URL> - Specify a URL from which to determine the
2581 external IP (the default is
2582 http://www.whatismyip.org/).
2583 --User-agent <string> - Specify the user agent string to use when
2584 resolving IP via http://www.whatismyip.org
2585 (must use the -R option). The default user
2586 agent is: $ext_resolve_user_agent
2587 -f, --fw-timeout <seconds> - Specify the time the port will remain open
2588 on the server (requires
2589 PERMIT_CLIENT_TIMEOUT in access.conf on the
2590 fwknopd server side).
2591 --Include-salted - Include the encoded "Salted__" prefix; this
2592 is only necessary for older versions of the
2593 fwknopd server (< 1.9.2).
2594 --Include-equals - Include the trailing "=" chars used by
2595 base64 encoding scheme; this is only
2596 necessary for older versions of the fwknopd
2598 --Include-gpg-prefix - Include the base64-encoded $gpg_prefix prefix that
2599 GnuPG includes by default; this is only
2600 necessary for older versions of the fwknopd
2602 --Save-dst - Save the command line args for this
2603 invocation against the destination to the
2604 special file ~/.fwknop.save (this file
2605 provides a priority location that is only
2606 overwritten with --Save-dst and is useful
2607 for an fwknop client command that you want
2608 to always preserve).
2609 --Save-packet - Save a copy of an encrypted SPA packet to
2610 to a file (~/fwknop_save_packet.<pid> by
2612 --Save-packet-file <file> - Specify the path to the file where the
2613 encrypted SPA packet is stored when the
2614 --Save-packet argument is used.
2615 --Save-packet-append - Append a newly generated SPA packet to the
2616 --Save-packet-file instead of overwriting
2617 an existing file. This is useful for
2618 creating lots of SPA packets for testing
2619 randomness and encryption properties.
2620 --Source-port <port> - Fix a specific source port for outgoing SPA
2621 packets. This is not usually necessary,
2622 and the fwknop client randomizes its source
2624 --Server-port <port> - Specify the port number to which to send
2625 the single authentication packet (this is
2626 only used for an fwknop server that is
2627 operating in pcap mode).
2628 --Server-mode <mode> - Run in legacy port knocking mode ("mode" =
2629 "knock" or "shared").
2630 --Server-cmd <cmd> - Specify a complete command that an fwknop
2631 server should execute (as root).
2632 --Server-auth <method> - Provide additional authentication
2633 information that the fwknopd server can
2634 apply (such as integration with crypt()).
2635 --Spoof-src <IP> - Spoof the source IP address (requires
2636 fwknop to be run as root).
2637 --Spoof-user <username> - Supply a non-root username when spoofing
2639 --Spoof-proto <protocol> - Send authentication packet over the
2640 specified protocol (tcp, udp, or icmp)
2641 when spoofing the source address.
2642 --icmp-type <type> - Set the ICMP type when sending SPA packets
2643 over spoofed ICMP packets (default is
2644 $icmp_type for echo-request).
2645 --icmp-code <code> - Set the ICMP code when sending SPA packets
2646 over spoofed ICMP packets (default is
2647 $icmp_code for echo-request).
2648 -r, --rotate-proto - Rotate protocol (tcp and udp only) for
2649 encrypted sequences.
2650 --Max-packet-size <bytes> - Maximum size of outbound SPA packets - the
2651 default is $max_msg_len bytes.
2652 --offset <port> - Specify port offset to use when run in
2653 --encrypt knock mode. The default is
2655 --get-key <file> - Get encryption key from <file> instead of
2657 --Test-mode - Build SPA packet data but do not send it
2659 --time-offset-plus <str> - Add a time offset to the advertised time
2660 stamp in the SPA packet (e.g. "60sec" or
2662 --time-offset-minus <str> - Subtract a time offset from the advertised
2663 time stamp in the SPA packet (e.g. "60sec"
2665 --HTTP - Send SPA packets over HTTP (requires that
2666 the system running the fwknopd server is
2667 also running a webserver).
2668 --TCP-sock - Send SPA packets over an established TCP
2669 socket with the fwknopd server. This
2670 allows SPA packets to be sent over the Tor
2672 --no-save-args - Do not save command line args to
2674 --Show-last-cmd - Display the last fwknop command and exit.
2675 --Show-host-cmd <host> - Display the last fwknop command that was
2676 executed for <host> and exit.
2677 -u, --user-rc <rc-file> - Specify path to user connect rc file
2678 instead of using the default ~/.fwknoprc.
2679 This file is not referenced for encrypted
2680 port sequences; only for shared sequences.
2681 -H, --Home-dir <directory> - Specify the home directory of the current
2682 user that is running fwknop.
2683 --time-delay <seconds> - (Legacy port knocking mode) Introduce a
2684 time delay between each connection in a
2685 knock sequence. This is mainly used in
2686 conjunction with the MIN_TIME_DIFF access
2688 -k, --knock-dst <IP> - Connection destination IP address for port
2689 knock sequence (synonym for -D).
2690 -d, --debug - Run fwknop in debugging mode.
2691 --Lib-dir <path> - Path to the perl modules directory (not
2693 --locale <locale> - Manually define a locale setting.
2694 --no-locale - Don't set the locale to anything (the
2695 default is the "C" locale).
2696 --no-FKO-module - Revert to older perl implementation even if
2697 the FKO module is installed.
2698 --test-FKO-exists - See if the FKO module is available to use
2699 and exit (this is used by the fwknop test
2701 -v, --verbose - Verbose mode.
2702 -V, --Version - Display version and exit.
2703 -h, --help - Print help and exit.