Aktive Verteidigung gegen Krypto-Trojaner

Mit Locky sieht die IT-Welt derzeit einen der koordiniertesten Trojanerangriff der jüngeren Zeit. Während er sich Anfangs als Schläfer getarnt haben soll, findet er inzwischen auf unterschiedlichsten Wegen sein Ziel: als Word- oder Excel-Dokument mit schädlichem Makro, als JavaScript-Datei - z.B. getarnt als angebliches Sipgate-Fax - oder gar als altmodische und ausgestorben geglaubte Batch-Datei. Bei den Opfern des Verschlüsselungstrojaners handelt es sich durchaus auch um größere Organisationen wie Krankenhäuser und ein Fraunhofer-Institut. Man ging zeitweise von 5000 Neuinfektionen pro Stunde aus.

Die Vorschläge für Gegenmaßnahmen sehen derzeit noch eher mager aus: die einen raten zur Symptombekämpfung durch Backups, deaktivierte Makros und Virenscannern, andere wiederum versuchen, durch das Erkennen der Funktionsweise des Trojaners (schnelles Bearbeiten vieler Dateien in kurzer Zeit) eine Anti-Malware-Software auf Heuristikbasis zu erstellen.

Um sich erfolgreich gegen solch einen Trojanerangriff zur Wehr zu setzen, muss man jedoch an mehreren Baustellen aktiv werden - und anstatt unterschiedlichste Teilmaßnahmen einzuführen, sollte man sich überlegen, welche Kombination von Maßnahmen zu einem ganzheitlichen Schutz führen kann. Die folgende Auflistung soll dabei eine kleine Hilfestellung für einen aktiven Umgang mit solch einem Infektionsrisiko bieten. Das folgende Dreiergespann aus Vermeidung, Erkennung und Behandlung ist auch in der Medizin durchaus weit verbreitet.

Zuerst einmal sollte man natürlich das Thema Vermeidung betrachten. Hierzu zählen Dinge wie das Deaktivieren der Makrofunktion in Office-Anwendungen, die Aufklärung von Mailnutzern darüber, dass Office-Anhänge von außen nicht zu öffnen und ZIP-Dateien von außen nicht zu entpacken sind. Auch das Deaktivieren von JavaScript auf exponierten Arbeitsplätzen oder gar der Wechsel auf ein Betriebssystem mit konsequenter Trennung von Ausführungsrechten und Dateinamen kann eine Option darstellen.

Anschließend sollte man sich darum kümmern, eine Erkennung eines erfolgreichen Angriffs zu ermöglichen. Erst, wenn eine Infektion erkannt werden kann, kann sie im Anschluss auch behandelt werden. Leider ist es noch häufig so, dass eine Erkennung lediglich durch Virenscanner erfolgt. Diese sind jedoch so spezifisch an einzelne Schädlinge und deren spezifisches Verhalten angepasst, dass neue Generationen häufig für längere Zeit unentdeckt bleiben. Anstatt Verhaltensmuster der Schädlinge zu untersuchen, ist es daher sinnvoller, Standardverhaltensmuster seiner Mitarbeiter zu aggregieren und Abweichungen von diesem Standardverhalten in einem Sicherheitsmonitoring zu sammeln. So ist es möglich, auch neue Schädlinge anhand eines vom Durchschnitt abweichenden Verhaltens zu erkennen, anstatt bereits im Voraus das Verhalten des Schädlings kennen zu müssen. Beispielsweise könnte zum Erkennen eines Krypto-Trojaners die durchschnittliche Anzahl an neu erstellten, bearbeiteten und gelöschten Dateien pro Tag herhalten. Während ein typischer Büroarbeiter eher selten viele Dateien gleichzeitig anlegt und gleichzeitig viele Dateien löscht, ist genau das das Hauptgeschäft von Krypto-Trojanern. Solch eine Erkennung von Abweichungen kann mit Canaries ergänzt werden. Dabei handelt es sich um Dateien, die extra als Opfer für einen Verschlüsselungstrojaner bereitgestellt werden und deren Bearbeitung als ein eindeutiges Indiz für das Vorhandensein eines Krypto-Trojaners dienen kann. Das Ziel der Erkennung ist es, von einem Problem zu erfahren, noch bevor der Nutzer etwa Ungewöhnliches feststellt.

Abschließend muss das Thema der Behandlung betrachtet werden. Hierzu zählen an erster Stelle regelmäßige Backups. Das häufig kommunizierte Mantra, Dateien auf einem externen Datenträger zu sichern und diesen anschließend wieder vom Computer zu trennen, stellt in den meisten Umgebungen keinen ausreichenden Schutz dar. Denn während des Backups kann auch der externe Datenträger befallen und verschlüsselt werden. Der eigentliche Wunsch hinter solch einer Aussage ist vielmehr, eine Sicherung zu erstellen, die von einer eventuellen Infektion nicht beeinflusst werden kann. Sinnvolle Varianten können hierbei extern durchgeführte Backups sein, die nicht vom infizierten Nutzer angestoßen und demnach auch nicht vom infizierten Nutzer modifiziert werden können. Auch lokale Backups unter der Hoheit eines anderen Systemnutzers stellen eine Möglichkeit dar. Noch sinnvoller als ein einfaches Backup ist jedoch eine Versionierung, die zwar mehr Speicherplatz benötigt, dafür aber auch eine selektive Wiederherstellung von Dateiinhalten unterschiedlichster Zeitpunkte ermöglicht.

Die Implementierung eines entsprechenden Schutzes ist auch mit einfachen Tools möglich - auch, wenn speziell entwickelte Werkzeuge wesentlich mehr Komfort bieten. Im Folgenden soll eine Erkennung und Behandlung eines Krypto-Trojaner-Angriffes mit Hilfe von Mercurial dargestellt werden. Wie Mercurial für das Erkennen von unspezifischen Angriffen auf Server zum Einsatz kommen kann, hatte ich bereits vor einiger Zeit schon einmal demonstriert.

Anbei einmal ein beispielhaftes Script zur Erkennung von Abweichungen in der Bearbeitung von Dateien. Das Script ermittelt Abweichungen von der durchschnittlichen Menge an erstellten/gelöschten/bearbeiteten Dateien. Zudem prüft es separat die Veränderung eines Canaries. Das Script ist nur als Proof-of-Concept eines dateiorientierten host-based, anomality-based Intrusion Detection Systems zu verstehen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/php
<?php
  // user-defined values
  define("CHECKFOLDER", "/testpit/");
  define("CHECKCANARY", CHECKFOLDER . "data/do-not-edit.txt");
  define("STATUSFILE",  CHECKFOLDER . "status/status");

  // deviation of user behaviour from previously collected behaviour
  define("DEVIATIONVALUE", 0.5); // default deviation of 50% is allowed

  // generated values
  define("CHECKDATE", date("Ymd-His"));

  // concatenated actions
  define("ADDREMOVECMD", "hg addremove -R "" . CHECKFOLDER . "" -X "" . CHECKCANARY . """);
  define("COMMITCMD",    "hg commit -R "" . CHECKFOLDER . "" -X "" . CHECKCANARY . "" -m "" . CHECKDATE . """);
  define("STATUSCMD",    "hg status -R "" . CHECKFOLDER . """);

  // static definitions
  define("ADDEDHINT",     "A");
  define("MISSINGHINT",   "!");
  define("MODIFIEDHINT",  "M");
  define("NOTTRACKEDHINT","?");
  define("REMOVEDHINT",   "R");

  define("HINTDELIMITER",       " ");
  define("STATISTICSDELIMITER", ":");

  function check_filename($line, $filename) {
    $result = false;
   
    $parts = explode(HINTDELIMITER, $line, 2);
    if ((false !== $parts) && (2 === count($parts))) {
      $result = (0 == strcasecmp(CHECKFOLDER . $parts[1], $filename));
    }

    return $result;
  }

  function check_hint($line, $hint) {
    $result = false;

    $parts = explode(HINTDELIMITER, $line, 2);
    if ((false !== $parts) && (2 === count($parts))) {
      $result = (0 == strcasecmp($parts[0], $hint));
    }

    return $result;
  }

  function get_statistics_text($additions, $deletions, $modifications) {
    return (CHECKDATE . STATISTICSDELIMITER . ADDEDHINT . STATISTICSDELIMITER . $additions .
                        STATISTICSDELIMITER . MODIFIEDHINT . STATISTICSDELIMITER . $modifications .
                        STATISTICSDELIMITER . REMOVEDHINT . STATISTICSDELIMITER . $deletions);
  }

  function check_statistics($additions, $deletions, $modifications) {
    $result = true;

    // with any modification there's nothing to check
    if (0 < ($additions + $deletions + $modifications)) {
      // read statistics and execute statistics checkvg_additions_count     = 0;
      $avg_additions_count     = 0;
      $avg_additions_value     = 0;
      $avg_deletions_count     = 0;
      $avg_deletions_value     = 0;
      $avg_modifications_count = 0;
      $avg_modifications_value = 0;

      if (is_file(STATUSFILE)) {
        $statistics = file(STATUSFILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if ((false !== $statistics) && (0 < count($statistics))) {
          // calculate average additions, deletions and modifications from statistics data
          foreach ($statistics as $line) {
            $parts = explode(STATISTICSDELIMITER, $line, 7);
            if ((false !== $parts) && (7 === count($parts))) {
              // check if value is integer and bigger than 0
              if (is_numeric($parts[2]) && (0 < $parts[2])) {
                $avg_additions_value += $parts[2];
                $avg_additions_count++;
              }
              if (is_numeric($parts[6]) && (0 < $parts[6])) {
                $avg_deletions_value += $parts[6];
                $avg_deletions_count++;
              }
              if (is_numeric($parts[4]) && (0 < $parts[4])) {
                $avg_modifications_value += $parts[4];
                $avg_modifications_count++;
              }
            }
          }
        }
      }

      // there's nothing wrong when nothing happened
      if (0 < $additions) {
        // when actions has never been seen then that's a deviation
        if (0 === $avg_additions_count) {
          $result = false;
        } else {
          // more additions than expected
          if (((1.0 + DEVIATIONVALUE) * ($avg_additions_value / $avg_additions_count)) < $additions) {
            $result = false;
          }
        }
      }

      // there's nothing wrong when nothing happened
      if (0 < $deletions) {
        // when actions has never been seen then that's a deviation
        if (0 === $avg_deletions_count) {
          $result = false;
        } else {
          // more deletions than expected
          if (((1 + DEVIATIONVALUE) * ($avg_deletions_value / $avg_deletions_count)) < $deletions) {
            $result = false;
          }
        }
      }

      // there's nothing wrong when nothing happened
      if (0 < $modifications) {
        // when actions has never been seen then that's a deviation
        if (0 === $avg_modifications_count) {
          $result = false;
        } else {
          // more modifications than expected
          if (((1 + DEVIATIONVALUE) * ($avg_modifications_value / $avg_modifications_count)) < $modifications) {
            $result = false;
          }
        }
      }
    }
   
    // add new result to statistics
    file_put_contents(STATUSFILE,
                      get_statistics_text($additions, $deletions, $modifications) . "\n",
                      FILE_APPEND | LOCK_EX);

    return $result;
  }

  function main() {
    $additions     = 0;
    $deletions     = 0;
    $modifications = 0;

    $canary_found = false;

    // retrieve information about file changes
    exec(STATUSCMD, $output);

    // accept file changes right away
    exec(ADDREMOVECMD);
    exec(COMMITCMD);

    // iterate through file changes
    foreach ($output as $line) {
      // check if the canary file is part of the modifications
      if (check_filename($line, CHECKCANARY)) {
        $canary_found = true;
      } else {
        // check if a file has been added
        if (check_hint($line, ADDEDHINT) || check_hint($line, NOTTRACKEDHINT)) {
          $additions++;
        } else {
          // check if a file has been deleted
          if (check_hint($line, MISSINGHINT) || check_hint($line, REMOVEDHINT)) {
            $deletions++;
          } else {
            // check if a file has been modified
            if (check_hint($line, MODIFIEDHINT)) {
              $modifications++;
            }
          }
        }
      }
    }

    if (0 < ($additions + $deletions + $modifications)) {
      // accept file changes
      exec(ADDREMOVECMD);
      exec(COMMITCMD);
    }

    // print result
    print(get_statistics_text($additions, $deletions, $modifications) . "\n");

    // the canary has been modified
    if ($canary_found) {
      //!!! do something
      print("ALERT! CANARY HAS BEEN MODIFIED!\n");
    }

    // check if the modifications do not match the statistics
    if (!check_statistics($additions, $deletions, $modifications)) {
      //!!! do something
      print("ALERT! BEHAVIOUR DOES NOT MATCH STATISTICS!\n");
    }
  }

  // execute application
  main();

?>

Bei jedem Aufruf ermittelt das Script mit Hilfe von Mercurial, welche Dateien in einem Repository/Ordner hinzugefügt, entfernt oder bearbeitet wurden. Sollte sich darunter die Canary-Datei befinden, wird Alarm geschlagen. Zudem wird eine statistische Analyse durchgeführt. In diesem einfachen Beispiel gilt als Abweichung, wenn mehr als 150% der durchschnittlichen Dateioperationen erkannt wurden. Wird solch eine Abweichung erkannt, wird ebenfalls Alarm geschlagen. Durch die Verwendung von Mercurial ließen sich zudem zu jeder Zeit alle bearbeiteten Dateien rekonstruieren.

In einem realen Umfeld könnte solch eine Analyse natürlich noch viel tiefergreifend sein. So könnten beispielsweise folgende Prüfungen mit einfließen, um Anomalien besser erkennen zu können:

  • Es könnte die Uhrzeit der Dateibearbeitung mit in die Analyse einfließen. In Kombination mit der Auswertung von Arbeitsplänen und/oder Anwesenheitszeiten ließen sich bessere Modelle erstellen. So sollte es seltener der Fall sein, dass Dateien eines Mitarbeiters bearbeitet werden, der gar nicht anwesend ist.
  • Es könnte die Relation der Dateibearbeitungen untereinander betrachtet werden. Beispielswiese werden Büromitarbeiter wesentlich mehr Dateien erstellen und bearbeiten als löschen, da Arbeitsergebnisse selten wieder vernichtet werden.
  • Es könnte das ursprüngliche Dateidatum mit berücksichtigt werden. Beispielsweise ist es eher unüblich, dass Dateien ab einem bestimmten Alter noch einmal bearbeitet werden. Stattdessen werden sie eher als Referenz vorgehalten, anstatt als aktives Arbeitsdokument zu fungieren.

Je besser das erstellte Modell ist, mit dem das Verhalten der Systemnutzer abgeglichen wird, desto schneller erkennt man in Ausnahmesituationen einen potentiellen Angreifer. Nicht immer müssen das externe Angreifer sein. Auch interne Mitarbeiter, die ein ungewöhnliches Verhalten in den Tag legen, können auf diese Weise unter Umständen identifiziert werden - es muss nicht einmal zwingend eine böse Absicht hinter diesem Verhalten stecken, evtl. stellt ein Mitarbeiter einfach eine Ausnahme zur üblichen Regel dar.

Generell sollte man sich bei solch einer Angriffserkennung auf Basis von Abweichungsermittlungen daran gewöhnen, auch mal False Positives zu erhalten. Diese sind durchaus wünschenswert, da sie einerseits zeigen, dass die durchgeführten Analysen tatsächlich einen Effekt haben und einem andererseits Verbesserungspotentiale in den erstellten Anomaliemodellen aufzeigen. 🙂

Verteidigende Grüße, Kenny

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.