# Simple Web Service Monitoring using Perl # Proof of concept/sample code for # - DNS Hijacking # - Web page spoofing # - Web page uptime and response time # - Web application outage monitoring # Load required modules use Net::DNS; # Used for DNS lookup use LWP::UserAgent; # Used for Web Page Query use Crypt::SSLeay; # Used for SSL Queries use File::Compare; # Used to compare website output with prior versions use Net::SMTP::OneLiner; # Used to easily send a basic email alert use Time::HiRes qw(gettimeofday tv_interval); # Used to calculate reponse times in milliseconds # Define Variables $Net::SMTP::OneLiner::HOSTNAME = 'localhost'; $logfile = "DefacementMonitorLog.txt"; $pageToMonitor = 'https://www.demo.com'; $serverName = 'www.demo.com'; $monitorDescription = 'Demo Page'; $expectedIPAddress = '192.168.10.1'; $pageContentFolder = "PagesDownloaded"; # set a High Resolution timer $t0 = [gettimeofday]; # Lookup DNS, Error if incorrect output my $resolver = Net::DNS::Resolver->new; my $query = $resolver->search($serverName, "A"); if ($query) { foreach $responserecord ($query->answer) { $returnedAddress = $responserecord->address; if ($returnedAddress ne $expectedIPAddress) { send_mail($mailfrom, $mailto, $mailsubj, "Error: DNS Address $returnedAddress does not match $expectedIPAddress" ); SaveToFile($logfile, &FormattedDateString . "\tError: DNS Address $returnedAddress does not match $expectedIPAddress\n"); } else { $responseTime = tv_interval ($t0, [gettimeofday]); SaveToFile($logfile, &FormattedDateString . "\tDNS Response Correct: $returnedAddress in $responseTime milliseconds\n"); } } } else { # DNS lookup failed SaveToFile($logfile, &FormattedDateString . "\tError: DNS Services for $serverName appear to be unavailable\n"); } # Load SSL web page, save response to file $thisResponseFilename = $monitorDescription . "." . &FormattedDateString; $thisResponseFilepath = $pageContentFolder . "\\" . $thisResponseFilename; # Figure out the filename of the last opendir CONTENTDIR, "$pageContentFolder" or die $!; @folders = sort readdir CONTENTDIR; # since they are sorted and named with a datestamp, the last should be the newest $lastResponseFilename = $folders[$#folders]; $lastResponseFilepath = $pageContentFolder . "\\" . $lastResponseFilename; my $ua = new LWP::UserAgent; my $req = new HTTP::Request('GET', $pageToMonitor); my $res = $ua->request($req); # check the outcome if ($res->is_success) { SaveToFile($pageContentFolder . "\\" . $thisResponseFilename, $res->content); if (compare($thisResponseFilepath,$lastResponseFilepath) == 0) { SaveToFile($logfile, &FormattedDateString . "\tWebsite $pageToMonitor has not changed\n"); } else { SaveToFile($logfile, &FormattedDateString . "\tError: website $pageToMonitor appears to have changed since last download\n"); #send_mail($mailfrom, $mailto, $mailsubj, "Error: website $pageToMonitor appears to be unavailable" ); } } else { #send_mail($mailfrom, $mailto, $mailsubj, "Error: website $pageToMonitor appears to be unavailable" ); SaveToFile($logfile, &FormattedDateString . "\tError: website $pageToMonitor appears to be unavailable\n"); } # Shared Functions # SaveToFile (data, filepath) # Writes the data in the first block to the filepath in the second block sub SaveToFile { my ($filepath, $data) = @_; open OUTFILE, ">>$filepath" or die $!; print OUTFILE $data; close OUTFILE; } # FormattedDateString # Returns the current date/time in the format YYYYMMDDHHMMSS sub FormattedDateString { my($sec, $min, $hour, $mday, $mon, $year, $wday, $isdst) = localtime(); return sprintf('%.4d%.2d%.2d%.2d%.2d%.2d', $year + 1900, $mon++, $mday, $hour, $min, $sec); }