Tag: code

  • Shellshock scanner

    So I had a bunch of machines with a standard naming convention that I needed to scan for the Shellshock bug. Since I just needed to run a command on each one and check the output, and I had SSH access, it seemed easy enough to put together a quick script to manage the process.

    Here’s a skeleton of that script, with the details on what machines I was logging into elided. This does a pretty reasonable job, checking 300 machines in about a minute. You need to have a more recent copy of Parallel::ForkManager, as versions prior to 1.0 don’t have the  ability to return a data structure from the child.

    $|++;
    use strict;
    use warnings;
    use Parallel::ForkManager 1.07;
    
    my $MAX_PROCESSES = 25;
    my $pm = Parallel::ForkManager->new($MAX_PROCESSES);
    my @servers = @SERVER_NAMES;
    my %statuses;
    my @diagnostics;
    $pm-> run_on_finish (
        sub {
            my($pid, $exit_code, $ident, $exit_signal, $core_dump,
               $data_structure_reference) = @_;
            if (defined($data_structure_reference)) { 
                my ($host_id, $status, $results) = @{$data_structure_reference};
                if ($status eq 'Unknown') {
                    push @diagnostics, $host_id, $results;
                } else {
                    push @{ $statuses{$status} }, $host_id;
                }
            } else { 
                warn qq|No message received from child process $pid!\n|;
            }
        }
    );
    
    print "Testing servers: ";
    for my $host_id (@servers) {
        my $pid = $pm->start and next;
        my $result = << `EOF`;
    ssh -o StrictHostKeyChecking=no $host_id <<'ENDSSH' 2>&1
    env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
    ENDSSH
    EOF
        my $status;
        if ($result =~ /Permission denied/is) {
           $status = q{Inacessible};
        } elsif ($result =~ /key verification failed/s) {
           $status = q{Key changed};
        } elsif ($result =~ /timed out/is) {
           $status = q{Timed out};
        } elsif ($result =~ /vulnerable/s) {
               $status = q{Vulnerable};
        } elsif ($result =~ /ignoring function definition attempt/s) {
           $status = q{Patched};
        } elsif ($result =~ /Name or service not known/s) {
           $status = q{Nonexistent};
        } else {
           $status = q{Unknown}
        }
        print "$host_id, ";
        $pm->finish(0, [$host_id, $status, $result]);
    }
    $pm->wait_all_children;
    print "done!\n";
    for my $status (keys %statuses) {
        print "$status: ",join(',', @{$statuses{$status}}), "\n";
    }
    print "The following hosts returned an undiagnosed status:",
          join("\n", @diagnostics), "\n";

    Note that this doesn’t test the most recent version (#3) of the bug; I have modified it slightly to test for that, but that’s a reasonable exercise for the reader.