Automatisierung und Abwehr automatisierter Angriffe
Wenn Ihre Anwendung mit dem Internet verbunden ist, stehen die Chancen gut, dass sie bereits Opfer automatisierter Angriffe geworden ist. Dazu gehört eine Reihe verschiedener Angriffstypen wie Content Scraping, Credential Stuffing, Anwendungs-DDoS, Missbrauch von Webformularen, Token Guessing und vieles mehr. Die Software, die für solche automatisierten Aufgaben eingesetzt wird, wird als Bot bezeichnet, und die böswillige Nutzung automatisierter Tools lässt sich als automatisierte Bedrohung kategorisieren. Derartige automatisierte Angriffe können von einer Vielzahl von Akteuren ausgehen. In der Regel handelt es sich dabei um Cybercrime-Organisationen, gelegentlich aber auch um Einzelpersonen, die sich persönliche Vorteile verschaffen wollen (zum Beispiel durch das Snipen von eBay-Auktionen).
Um automatisierte Angriffe zu verstehen und abzuwehren, bevor sie sich negativ auf Ihr Geschäft auswirken, ist es wichtig, die Taktiken und Techniken der Angreifer zu kennen. Durch Simulation einfacher und komplexer automatisierter Angriffe können Sie weitere Erkenntnisse gewinnen und überprüfen, ob Ihre Sicherheitsmaßnahmen gemäß Ihren Vorstellungen funktionieren.
In diesem Blogpost stellen wir Ihnen verschiedene Kategorien von Bots, Methoden und Tools vor, die Red Teams zu Testzwecken nutzen können. Außerdem zeigen wir Ihnen, wie Sie die richtige Auswahl treffen und Funktionalitäten ausbauen und feinabstimmen können. Abschließend beschäftigen wir uns mit defensiven Sicherheitsmaßnahmen, die Angriffe erschweren und kostspielig machen.
Browserunabhängige Clients
Die herkömmlichste Form der Automatisierung nutzt eine Art browserunabhängigen Client wie Python, Go, curl oder andere. Diese Arten von Bots reichen von handelsüblichen Anbieter-Tools bis hin zu Open-Source-Projekten und können für das Scraping von Daten, automatisierte Penetrationstests und vieles mehr verwendet werden. Eine Suche nach #pentest auf GitHub dürfte beispielsweise einige interessante Ergebnisse liefern.
Das folgende Snippet zeigt ein Beispiel für einen in Python entwickelten Bot, der die Bibliotheken von Requests und BeautifulSoup für das Scrapen und Parsen von Website-Daten nutzt:
import requests
from bs4 import BeautifulSoup
page = requests.get(url='https://example.com')
content = BeautifulSoup(page.content, 'html.parser')
Im Gegensatz zu einem normalen Browser können browserunabhängige Clients wie dieses Skript keinen Javascript Code ausführen. Moderne Anti-Bot-Lösungen verwenden Javascript, um automatisch Attribute in Bezug auf den Browser und die Systemkonfiguration zu sammeln und zu melden. Dies bezeichnet man auch als clientseitige Erkennung. Wenn diese Daten in den Client-Anfragen nicht zurückgegeben werden, ist das ein guter Hinweis darauf, dass Automatisierung im Spiel ist.
Browser-Automatisierungs-Frameworks
Obwohl browserunabhängige Tools auf Angreiferseite äußerst nützlich sein können, sind sie auf Verteidigerseite oft leichter zu erkennen. Eine der größten Herausforderungen für Bot-Entwickler sind Sicherheitslösungen, die diese Tools laufend aufspüren und blockieren. Um also nicht entdeckt zu werden, müssen Angreifer echten Traffic imitieren.
Browser-Automatisierungs-Frameworks wie Puppeteer und Playwright haben sich zu den Standardlösungen für die Erstellung humanoider Bots entwickelt. Sie unterstützen die Ausführung von Javascript Code, da sie echte Browser nutzen. Außerdem sind sie in der Lage, eine ganze Reihe von Aufgaben zu automatisieren, um den Schein eines echten Nutzers zu geben, darunter eine Seite laden, die Maus bewegen oder Text eingeben.
Sie können diese Automatisierungs-Frameworks zur Instrumentierung von Browsern mit oder ohne grafische Nutzeroberfläche (GUI) verwenden. Browser ohne GUI, sogenannte „Headless“-Browser, haben die gleiche Funktionalität wie echte Browser, beanspruchen aber viel weniger CPU-Leistung und RAM, da sie keine Grafiken oder anderen Komponenten der Nutzeroberfläche bearbeiten müssen. Der beliebteste Headless-Browser ist Headless Chrome.
Das folgende Snippet ist ein Beispiel für ein Puppeteer Skript, das zu https://example.com/ navigiert und einen Screenshot als example.png im Headless-Modus speichert:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/');
await page.screenshot({ path: example.png' });
await browser.close();
})();
Auch wenn das Schreiben solcher Skripte Spaß machen kann, erfordert ihre Entwicklung Fachkenntnisse und Zeit. Aber glücklicherweise gibt es Tools, die Ihnen dabei helfen können. Google hat zum Beispiel Recorder eingeführt, das in den Chrome Developer Tools (ab Chrome Version 97) verfügbar ist und mit dem Sie Nutzerabläufe im Recorder Panel aufzeichnen und wiedergeben sowie in ein Puppeteer Skript exportieren können, ohne eine einzige Zeile Code schreiben zu müssen.
Käufliche Bots
Bei der Vorstellung eines Angreifers fällt vielen von uns das Klischee eines teuflischen Genies ein, das mit ein paar Zeilen Code innerhalb von Minuten in Systeme eindringen kann. Wie hinter jedem Klischee steckt auch hinter diesem ein Fünkchen Wahrheit, in den meisten Fällen ist diese Vorstellung aber übertrieben und unrealistisch. In Wirklichkeit suchen Cyberkriminelle oft den Weg des geringsten Widerstands. Bots sind bekanntermaßen schwierig einzurichten und auszuführen, und die meisten Angreifer möchten ihre Ziele so schnell wie möglich erreichen. Wie Sie vielleicht schon richtig vermutet haben, gibt es also einen Marktplatz für den Kauf und Verkauf von Bots.
Eine Branche, in der käufliche Bots – nämlich in Form von sogenannten „Sneaker Bots“ – ein echter Renner sind, ist der Einzelhandel. Wenn Sneaker-Sportschuhe in begrenzter Stückzahl auf den Markt kommen, wird der Weg zur Kasse für Sneakerheads häufig zum Wettlauf. Sneaker Bots sind darauf ausgelegt, diesen Prozess zu beschleunigen und sich so einen Vorteil gegenüber anderen Käufern zu verschaffen, die ihre Transaktionen manuell abwickeln. Der Einsatz solcher Scalping Bots beschränkt sich aber nicht nur auf Sportschuhe, sondern auch auf alle anderen Online-Artikel, die in begrenzter Auflage verfügbar sind.
Meist sind diese (im Voraus geplanten) Bot-Dienste nicht illegal. Allerdings verstoßen sie oft gegen die von vielen Websites festgelegten Nutzungsbedingungen. Sie können sich negativ auf den Ruf einer Marke und die Markentreue auswirken, denn Kunden werden künstlich am Kauf eines Produkts gehindert und eine schlechtere Website-Performance sowie höhere Infrastrukturkosten durch Überlastung verursacht.
Andere Arten von Bots, die häufig zum Verkauf stehen, sind Botnets, also eine Reihe von Rechnern, die mit Malware infiziert sind. In Foren wie RaidForums, einem kürzlich von den Behörden geschlossenen Forum für Cyberkriminalität, wird bekanntermaßen Handel mit Botnets betrieben. Botnets können zu einer großen Anzahl von Bots heranwachsen. Es ist denkbar, dass ein großes Botnet, das aus Millionen von Rechnern besteht, für einen DDoS-Angriff (Distributed Denial of Service) verwendet wird, während ein kleines Botnet gezielt in wertvolle Systeme wie Regierungs- oder Finanzdaten eindringt.
Simulation automatisierter Angriffe
Bisher haben wir verschiedene Bot-Kategorien vorgestellt, darunter browserunabhängige Clients, Bots, die Headless-Browser nutzen, und käufliche Bots. In diesem Abschnitt befassen wir uns mit den Methoden und Tools, die Red Teams verwenden können, was bei der Auswahl zu beachten ist und wie Sie die entsprechenden Funktionalitäten ausbauen und feinabstimmen können.
Phase 1
Beginnen wir mit einem in Python entwickelten Bot, der die Bibliotheken von Requests und BeautifulSoup für das Scrapen und Parsen von Website-Daten nutzt:
import requests
from bs4 import BeautifulSoup
page = requests.get(url='https://example.com/')
content = BeautifulSoup(page.content, 'html.parser')
Hier ein relativ einfaches Skript, das eine GET-Anfrage stellt und ein BeautifulSoup Objekt zurückgibt. Führen wir dieses Skript aus und sehen wir uns die gesendete HTTP-Anfrage an:
GET / HTTP/2
Host: example.com
User-Agent: python-requests/2.28.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Bei dieser Anfrage stechen ein paar Dinge hervor. Wenn wir den User-Agent-Header genauer unter die Lupe nehmen, sehen wir, dass er python-requests/2.28.0 anzeigt. Außerdem ist der Accept-Header auf */* gesetzt. Der Accept-Header wird verwendet, um dem Server mitzuteilen, welche Arten von Inhalten der Client verarbeiten kann. Die Anweisung */* gibt an, dass alle Inhaltstypen akzeptiert werden.
Stellen wir nun dieselbe Anfrage in einem Chrome Browser:
GET / HTTP/2
Host: example.com
Sec-Ch-Ua: "-Not.A/Brand";v="8", "Chromium";v="102"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Der Unterschied zwischen den beiden Anfragen ist offensichtlich. Nicht nur sind der User-Agent- und der Accept-Header anders, in der Browser-Anfrage tauchen auch neun zusätzliche Header auf, die in dem in Python entwickelten Bot nicht erscheinen. Diese Merkmale werden in Ansätzen verwendet, die auf Fingerprinting basieren, um zu klassifizieren, ob es sich bei einem Nutzer um einen Menschen oder einen Bot handelt. Verteidiger können diese Informationen dann schnell nutzen, um Bot-Traffic zu blockieren.
Mit diesem Wissen können wir unseren Python Bot so abändern, dass er der Anfrage ähnelt, die über unseren Browser gestellt wurde:
import requests
from bs4 import BeautifulSoup
headers = {
'Sec-Ch-Ua': '"-Not.A/Brand";v="8", "Chromium";v="102"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': 'macOS',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.9',
}
page = requests.get(url='https://example.com/')
content = BeautifulSoup(page.content, 'html.parser')
Führen wir also dieses Skript aus und sehen wir uns die gesendete HTTP-Anfrage an:
GET / HTTP/2
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36
Accept-Encoding: gzip, deflate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Connection: keep-alive
Sec-Ch-Ua: "-Not.A/Brand";v="8", "Chromium";v="102"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: macOS
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Language: en-US,en;q=0.9
Auf den ersten Blick sieht es so aus, als wären nun alle Header in der Anfrage enthalten. Bei näherer Betrachtung stellt sich aber heraus, dass das Python Skript die Reihenfolge der Anfrage-Header nicht beibehalten hat. Die meisten Bibliotheken verwenden eine nicht festgelegte Reihenfolge, da die RFC-Spezifikation besagt, dass diese keine Rolle spielt. Die gängigsten Browser senden die HTTP-Header jedoch in einer bestimmten Reihenfolge, und genau dieser feine Unterschied liefert uns eine weitere Technik, um automatisierten Traffic zu erkennen. Glücklicherweise bietet die Python Request-Bibliothek die Möglichkeit, die Header-Reihenfolge in unserem Skript beizubehalten.
Phase 2
Beim Versuch, unseren Ansatz zu verfeinern, um von Sicherheitstools nicht aufgespürt zu werden, gilt es zu bedenken, dass Sicherheitslösungen auf verhaltensbasierten Ansätzen beruhen, um überhaupt Bots von Menschen unterscheiden zu können. Bots verhalten sich in der Regel ganz anders als legitime Nutzer. Um das Verhalten eines Nutzers während einer Sitzung zu tracken, wird bei diesen Ansätzen oft das Nutzerverhalten mit einer IP-Adresse verknüpft (IP-Reputation).
Hier einige Beispiele für Arten von Verhaltensmustern, die mit einer IP-Adresse verknüpft werden können:
Gesamtzahl der Anfragen
Gesamtzahl der besuchten Seiten
Zeit zwischen Seitenaufrufen
Reihenfolge, in der Seiten aufgerufen wurden
Auf Seiten geladene Arten von Ressourcen
Wenn wir, ausgehend von unserem Beispielskript, große Datenmengen scrapen wollten, wären wir anhand eines dieser Muster leicht zu entdecken oder zu blockieren. Daher probieren wir, die IP-Adresse alle paar Versuche zu wechseln, um nicht ins Raster von Rate-Limiting- oder Blockierungsregeln zu fallen. Dies lässt sich mithilfe eines Proxy-Servers erreichen. Ein Proxy fungiert als Vermittler zwischen einem Client und dem Rest des Internets. Wenn eine Anfrage über einen Proxy gesendet wird, wird die IP-Adresse des Proxys offengelegt und die IP-Adresse des Clients (oder Bots), von dem die Anfrage stammt, verdeckt.
Welche Art von Proxy Sie benötigen, hängt von den Einschränkungen ab, die Sie umgehen möchten. IP-Adressen, die zu Datencentern, VPNs, bekannten Proxy-Servern und Tor Netzwerken gehören, fallen bei einer Analyse viel eher auf oder wurden bereits als verdächtig eingestuft. Die meisten ausgefeilten Bots nutzen daher weniger bekannte private Proxy-Server. Da private IP-Adressen von legitimen Nutzern verwendet werden, gelten sie im Vergleich zu anderen Typen als vertrauenswürdiger.
Bevor Sie sich aber die Mühe machen, Proxy-Services einzurichten, sollten Sie überprüfen, ob die Zielanwendung so konfiguriert ist, dass sie Anfrage-Headern wie X-Forwarded-For, True-Client-IP oder X-Real-IP zur Angabe der entfernten IP-Adresse des anfragenden Clients vertraut. Angreifer können dies ausnutzen, indem sie eine gefälschte IP-Adresse angeben. Tatsächlich war das Fastly Security Research Team an der Entwicklung von Nuclei beteiligt, mit dem Sie eine beliebige IP-Adresse in einen bestimmten HTTP-Anfrage-Header einfügen können, um diese Schwachstelle auszunutzen und das Verhalten eines Proxys zu emulieren.
Neben der IP-Adresse hat sich das TLS-Fingerprinting zu einem weit verbreiteten Tool entwickelt, um die Art des Clients zu bestimmen, der mit einer Website kommuniziert. Diese Fingerabdrücke verwenden unterstützte TLS Cipher Suites, um ein Gerät zu identifizieren. Lesen Sie unseren Blogpost The State of TLS Fingerprinting (nur auf Englisch verfügbar), um mehr über TLS-Fingerprinting zu erfahren. Diese Erkennungsmethode hat ihre Tücken, da der Client die Kontrolle über das ClientHello Paket hat, sodass ein Bot seinen Fingerabdruck ändern kann. Tools wie uTLS können verwendet werden, um TLS-Signaturen zu imitieren.
Phase 3
Die bisher erläuterten Ansätze beziehen sich auf serverseitige Funktionen. Eine Bot-Lösung, die nur auf serverseitige Erkennung abzielt, hat ihre Grenzen. Denn im Gegensatz zu browserunabhängigen Bots verwenden fortschrittliche Bots Browser-Automatisierungs-Frameworks, die echte Browser nutzen und damit in der Lage sind, gefälschte HTTP- und TLS-Fingerabdrücke zu erstellen, die denen legitimer Nutzer ähneln, und Javascript Code auszuführen.
Um fortschrittliche Bots erkennen zu können, ist eine clientseitige Erkennung unerlässlich. Zu den clientseitigen Funktionen gehören Ereignisse und Attribute wie Mausbewegungen, gedrückte Tasten, Bildschirmauflösung und Geräteeigenschaften, die mit Javascript erfasst werden. Diese Skripte testen auf das Vorhandensein von Attributen, die in Headless-Browsern oder Instrumentierungs-Frameworks zu finden sind.
Eine Technik, die bei der clientseitigen Erkennung Einsatz findet, ist beispielsweise die Prüfung auf das Vorhandensein von Audio- und Videocodecs. Das folgende Code-Snippet prüft mithilfe der Funktion canPlayType auf einen unterstützten Medientyp:
const audioElt = document.createElement('audio')
return audioElt.canPlayType('audio/aac')
Bei einem echten Chrome Browser sollte hier der Wert „probably“ zurückgegeben werden. Ändern wir also unser Puppeteer Skript aus einem früheren Beispiel ab, um zu überprüfen, ob es den erwarteten Wert zurückgibt:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: example.png' });
const audioElt = await page.evaluate(() => {
let obj = document.createElement('audio');
return obj.canPlayType('audio/aac');
})
console.log(audioElt);
await browser.close();
})();
Leider wird dabei ein leerer Wert zurückgegeben. Außerdem kann Headless Chrome serverseitig über seinen User Agent identifiziert werden:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/103.0.5058.0 Safari/537.36
Standardmäßig maskiert Puppeteer keine Features, die offensichtliche Erkennungssignale sind, sondern legt offen, ob eine Automatisierung vorliegt. Der Hauptzweck von Puppeteer ist die Automatisierung von Website-Tests und nicht die Erstellung humanoider Bots, die sich der Erkennung entziehen können. Erwartungsgemäß gibt es jedoch eine Open-Source-Bibliothek namens puppeteer-extra, in der ein Stealth-Plugin zu finden ist, mit dem Bot-Entwickler diese Standardeinstellungen leicht ändern und verschiedene Techniken anwenden können, um die Erkennung von Headless Puppeteer zu erschweren.
Der Hauptunterschied besteht ganz einfach darin, dass Sie nicht puppeteer, sondern puppeteer-extra importieren:
const puppeteer = require('puppeteer-extra');
puppeteer.use(require('puppeteer-extra-plugin-stealth')());
Nun sollten Sie in der Lage sein zu überprüfen, ob der User Agent und die Codecs-Ausgabe des Bots mit einem legitimen Chrome Browser übereinstimmen:
const userAgent = await page.evaluate(() => {
return navigator.userAgent;
})
console.log(userAgent);
// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5109.0 Safari/537.36
const audioElt = await page.evaluate(() => {
let obj = document.createElement('audio');
return obj.canPlayType('audio/aac');
})
console.log(audioElt);
// probably
Es gibt noch viele andere Möglichkeiten, das Verhalten dieses orchestrierten Browsers zu erkennen. Deshalb ist es wichtig, dass Sie die derzeitigen Grenzen Ihres Anwendungsschutzes kennen und sich Gedanken darüber machen, was Sie tun können, um dieses Risiko weiter zu minimieren.
Zusammenfassung
Das Erkennen und Blockieren automatisierter Angriffe erfordert sorgfältige Analyse und Raffinesse. Nicht jeder Bot-Traffic ist bösartig, sondern wird manchmal sogar erwartet – zum Beispiel, wenn ein Unternehmen Client-Bibliotheken in mehreren Sprachen bereitstellt, um den Zugang zu seinen Services zu erleichtern. Aus diesem Grund ist es wichtig festzulegen, welche Art von Traffic und welche Geräte Sie beim Zugriff auf Ihre Infrastruktur begrüßen. Der Unterschied zwischen gutem und schlechtem Traffic hängt von der Umgebung ab und davon, was Sie als Abweichung von bekanntem akzeptablem Verhalten durch den Einsatz von Automatisierung betrachten.
Automatisierte Angriffe auf Ihre eigene Infrastruktur zu emulieren kann Ihren Teams dabei helfen, weitere Erkenntnisse darüber zu gewinnen, was als akzeptabel gilt. Wenn Sie die verschiedenen Bot-Kategorien verstehen und mit gegnerischen Techniken experimentieren, sind Sie besser gerüstet, um automatisierte Angriffe einzudämmen und das zu schützen, was für Ihr Unternehmen am wichtigsten ist.