diff --git a/exercises/h6.md b/exercises/h6.md index 92682b1..f151e51 100644 --- a/exercises/h6.md +++ b/exercises/h6.md @@ -135,7 +135,7 @@ Hello World! **Answer:** -This could have been very interesting assignment to work out. Unfortunately, my working laptop was broken so that I was not effectively able to write new code. I have worked with a perl program which generated random port and TCP/UDP sequences for knockd daemon. In addition, I have worked with some updates to Wine Configuration window (winecfg, part of Wine program) in C language: +This could have been very interesting assignment to work out. Unfortunately, my working laptop was broken so that I was not effectively able to write new code. I have worked with [a perl program which generates random port and TCP/UDP sequences for knockd daemon](../other/random_port.pl). In addition, I have worked with some updates to Wine Configuration window (winecfg, part of Wine program) in C language: ![winecfg-update](https://i.imgur.com/SihmPUA.jpg) diff --git a/other/random_port.pl b/other/random_port.pl new file mode 100644 index 0000000..6ff3810 --- /dev/null +++ b/other/random_port.pl @@ -0,0 +1,385 @@ +#!/usr/bin/perl +# +# knockgen 0.1 - Random port & protocol sequence generator for knockd daemon +# Copyright (C) 2018 Pekka Helenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +#----------------------------------------- +# PROGRAM DESCRIPTION + +# This perl program generates a random port & protocol sequence for knockd daemon. +# This generated sequence can be used to knock ports of the target machine (usually a SSH server behind a firewall). +# The generated data must be equal for the server (knockd daemon computer) and your client (ssh/knock client computer). + +#----------------------------------------- +# ENVIRONMENT + +use strict; +use warnings; + +# For CLI input parameters +use Getopt::Long; + +# For help text +use Pod::Usage; + +# For IP regular expressions +# Requires 'perl-regexp-common' package (e.g. Arch Linux package database) +use Regexp::Common qw/net number/; + +# Requires 'perl-io-socket-inet6' package (e.g. Arch Linux package database) +use IO::Socket; + +# For pinging target hosts +use Net::Ping; + + +# TODO output file (Default: /etc/knockd.conf) + # Check if the file exists + # Do same checks than in the bash script + +# Ask user for these +# TODO knockd daemon: Configuration file? [default: /etc/knockd.conf ] +# TODO knockd daemon: Network interface for daemon? [default: eth0 ] +# TODO knockd daemon: Time limit for port knocking in seconds? [default: 10 ] +# TODO knockd daemon: Ports to be opened after knocking? [default: 22 ] +# TODO knockd daemon: Open port for specified IP or any client? [default: any ] +# TODO knockd daemon: How long to keep the port opened in seconds? [default: 15 ] +# TODO knockd daemon: TCP Flags? [default: syn ] +# TODO knockd daemon: Use log file /var/log/knockd.log? [default: n] + +# TODO If previous knockd configuration detected (get creation date of it), warn sysadmin about it +# TODO add commented date tag to generated knockd.conf file (for sysadmins) +# TODO Ask user if the generated port sequence pattern is ok or generate a new one +# TODO if output/override etc parameter is given with valid input, use it instead of generating a new one + +# TODO detect old SSH configurations from /etc/iptables/*.rules files. Warn user about them and delete if permission granted +# Do this by detecting the the ports which should be opened by knockd + +# TODO support for multiple port openings. Can we do this just by adding a new port to IPTABLES rule or do we have to generate a new +# rule for each port? + +#----------------------------------------- +# DEFAULT VALUES +# +# Port test before generating values for knockd. +# We need testing because we want to avoid any ports which may be listened/used by a running server daemon. +# +my $default_target_ip = "127.0.0.1"; # IPv4 address of the target host computer. Default: 127.0.0.1 +my $connection_timeout = 0.3; # Connection timeout in seconds. Default: 0.3 +my $connection_timeout_minlimit = 0.2; # This is the minimum time out limit for connection attempts +my $connection_timeout_maxlimit = 5.0; # This is the maximum time out limit for connection attempts + +# Knockd specific values +my $knockd_protocols = "tcp,udp"; # Protocols to be used. Only tcp or udp is accepted. +my $knockd_port_count = 6; # How many port and protocol combinations we generate for knockd input. +my $knockd_port_count_limit = 30; # This is the maximum amount of ports accepted to output. +my ($knockd_min_port, $knockd_max_port) = (1, 65535); # Scanned port range. Default: 1, 65535 + +# TODO override port pattern +# TODO override port + protocol pattern + +# TODO parameter: output pattern only, no knockd questions described above + +# TODO knockd_protocols: check for input values (must be either tcp or udp, max values (array length) is 2" +# TODO knockd_port_count: set minimum limit to 2 + +# TODO check for iptables Default policy, too (does it reject all traffic etc) +#/usr/bin/iptables -I INPUT -p tcp -dport 5461 -j ACCEPT +#/usr/bin/iptables -D INPUT -p tcp -dport 5461 -j ACCEPT + +#----------------------------------------- +# USER INPUT PARAMETERS + +my %opts = ("all" => 0, + "ports-only" => 0, + "target-ip" => $default_target_ip, + "dry-gen" => 0, + "random-always" => 0); + +GetOptions ("a|all" => \$opts{"all"}, + "p|ports-only" => \$opts{"ports-only"}, + "i|target-ip=s" => \$opts{"target-ip"}, + "d|dry-gen" => \$opts{"dry-gen"}, + "r|random-always" => \$opts{"random-always"}, + "t|conn-timeout=f" => \$connection_timeout, + "c|protocols=s" => \$knockd_protocols, + "m|min-port=i" => \$knockd_min_port, + "x|max-port=i" => \$knockd_max_port, + "n|port-count=i" => \$knockd_port_count, + "h|help" => sub { pod2usage(1) }) +or pod2usage(2); + +my $all = $opts{"all"}; +my $ports_only = $opts{"ports-only"}; +my $target_ip = $opts{"target-ip"}; +my $dry_gen = $opts{"dry-gen"}; +my $random_always = $opts{"random-always"}; + +#----------------------------------------- +# ERROR HANDLING + +# We don't accept regular PERL arguments here. We take arguments only from GetOptions +# Perl arguments are handled differently, we don't want them. +if (@ARGV) +{ + print "Unknown option: @ARGV\n"; + pod2usage(2); +} + +#-------------------- +# If 'target_ip' is default value and 'dry-gen' is set, clear the IP value. +if ($target_ip eq $default_target_ip and $dry_gen) { + print "WARNING: Not testing whether generated ports are being used or not.\n"; + undef $target_ip; +} + +#-------------------- +# If both 'dry-gen' and 'target_ip' is set, terminate. +if ($target_ip and $dry_gen) { + die "ERROR: Define either target IPv4 address or 'dry-gen' parameter.\n"; +} + +#-------------------- +# If neither 'ports-only' or 'all' hash is set, use default 'all' value. +if (not $ports_only and not $all) { + $all = 1; +} + +#-------------------- +# If both 'ports-only' and 'all' is set, terminate. +if ($ports_only and $all) { + die "ERROR: Define either 'ports-only' or 'all' (default is 'all').\n"; +} + +#-------------------- +# If more than allowed ports have been instructed. +if ($knockd_port_count > $knockd_port_count_limit) { + die "ERROR: Maximum number of ports is " . $knockd_port_count_limit . ". You have defined " . $knockd_port_count . " ports.\n" +} + +#-------------------- +# Check for invalid port values (min & max) +my $port_fault = ""; +if (($knockd_min_port < 1) or ($knockd_min_port > 65535)) +{ + print "ERROR: Minimum port value is not in valid range 1-65535 (Value: " . $knockd_min_port . ")\n"; + $port_fault = "true"; +} + +if (($knockd_max_port < 1) or ($knockd_max_port > 65535)) +{ + print "ERROR: Maximum port value is not in valid range 1-65535 (Value: " . $knockd_max_port . ")\n"; + $port_fault = "true"; +} + +# If faulty port values, exit the program +# We define the exit procedure separately because we want to print all previous port-related error messages +if ( $port_fault eq "true" ) { exit } + +#-------------------- +# Inform user that the minimum port value exceeds maximum port value. +if ($knockd_min_port > $knockd_max_port) +{ + die "ERROR: Minimum port value is set above maximum port value.\n"; +} + +#-------------------- +# Inform user that more random ports must be available in the given pool. +if (($knockd_max_port - $knockd_min_port) < $knockd_port_count) +{ + die "ERROR: You must have more ports for randomizable port sequence (Current port pool size: " . ($knockd_max_port - $knockd_min_port) . ")\n"; +} + +#-------------------- +# Minimum connection time out value can't be less than the limit value defines. +if ($connection_timeout < $connection_timeout_minlimit) +{ + die "ERROR: Connection time out can't be set below " . $connection_timeout_minlimit . " seconds.\n"; +} + +# Maximum connection time out value can't exceed the maximum limit value. +if ($connection_timeout > $connection_timeout_maxlimit) +{ + print "WARNING: Connection time out limit is set above recommended limit (" . $connection_timeout_maxlimit . " seconds).\n"; +} + +#-------------------- +# If user is not having dry-gen parameter set... +if (not $dry_gen) { + + # ...check for valid IP address syntax. + if (not $target_ip =~ /^$RE{net}{IPv4}$/ or $target_ip eq "localhost") { + die "ERROR: Invalid IPv4 address given (" . $target_ip . ")\n" + } + + # ... check that we can establish a connection to the target IP address. + my $p = Net::Ping->new; + if (not $p->ping($target_ip, $connection_timeout_maxlimit)) { + die "ERROR: Can't find the host computer.\n" + } +} + +#----------------------------------------- +# PORT & PROTOCOL GENERATION + +# Split user input into a new array, defined by @ symbol. +my @knockd_protocols = split /,/, $knockd_protocols; + +# Set port range. +my @knockd_port_range = ($knockd_min_port .. $knockd_max_port); + +# Declare a new array for generated ports (+ protocols). +# Fill with zeros, size is value of $knockd_port_seqs. +my @randoms = (0) x $knockd_port_count; + +my $i = 0; +while ($knockd_port_count > 0) +{ + + while () + { + + # Generate a random port from given port range. + my $random_port = $knockd_port_range[rand @knockd_port_range]; + + # Choose randomly between protocols tcp & udp. + my $random_protocol = $knockd_protocols[rand @knockd_protocols]; + + # TODO make this optional with '$always_random' user input parameter + # Never accept already generated port in port sequence. + if (not grep {$_ =~ /$random_port/} @randoms) + { + + # Initialize socket value + my $socket = 0; + + if (not $dry_gen) { + + # Test the generated port for a connection. + # Protocol must always be else than udp because udp doesn't give + # a response whether the connection was successful or not. + # This is in the procotol specification. + # + # Therefore, udp would fail the connection test we establish here + # + $socket = IO::Socket::INET->new(PeerHost => $target_ip, + PeerPort => $random_port, + Proto => "tcp", #This is hardcoded purposefully + Timeout => $connection_timeout); + + } + else + { + undef $socket; + } + + # If socket dest is not used + if (not $socket) + { + if ( $all ) { + $randoms[$i] = $random_port . ":" . $random_protocol; + } + else + { + $randoms[$i] = $random_port; + } + last; + } + } + } + $knockd_port_count--; + $i++; +} + +my $knockd_seqs = join(',', @randoms); +print $knockd_seqs . "\n"; + +#----------------------------------------- +# HELP TEXT + +__END__ + +# TODO IMPROVE THIS SECTION +# TODO UPDATE HELP TEXT TO CORRESPOND WITH THE COMMANDS ABOVE + +=head1 SYNOPSIS + +knockgen [options] (--help or -h for more information) + +knockgen 0.1 - Random port & protocol sequence generator for knockd daemon + + + knockgen Copyright (C) 2018 Pekka Helenius + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + +=head1 OPTIONS + +=item A<------------------------------> + +=item A + +=item B<--all> or B<-a> + +Print randomly generated ports & protocols (Default) + +=item B<--ports-only> or B<-p> + +Print randomly generated ports only + +=item B<--dry-gen> or B<-d> + +Generate port (& protocol) sequence without trying to connect anywhere. + +=item A<------------------------------> + +=item A + +=item B<--target-pc> or B<-i> + +IPv4 address of the target computer (server). Default: 127.0.0.1 + +=item B<--protocols> or B<-c> + +Use these protocols. Default: tcp,udp (Syntax: tcp or tcp,udp) + +=item B<--conn-timeout> or B<-t> + +Connection timeout for a port test in seconds. Default: 0.3 + +=item A<------------------------------> + +=item A + +=item B<--min-port> or B<-m> + +Minimum port number to be used for knockd daemon. Default: 1 + +=item B<--max-port> or B<-x> + +Maximum port number to be used for knockd daemon. Default: 65535 + +=item B<--port-seqs> or B<-n> + +Number of port (+ protocol) sequences for knockd daemon. Default: 6 + +=item B<--help> or B<-h> + +Prints this help text. + +=cut