#!/usr/bin/perl -X
use warnings;
use strict;
use Socket;
use Data::Dumper;
use Term::ANSIColor qw(:constants);	
use Getopt::Long;
use Net::OpenSSH;
use POSIX qw(strftime);
$| = 1;

my %options = (
	"--host=s"			=>	\my $argHost,
	"--login=s"			=>	\my $argLogin,
	"--password=s"		=>	\my $argPassword,
	"--portssh=s"		=>	\my $argPortSSH,

	"--leczenie"				=>	\my $argLeczenie,
	"--newporthttp|newPortHTTP=n"		=>	\my $argNewPortHTTP,
	"--newporthttps|newPortHTTPS=n"	=>	\my $argNewPortHTTPS,
	"--newportssh|newPortSSH=n"		=>	\my $argNewPortSSH,

	"--pingbin=s"		=>	\my $argPingBinary,
	"--hpingbin=s"		=>	\my $argHPingBinary,

	"--log"			=>	\my $argLogToFile,
	"--debug|d"			=>	\my $argDebug,
	"--help|h|?"		=>	\my $argHelp,
);
Getopt::Long::config("no_ignore_case");
GetOptions(%options);
$Net::OpenSSH::debug |= ($argDebug ? 16 : 0);

   if($argHelp) {
	print " Wykrywanie wirusa \".mf\" \@UBNT \n";
	print "  Wersja: 1.1.3 (2016-05-27 13:00)\n";
	print "  Autro: seszu (seba\@kazuko.pl)\n";
	print "  Strona: http://kazuko.pl\n";
	print "\n";
	print "\n";
	print " --host=\"\"\t\thosty (zakresy IP) oddzielone spacją (192.168.1.1 192.168.1.0/24 192.168.2.0/30)\n";
	print " --login=\"\"\t\tloginy oddzielone spacją (admin ubnt)\n";
	print " --password=\"\"\t\thasła oddzielone spacją (admin superhaslo 1234)\n";
	print " --portssh=\"\"\t\tporty SSH oddzielone spacją (22 1234 2200)\n";
	print "\n";
	print " --leczenie\t\tleczy ubnt z wirusa\n";
	print "\n";
	print " --newPortHTTP=\"\"\tzmień port HTTP na nowy (zalecane, jeśli ustawiony jest domyślny: 80)\n";
	print " --newPortHTTPS=\"\"\tzmień port HTTPS na nowy (zalecane, jeśli ustawiony jest domyślny: 443)\n";
	print " --newPortSSH=\"\"\tzmień port SSH na nowy (zalecane, jeśli ustawiony jest domyślny: 22)\n";
	print "\n";
	print " --pingbin=\"\"\t\tścieżka do pinga (domyślnie /bin/ping)\n";
	print " --hpingbin=\"\"\t\tścieżka do hpinga (domyślnie /usr/sbin/hping3)\n";
	print "\n";
	print " --log\t\t\tzrzuca log do pliku, będącego w katalogu w którym jest skrypt\n";
	print " --help\t\t\tdrukuje pomoc\n";
	print " --debug\t\tdebuguje połączenia ssh\n";
	print "\n";
	exit 0;
   }

   if(!$argHost or !$argLogin or !$argPassword or !$argPortSSH) {
	print " Wykrywanie wirusa \".mf\" \@UBNT \n";
	print "\n";
	print " przykład:\n";
	print " perl $0 --host=\"A.A.A.A/Y B.B.B.B/Y\" --login=\"ubnt admin\" --password=\"ubnt haslo\" --portssh=\"22 2200\"\n";
	print "\n";
	print " --help\t\twięcej...\n";
	print "\n";
	exit 1;
   }

my $path = $0;
my @pathARR = split(/\//, $path);
my $ile = @pathARR;
$path =~ s/@pathARR[$ile-1]//;


my $argPingBinary = $argPingBinary || "/bin/ping";
my $argHPingBinary = $argHPingBinary || "/usr/sbin/hping3";


   if(!(-f $argPingBinary) and $argPingBinary) {
	print "\n   "; print RED, "blad! (", RESET; print "plik $argPingBinary nie istnieje"; print RED, ")\n\n", RESET; print BOLD RED, "\n\tSTOP SCRIPT!\n\n", RESET;
	exit 0;

   } elsif(!(-r $argPingBinary) and $argPingBinary) {
	print "\n   "; print RED, "blad! (", RESET; print "brak dostępu do $argPingBinary"; print RED, ")\n\n", RESET; print BOLD RED, "\n\tSTOP SCRIPT!\n\n", RESET;
	exit 0;
   }


my @HOSTs = ();
my @arrHost = split(/\s/, $argHost);
   foreach my $host (@arrHost) {
	   if($host =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/) {
		my @siec = split(/\//, $host);
		my $cidr = @siec[1];
		my $ileIP = ((2**(32-$cidr))-2);
		my $adressLong = ip2long(@siec[0]);

		my $n;
		   for ($n = 1; $n <= $ileIP; $n++) {
			push(@HOSTs, long2ip($adressLong+$n));
		   }
	   } else {
		push(@HOSTs, $host);
	   }
   }


my @LOGINs = split(/\s/, $argLogin);
my @PASSWORDs = split(/\s/, $argPassword);
my @PORTSSHs = split(/\s/, $argPortSSH);


my $knownHosts = "~/.ssh/known_hosts_test";
   if(-f $knownHosts) { unlink($knownHosts); }
my $logFile = "$path/log.".strftime('%Y%m%d%H%M',localtime).".txt";


   foreach my $host (@HOSTs) {
	print BOLD WHITE, $host, RESET;
	print "\t";

	my $ping = ping($argPingBinary, "3", $host);
	   if($ping eq "U" or !$ping) {
		print RED, "host nieosiagalny", RESET; 
		print "\n";
		   if($argLogToFile) { plikDopisz($logFile, $host."\thost nieosiagalny\n"); }
		next;
	   }


	my $portSSHOK;
	   foreach my $portSSH (@PORTSSHs) {
		my $HpingSSH = qx($argHPingBinary -S -c 3 -p $portSSH $host 2> /dev/null);
		   if($HpingSSH =~ /flags\=RA/ or !($HpingSSH =~ /flags\=SA/)) {
			print RED, $portSSH, RESET; 
			print "\t";

		   } else {
			print GREEN, $portSSH, RESET; 
			print "\t";
			$portSSHOK = $portSSH;
		   }
	   }
	   if(!$portSSHOK) {
		print RED, "zamkniete wszystkie porty", RESET; 
		print "\n";
		   if($argLogToFile) { plikDopisz($logFile, $host."\tzamkniete wszystkie porty\n"); }
		next;
	   }


	my $ssh;
	my $loginOK;
	my $passwordOK;
	   foreach my $login (@LOGINs) {
		   foreach my $password (@PASSWORDs) {
			$ssh = Net::OpenSSH->new($host, user => $login, password => $password, port => $portSSHOK, timeout => 30, kill_ssh_on_timeout => 30, master_stderr_discard => 1, master_opts => [-o => "UserKnownHostsFile=$knownHosts", -o => "StrictHostKeyChecking=no"]);
			   if(!$ssh->error) {
				$loginOK = $login;
				$passwordOK = $password;
				last;
			   }
		   }
		   if(!$ssh->error) { last; }
	   }


	   if($ssh->error) {
		print RED, "cos nie tak z loginami/haslami", RESET; print "\n";
		   if($argLogToFile) { plikDopisz($logFile, $host."\tcos nie tak z loginami/haslami\n"); }
		next;

	   } else {
		my $log = $host;
		my %MCAStatus;
		my ($conf, $err) = trim($ssh->capture2("cat /etc/board.info && cat /tmp/system.cfg"));

		   foreach my $linia (split(/\n/, $conf)) { if($linia) {
			   if($linia =~ /deviceName\=/) {
				   foreach my $linia2 (split(/\,/, $linia)) { if($linia2) {
					my ($key, $val) = split(/\=/, trim($linia2));
					$MCAStatus{trim($key)} = trim($val);
				   } }
			   } else {
				my ($key, $val) = split(/\=/, trim($linia));
				$MCAStatus{trim($key)} = trim($val);
			   }
		   } }

		   if(!$MCAStatus{"board.name"}) {
			print WHITE, "to nie ubiquiti", RESET; print "\n";
			   if($argLogToFile) { plikDopisz($logFile, $host."to nie ubiquiti\n"); }
			next;

		   } else {

			my $zainfekowany = 0;
			my $ports = 0;
			my $save = 0;
			my ($ls, $err) = trim($ssh->capture2("ls -a /etc/persistent"));
			   foreach my $ff (split(/\s/, $ls)) {
				   if($ff =~ /.*\.mf.*/) {
					$zainfekowany = 1;
					last;
				   }
			   }
			my ($ssid, $err) = trim($ssh->capture2("cat /tmp/system.cfg | grep \"wireless.1.ssid=motherfucker\""));

			   if($zainfekowany) {
				print BOLD RED, "ZAINFEKOWANY !!!", RESET; 
				print " ";
				   if($ssid) { print BOLD RED, "SSID: motherfucker !!!", RESET; print " "; }
				   if($argLogToFile) { $log .="\tZAINFEKOWANY !!!"; }

				   if($argLeczenie) {
					print BOLD GREEN, "Leczenie...", RESET;
					print " ";

					my ($run, $err) = trim($ssh->capture2("iptables -D INPUT -p tcp --dport 80 -j DROP 2>/dev/null"));
					my ($run, $err) = trim($ssh->capture2("iptables -D INPUT -p tcp -i lo --dport 443 -j DROP 2>/dev/null"));
					my ($run, $err) = trim($ssh->capture2("for i in \$(ps | grep /etc/persistent/.mf | grep -v grep | awk '{print \$1}'); do kill -9 \$i; done"));
					my ($run, $err) = trim($ssh->capture2("for i in \$(ps | grep sleep | grep -v grep | awk '{print \$1}'); do kill -9 \$i; done"));
					my ($run, $err) = trim($ssh->capture2("for i in \$(ps | grep reboot | grep -v grep | awk '{print \$1}'); do kill -9 \$i; done"));
					my ($run, $err) = trim($ssh->capture2("for i in \$(ps | grep curl | grep -v grep | awk '{print \$1}'); do kill -9 \$i; done"));
					my ($run, $err) = trim($ssh->capture2("rm -f /etc/persistent/mf.tar"));
					my ($run, $err) = trim($ssh->capture2("rm -rf /etc/persistent/.mf"));
					my ($run, $err) = trim($ssh->capture2("sed -i 's/mother.*//' /etc/passwd"));
					my ($run, $err) = trim($ssh->capture2("sed -i 's/.*\.mf.*//' /etc/persistent/rc.poststart"));
					my ($run, $err) = trim($ssh->capture2("sed -i 's/.*mother.*//' /etc/dropbear/authorized_keys"));

					$save = 1;
				   }

			   } else {
				print BOLD GREEN, "ZDROWY", RESET; 
				print " ";
				   if($ssid) { print BOLD RED, "SSID: motherfucker !!!", RESET; print " "; }
				   if($argLogToFile) { $log .="\tZDROWY"; }
			   }

			   if(($argNewPortHTTP and $argNewPortHTTP ne $MCAStatus{"httpd.port"})  or ($argNewPortHTTPS and $argNewPortHTTPS ne $MCAStatus{"httpd.https.port"}) or ($argNewPortSSH and $argNewPortSSH ne $portSSHOK)) {
				print BOLD GREEN, "Zmiana portu/ow...", RESET;
				my $portsx;

				   if($argNewPortHTTP) {
					   if($MCAStatus{"httpd.port"}) {
						my ($run, $err) = trim($ssh->capture2("sed -i 's/httpd.port=.*/httpd.port=$argNewPortHTTP/' /tmp/system.cfg"));

					   } else {
						my ($run, $err) = trim($ssh->capture2("echo \"httpd.port=$argNewPortHTTP\" >> /tmp/system.cfg"));
					   }
					$portsx .= " http ";
				   }
				   if($argNewPortHTTPS) {
					   if($MCAStatus{"httpd.https.port"}) {
						my ($run, $err) = trim($ssh->capture2("sed -i 's/httpd.https.port=.*/httpd.https.port=$argNewPortHTTPS/' /tmp/system.cfg"));

					   } else {
						my ($run, $err) = trim($ssh->capture2("echo \"httpd.https.port=$argNewPortHTTPS\" >> /tmp/system.cfg"));
					   }
					$portsx .= " https ";
				   }
				   if($argNewPortSSH) {
					   if($MCAStatus{"sshd.port"}) {
						my ($run, $err) = trim($ssh->capture2("sed -i 's/sshd.port=.*/sshd.port=$argNewPortSSH/' /tmp/system.cfg"));

					   } else {
						my ($run, $err) = trim($ssh->capture2("echo \"sshd.port=$argNewPortSSH\" >> /tmp/system.cfg"));
					   }
					$portsx .= " ssh ";
				   }

				my ($dmzID, $err) = trim($ssh->capture2("cat /tmp/system.cfg | grep \"iptables.sys.dmz\" | cut -d '.' -f 6"));
				   if($dmzID) {
					   if($argNewPortHTTP) {
						   if($MCAStatus{"iptables.sys.dmz.1.except.4.port"}) {
							my ($run, $err) = trim($ssh->capture2("sed -i 's/iptables.sys.dmz.1.except.4.port=.*/iptables.sys.dmz.1.except.4.port=$argNewPortHTTP/' /tmp/system.cfg"));

						   } else {
							my ($run, $err) = trim($ssh->capture2("echo \"iptables.sys.dmz.1.except.4.port=$argNewPortHTTP\" >> /tmp/system.cfg"));
						   }
					   }
					   if($argNewPortHTTPS) {
						   if($MCAStatus{"iptables.sys.dmz.1.except.6.port"}) {
							my ($run, $err) = trim($ssh->capture2("sed -i 's/iptables.sys.dmz.1.except.6.port=.*/iptables.sys.dmz.1.except.6.port=$argNewPortHTTPS/' /tmp/system.cfg"));

						   } else {
							my ($run, $err) = trim($ssh->capture2("echo \"iptables.sys.dmz.1.except.6.port=$argNewPortHTTPS\" >> /tmp/system.cfg"));
						   }
					   }
					   if($argNewPortSSH) {
						   if($MCAStatus{"iptables.sys.dmz.1.except.5.port"}) {
							my ($run, $err) = trim($ssh->capture2("sed -i 's/iptables.sys.dmz.1.except.5.port=.*/iptables.sys.dmz.1.except.5.port=$argNewPortSSH/' /tmp/system.cfg"));

						   } else {
							my ($run, $err) = trim($ssh->capture2("echo \"iptables.sys.dmz.1.except.5.port=$argNewPortSSH\" >> /tmp/system.cfg"));
						   }
					   }
				   }
				print " (".trim($portsx).") ";

				$save = 1;
				$ports = 1;
			   }




			   if(!$save) {
				print "\n";
				   if($argLogToFile) { plikDopisz($logFile, $log."\n"); }

			   } else {
				print BOLD WHITE, "Save...", RESET;	
				print " ";
				my ($run, $err) = trim($ssh->capture2("cfgmtd -f /tmp/system.cfg -w"));
				my ($run, $err) = trim($ssh->capture2("cfgmtd -w -p /etc/"));

				print BOLD WHITE, "Restart.", RESET;
				my ($run, $err) = trim($ssh->capture2("reboot"));

				my $i=0;
				   for($i = 0; $i < 6; $i++) {
					sleep 20;
					my $ping = ping($argPingBinary, "3", $host);
					   if($ping eq "U" or !$ping) {
						print BOLD WHITE, ".", RESET;

					   } else {
						last;
					   }
				   }

				$ssh = Net::OpenSSH->new($host, user => $loginOK, password => $passwordOK, port => ($argNewPortSSH ? $argNewPortSSH : $portSSHOK), timeout => 30, kill_ssh_on_timeout => 30, master_stderr_discard => 1, master_opts => [-o => "UserKnownHostsFile=$knownHosts", -o => "StrictHostKeyChecking=no"]);
				   if($ssh->error) {
					print RED, "\tProblem z ponownym logowaniem !!!", RESET; 
					print "\n";
					   if($argLogToFile) { plikDopisz($logFile, $log."\tProblem z ponownym logowaniem !!!\n"); }

				   } else {
					my %MCAStatus;
					my ($conf, $err) = trim($ssh->capture2("cat /etc/board.info && cat /tmp/system.cfg"));

					   foreach my $linia (split(/\n/, $conf)) { if($linia) {
						   if($linia =~ /deviceName\=/) {
							   foreach my $linia2 (split(/\,/, $linia)) { if($linia2) {
								my ($key, $val) = split(/\=/, trim($linia2));
								$MCAStatus{trim($key)} = trim($val);
							   } }
						   } else {
							my ($key, $val) = split(/\=/, trim($linia));
							$MCAStatus{trim($key)} = trim($val);
						   }
					   } }


					   if($zainfekowany) {
						$zainfekowany = 0;
						my ($ls, $err) = trim($ssh->capture2("ls -a /etc/persistent"));
						   foreach my $ff (split(/\s/, $ls)) {
							   if($ff =~ /.*\.mf.*/) {
								$zainfekowany = 1;
								last;
							   }
						   }
						   if(!$zainfekowany) {
							print BOLD GREEN, "\tWYLECZONY", RESET; 
							print " ";
							   if($argLogToFile) { $log .= "\tWYLECZONY"; }

						   } else {
							print BOLD RED, "\tNIEWYLECZONY !!!", RESET; 
							print " ";
							   if($argLogToFile) { $log .= "\tNIEWYLECZONY !!!"; }
						   }
					   }

					   if(($argNewPortHTTP or $argNewPortHTTPS or $argNewPortSSH) and $ports) {
						print GREEN, "\tPort/ty zmienione", RESET; 
						print "\n";
						   if($argLogToFile) { plikDopisz($logFile, $log."\tPort/ty zmienione\n"); }

					   } else {
						print "\n";
						   if($argLogToFile) { plikDopisz($logFile, $log."\n"); }
					   }

				   }
			   }
		   }
	   }
   }







sub trim($) {
 my $string = shift;
 $string =~ s/^\s+//;
 $string =~ s/\s+$//;
 return $string;
}

sub ip2long {
 return unpack('N', inet_aton(shift));
}

sub long2ip {
 return inet_ntoa(pack("N*", shift));
}




sub ping {
 my($pingBinary, $pingCount, $host) = @_; 
 my $ping = 0;

   open(PROCESS, "$pingBinary -c $pingCount -w 1 $host 2>> /dev/null | grep 'icmp_[s|r]eq' | grep time |");
	$ping = <PROCESS>;
   close(PROCESS);
 $ping =~ m/(.*time=)(.*) (ms|usec)/;

   if($2 == "") { $ping = "U"; } elsif($3 eq "usec") { $ping = $2/1000; } else { $ping = $2; }

 return $ping; 
}




sub plikZapisz { 
 my($plik, $co) = @_; 
  open(UCHWYT, '>', $plik) or die "Nie można otworzyć $plik: $!"; 
  print UCHWYT $co; 
  close UCHWYT;
}

sub plikDopisz { 
 my($plik, $co, $gdzie) = @_; 
   # dopisanie u gory
   if($gdzie eq "up") {
	my $odczyt = join("\n", plikCzytaj($plik));
	plikZapisz($plik, "$co\n".$odczyt);

   # dopisanie u dolu
   } else {
	open(UCHWYT, '>>', $plik) or die "Nie można otworzyć $plik: $!"; 
	print UCHWYT $co; 
	close UCHWYT; 
   }
}

