29.02.2016 yahe code legacy security thoughts
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, die sich z.B. als angebliches Sipgate-Fax tarnt; oder gar als altmodische und als ausgestorben geglaubte Batch-Datei. Bei den Opfern des Verschlüsselungstrojaners handelt es sich 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, den Krypto-Trojaner anhand seiner Funktionsweise zu erkennen und so eine Anti-Malware-Software auf Heuristikbasis zu entwickeln.
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 Behebung 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 und 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 behoben 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 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. 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 Behebung 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 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, wobei speziell entwickelte Werkzeuge wesentlich mehr Komfort bieten können. Im Folgenden soll eine Erkennung und Behandlung eines Krypto-Trojaner-Angriffs mit Hilfe von Mercurial dargestellt werden. Anbei folgt ein beispielhaftes Skript 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-basierten, sowie Anomalie-basierten Intrusion Detection Systems zu verstehen.
#!/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:
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 an 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 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.