· 

Brute Force mit Python

Disclaimer: Die hier beschriebene Angriffstechnik darf nur auf dem eigenen Rechner durchgeführt werden. Auf Rechnern Dritter darf die hier beschriebene Angriffstechnik nur dann durchgeführt werden, wenn der Besitzer dieses Rechners seine Zustimmung dafür gegeben hat. Ich übernehme keine Haftung für jedweden durch Missachtung dieser Vorgaben entstandenen Schaden! Dieser Beitrag ist lediglich zu Bildungszwecken gedacht! Don't Learn to Hack - Hack to Learn!

1. Einführung

Wenn es um das Knacken von Passwörtern oder das Entschlüsseln von Dateien geht, hört man oft den Begriff "Brute Force". Hierbei handelt es sich um eine Herangehensweise, bei der man mit "roher Gewalt" einfach alle möglichen Kombinationen an Schlüsseln durchgeht, bis man denjenigen gefunden hat, der zum Ziel führt.

Es gibt bereits zahlreiche Tools, mit denen man Brute Force Attacken durchführen kann. Eines der bekanntesten dürfte in diesem Zusammenhang wohl Hashcat sein. Wir wollen uns in diesem Artikel trotzdem einmal selbst am Programmieren eines Bruteforcers in Python versuchen. Damit kann man dir dann nicht mehr vorwerfen ein Skript-Kiddie zu sein, da du den Angriff nicht nur verstanden hast, sondern sogar mithilfe eines selbst geschriebenen Programms durchführen kannst.

Du solltest dir vor diesem Artikel die Artikel Wie Hacker Passwörter knacken und Brute-Force-Attacken durchlesen.


2. Implementierung

Zunächst benötigen wir in Python vier Bibliotheken, nämlich itertools, functools, operator und hashlib. itertools wird für die Berechnung des Schlüsselraums gerbaucht, functools und operator zur Umwandlung der Ergebnisse des Schlüsselraums und hashlib für die Berechnung von Passwort-Hashes mit verschiedenen Algorithmen. Unser Programm soll mit dem SHA512-Algorithmus arbeiten. Du kannst natürlich auch jeden beliebigen anderen Hash-Algorithmus verwenden, den hashlib zur Verfügung stellt. Es kommt darauf an, welcher Algorithmus für die zu entschlüsselnden Daten verwendet wurde.

import itertools
import functools 
import operator  
import hashlib

Als nächstes definierst du dir ein Charset, also eine Menge an Zeichen, die in deinem Passwort vorkommen können. Dafür reicht ein einfacher String. Durch Anpassen dieses Strings kannst du gezieltere Angriffe durchführen, z. B. wenn nur Zahlen erlaubt sind.

chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

Wir schreiben uns nun eine Funktion bf (kurz für Brute Force), der wir als Argument die vermutete Länge unseres Passworts und den Passworthash übergeben. 

def bf(n, hash):

Nun erzeugen wir eine Liste mit allen möglichen Passwörtern, die aus dem angegebenen Zeichensatz erzeugt werden können. Hierzu rufen wir die Funktion product auf, der wir den Zeichensatz und die Länge der Passwörter übergeben. In einer for-Schleife werden alle möglichen Kombinationen aus Zeichen der Länge n aufgelistet. Da wir als Ergebnisse n-Tupel mit den einzelnen Zeichen der Passwörter erhalten, wandeln wir diese in eine Zeichenkette (String) um.

passwords = [functools.reduce(operator.add, (p)) for p in itertools.product(chars, repeat=n)]

Nun iterieren wir über alle erstellten Passwörter drüber und prüfen für jedes Passwort, ob der SHA512-Hash dieses Passworts mit dem übergebenen Hash übereinstimmt. Wenn das der Fall ist, dann geben wir den Hashwert und das dazugehörige Passwort aus. Da eine Hash-Kollision (also dass zwei verschiedene Passwörter denselben Hash ergeben) sehr unwahrscheinlich ist, prüfen wir nicht weiter und geben das gefundene Passwort danach zurück. Wenn wir am Ende der for-Schleife angekommen sind, geben wir None zurück, da das Passwort nicht unter den ermittelten Schlüsseln war.

for password in passwords:
    if hashlib.sha512(password.encode()).hexdigest() == hash:
        print("Hash: " + hash + "=> Passwort: " + password)
        return password
return None

Diese Herangehensweise ist übrigens sehr speicherintensiv, da alle möglichen Passwörter zuerst gespeichert und dann berechnet werden müssen. Diese Herangehensweise ist aber durchaus legitim und lädt dazu ein, die berechneten Ergebnisse zu speichern, um sie nicht jedesmal wieder neu ermitteln zu müssen. Findest du dafür vielleicht sogar selbst eine Lösung? In einem der nächsten Artikel beschreibe ich, wie man weitaus effizienter (d. h. weniger speicherintensiv) vorgehen kann.

Oft ist die genaue Länge eines Passworts nicht bekannt. Deshalb muss man in einem bestimmten Längenbereich suchen können, d. h. bspw. Passwörter mit einer Mindestlänge von 4 und einer Maximallänge von 7 Zeichen. Hierfür schreiben wir uns eine Funktion bruteforce, die drei Argumente erhält, nämlich eine Mindestlänge, eine Maximallänge und den Passwort-Hash. In einer for-Schleife, die von min bis max geht, rufen wir jetzt die zuvor geschriebene Funktion bf auf. Wenn als Ergebnis nicht None zurückkommt, d. h. das Passwort wurde gefunden, dann bricht die for-Schleife mit einem break ab. Ansonsten läuft sie weiter, bis alle Längen überprüft wurden.

def bruteforce(min, max, hash):
    for _ in range(min, max+1):
        if bf(_, hash) != None:
            break

Das Programm kann natürlich nach Belieben erweitert werden. Es bietet sich z. B. an anzugeben, wie viele Passwörter theoretisch noch überprüft werden müssen, wie lange das Programm schon läuft und voraussichtlich noch laufen wird etc..

Für den Hash 

f6e45472652e94d48f5ada6eac848ee3a3d3252906295532a1b93ae7bfa8f539eba5b123cb0b57daa99d3c17c313cbfa992cf40de5222c91e84bbc2ba4b80971

erhält man mit dem Funktionsaufruf

hash = "f6e45472652e94d48f5ada6eac848ee3a3d3252906295532a1b93ae7bfa8f539eba5b123cb0b57daa99d3c17c313cbfa992cf40de5222c91e84bbc2ba4b80971"
bruteforce(3, 5, hash)

bei dem alle Passwörter der Länge 3 bis 5 geprüft werden, nach wenigen Sekunden das Passwort cZ12.


3. Quellcode

import itertools
import functools 
import operator  
import hashlib

chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

def bf(n, hash):
    passwords = [functools.reduce(operator.add, (p)) for p in itertools.product(chars, repeat=n)]
    for password in passwords:
        if hashlib.sha512(password.encode()).hexdigest() == hash:
            print("Hash: " + hash + "=> Passwort: " + password)
            return password
    return None
            
def bruteforce(min, max, hash):
    for _ in range(min, max+1):
        if bf(_, hash) != None:
            break