Python Code-Hacking Game

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hallo zusammen,

ich habe mich in letzter Zeit etwas mit den Kryptowährungen beschäftigt und fand das ganze Prinzip recht interessant.

Daraus entstand eine Idee. Ich würde gern ein kleines Experiment starte:

I möchte einen Server hosten, der ein "Passwort" bereithält. Der Server soll von aussen erreichbar sein und mehrere Clients aufnehmen.
Nun soll jeder Client, der mit dem Server verbunden ist, per einfacher Iteration versuchen das Passwort zu lösen.
Der Client, der zuerst gelöst hat, gewinnt ;-)

Server- und Clientsoftware soll in Python laufen.
Meine Idee ist:

Der Server ist ein Pythonprogramm, dass durch "sockets" einen Server erstellt.
Ich werde ebenfalls eine Client-Software zur Verfügung stellen, die die Clients nutzen sollen um das Passwort zu lösen.

Jeder Lösungsversuch soll dann an den Server gesendet werden, der dann das Empfangene mit dem Passwort abgleicht und entsprechend darauf antwortet.
Schema:

Code: Alles auswählen

if client_guess == passphrase:
    answer:"You win"
else:
    answer "Try again"
Die Software der Clients läuft in einer Endlosschleife.

Hierbei handelt es sich lediglich um eine Idee zu der es noch keinerlei Code gibt.
Ist mein Vorhaben so machbar?
Kann ich in der Client-Software evtl. einfach eine simple random() -Funktion nutzen um den Code zu lösen?

Im Prinzip ist das Ganze wohl ähnlich eines Bruteforce-Angriffs, der allerdings gewollt ist ;-)

Danke schonmal
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Warum sollte es nicht machbar sein?
Wenn dein Server-Programm auf einen Netzwerkport lauscht und dort eine Zeichenkette entgegennimmt um dann zu antworten ob diese identisch mit der vorhandenen ist, dann ist es egal in welcher Sprache das Programm geschrieben ist, das sich als Client auf diesen Port verbindet.

Als Fingerübung ist das vielleicht ganz interessant, ich gebe aber zu bedenken, dass gut administrierte Systeme solche Brutefoce-Angriffe in der Regel verhindern.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hey,
danke für die Antwort.

Ich habe mal etwas zusammengezimmert, allerdings super simpel. Generell funktioniert es auch. Bei einem vierstelligen Passwort, kann die Lösung zwischen 30 Sekunden und 1 Minute erfolgen.
Hat jemand Ideen um das Ganze etwas spannender zu gestalten? Gibt es z.B. andere Möglichkeiten als random(), so dass die Clients z.B. jede Kombination nur einmal versuchen?
Gibts eine Grundlage die Lösungszeit zu berechnen, z.B. bei zehnstelligem Passwort?

Server:

Code: Alles auswählen

import socket
Quelle='' # Offen fuer alle
Port=123
 
e_udp_sock = socket.socket( socket.AF_INET,  socket.SOCK_DGRAM )
e_udp_sock.bind( (Quelle,Port) )  

def listen():
    passwort = "123"  
    while 1:
        data, addr = e_udp_sock.recvfrom( 1024 )                            
                            
        print "empfangene Nachricht:", data
        print "Clientadresse:", addr
        if data == passwort:        
            Nachricht2= "geloest"
            e_udp_sock.sendto( str(Nachricht2), (addr) )
            print "geloest"
            break
        else:
            Nachricht2= "weiter versuchen"
            e_udp_sock.sendto( str(Nachricht2), (addr) )
            print "antwort gesendet ", Nachricht2, "an ", addr
            continue
 listen()


Client:

Code: Alles auswählen

import socket
import random
import sys
Ziel='IP des Servers'
Quelle=' '

Port=123
s_udp_sock = socket.socket( socket.AF_INET,  socket.SOCK_DGRAM )
s_udp_sock.bind( (Quelle,Port) )

def sende(): 
    Nachricht= random.randint(1, 999)
    print "Nachricht:", Nachricht 
    s_udp_sock.sendto( str(Nachricht), (Ziel,Port) )
    data, addr = s_udp_sock.recvfrom( 1024 )
    if data == "geloest":
        print "fertig"
        sys.exit()
    else:
        print data
while True: 
	sende ()
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

CrytoChris hat geschrieben:Hat jemand Ideen um das Ganze etwas spannender zu gestalten? Gibt es z.B. andere Möglichkeiten als random(), so dass die Clients z.B. jede Kombination nur einmal versuchen?
In dem du das ganze nicht mit random machst sondern kombinatorisch (bzw. Markov Chains?) löst, also einfach der Reihe nach alle Möglichkeiten durchgehen (Tipp: itertools-Modul).

Du solltest statt UDP lieber TCP verwenden, denn du willst ja dass alle deine Versuche ankommen und nicht manche verloren gehen.

Außerdem solltest du einen Blick in die PEP-8 wagen ;).

Um die Dauer des Testens aller möglichen Passwörter kannst du einen Durchschnitt von ca. 100.000 Versuchen nehmen und den mit der Anzahl aller Möglichen Versuche multiplizieren.
the more they change the more they stay the same
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hey,
vielen Dank. Werde ich mir anschauen.

Eine Frage habe ich noch vorab ;-)

Im lokalen Netzwerk funktioniert das Ganze super und vor allem schnell.
Greife ich nun von "Aussen" auf den Server zu (mittels Umleitung im Router), so ist das ganze echt langsam. Ca. 1-2 Lösungsversuche pro Sekunde.
Und zusätzlich hört die Kommunikation nach 10-20 Versuchen auf.
Jedoch ohne Fehlermeldung. Client und Server warten beiden auf Antwort.

Gibt`s dafür eine spontane Erklärung?

Danke
BlackJack

@CrytoChris: Vielleicht genau das Problem mit UDP: Die Pakete dürfen verloren gehen und man muss seine Programme so schreiben dass das nichts ausmacht.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Jau, danke.
Das war es.

Hab nun eine stabile Lösung mit TCP :-)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@CrytoChris: zeig mal den Code. Ich habe den Verdacht, die "stabile Lösung mit TCP" ist nur scheinbar stabil.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Aber nur, wenns keine doofen Kommentare bezüglich meiner Holzhammer-Prozessbeendungsmethode gibt :-P
Vielleicht hat in dem Zuge ja noch jemand eine schönere Idee. Ich hab`s mit .join() / .terminate() sowie mit Thread, Threading und Multiprocessing probiert, bekomme die Prozesse allerdings nicht beendet. Sobald ein Client das Passwort gelöst hat, soll der Server natürlich komplett dichtmachen. Anderenfalls lösen die anderen Clients weiter, bis am Ende jeder gewonnen hat ;)


Sever.py

Code: Alles auswählen

import socket
from multiprocessing import Process
import sys
import os
Quelle='' # Offen fuer alle
Port=80

 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('', 80)
sock.bind(server_address)
sock.listen(10) 
 
def listenf(connection):
    passwort = "5064"  

    while 1:
                
        data = connection.recv(1024)                        
        print "empfangene Nachricht:", data

        if data == passwort:    
            connection.sendall("Gewinner!")
            winmsg=("Super, du hast gewonnen! Passwort: ", data)            
            connection.sendall(str(winmsg))             
            print "Der Gewinner ist: ", client_address[0], str(client_address[1])
            os.system("killall python")
		
        else:
        	
            Nachricht3 = "Dein Loesungsversuch: ", data, " ist falsch."
            connection.sendall(Nachricht3)
            continue

while True:
    connection, client_address = sock.accept()
    print("[-] Connected to " + client_address[0] + ":" + str(client_address[1]))
    process=Process(target=listenf, args=(connection,))
    process.start()
Client.py

Code: Alles auswählen


import socket
import random
import sys

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('Server-IP', 80)
sock.connect(server_address)

def sende(): 
    Nachricht= random.randint(1, 9999)
    sock.sendall(str(Nachricht))
    data = sock.recv(1024)  

    if data == "Gewinner!":
        
        data = sock.recv(1024)  
        print data
        sys.exit()
    else:        
        print data     
while True: 
	sende ()
Mittlerweile habe ich auch mit itertools eine weitaus schnellere Möglichkeit zur Lösung gefunden. Dieser Code ist aber noch nicht angepasst. Falls Interesse besteht, kann ich heute Nachmittag einmal die Itertools-Variante posten.

Ich werde das Ganze noch etwas erweitern und mehrere Lösungsblöcke erstellen. Sobald von einem Client, ein Block gelöst wurde, soll der nächste Block an der Reihe sein. Am Ende zählen die Anzahl der gelösten Blöcken pro Client.
BlackJack

@CrytoChris: Also den Kommentar bezüglich des beendens kann man hier leider nicht ersparen, denn das ist wirklich so gar keine gute Idee. Wenn ich das laufen lassen würde, dann würden mir am Ende wichtige Programme beendet die überhaupt nichts mit diesem Code zu tun haben!

Ich würde ja Threads verwenden und da müsste es eigentlich reichen wenn man das `daemon`-Attribut entsprechend setzt damit die zusammen mit dem Hauptthread beendet werden wenn das Programm endet. Und den müsste man aus einem Thread informieren können das Ende ist. Dafür gibt es im `threading`-Modul zum Beispiel das `Event`-Objekt über das man kommunizieren könnte.

Das was Sirius3 wahrscheinlich vermutet hat (und ich auch) ist das falsche verwenden von `recv()`. Ein Aufruf garantiert nicht das die gesamten Daten gelesen wurden, sondern man muss so lange lesen bis man auch ganz bestimmt eine komplette Nachricht gelesen hat. Und wenn über die Leitung mehrere Nachrichten kommen, dann muss man auch damit rechnen das man schon Teile der nächsten Nachricht mitliest und muss sich die merken. Ausser das Protokoll ist strikt Halbduplex, dann kommt man damit aus nur sicherzustellen das man eine Nachricht komplett hat bevor man sie verarbeitet.

Un das mit den kompletten Nachrichten sicherzustellen gibt es im Grunde zwei Verfahren: Man sendet am Anfang die Länge der Nachricht (in einer festen Länge kodiert) damit der Empfänger weiss wieviele Bytes er lesen muss, oder man beendet eine Nachricht mit einem Byte oder einer Bytefolge die garantiert nicht innerhalb der Nachricht vorkommen kann. Beispielsweise ein Zeilenendezeichen oder ein Nullbyte. Dann weiss der Empfänger das er solange lesen muss, bis so ein Byte oder eine Bytefolge angekommen ist bevor er die Nachricht weiterverarbeiten kann.

Schau Dir mal den Style Guide for Python Code an was die Namenschreibweise und einiges andere an Konventionen bei Python angeht.

Auf Modulebene sollte eigentlich kein Code stehen, ausser welcher der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm gehört in eine Funktion. Die heisst üblicherweise `main()` und wird mit folgendem Idiom aufgerufen:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dann kann man das Modul importieren ohne das automatisch das Hauptprogramm abläuft. Zum Beispiel um Funktionen in einer Shell zu testen oder automatisiert testen zu lassen, oder in anderen Modulen wiederzuverwenden. Und einige Werkzeuge die mit Python-Modulen arbeiten, erwarten auch das man die ohne Nebeneffekte importieren kann.

Wenn man Wahrheitswerte meint sollte man keine Zahlen benutzen. Also ``while True:`` statt ``while 1:``.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Ich habe sowas vermutet ;-)

Aber danke, für die konstruktive Kritik.
Ich werde mich heute noch einmal dransetzten.

Würden sich wohl auch eine handvoll Leute bereiterklären, dem "Server" ordentlich zuzusetzen? ;-)
Der Server würde auf einem Raspberry laufen und in einem separaten Netzwerk.

Client kann selbst gebaut werden. Gern auch mit Negativbeispielen, die den Server ggf. lahmlegen,
um mir danach ein paar Tips zu geben, die Sicherheitslücke zu schließen? ;)

Vielen Dank
Antworten