ooooooooooooo o8o o8o 8' 888 `8 `"' `"' 888 oooo ooo. .oo. .oo. oooo ooo. .oo. .oooooooo 888 `888 `888P"Y88bP"Y88b `888 `888P"Y88b 888' `88b 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 `88bod8P' o888o o888o o888o o888o o888o o888o o888o o888o `8oooooo. d" YD "Y88888P' .o. . . oooo .888. .o8 .o8 `888 .8"888. .o888oo .o888oo .oooo. .ooooo. 888 oooo .oooo.o .8' `888. 888 888 `P )88b d88' `"Y8 888 .8P' d88( "8 .88ooo8888. 888 888 .oP"888 888 888888. `"Y88b. .8' `888. 888 . 888 . d8( 888 888 .o8 888 `88b. o. )88b o88o o8888o "888" "888" `Y888""8o `Y8bod8P' o888o o888o 8""888P' ________________________________________________________________________________ Vor geschätzten drei Monaten las ich während eines kurzen Kontaktes mit Ruby on Rails in einem Bugreport zum ersten Mal von "timing attacks". Es handelt sich dabei um einen Exploit, der die bei manchen Stringvergleichsfunktionen je nach Eingangsdaten unterschiedliche Laufzeit ausnutzt. ---[ code type:python ]--------------------------------------------------------- def vulnequal(first,second): if len(first) != len(second): return False for i in range(len(first)): if first[i] != second[i]: return False return True ---[ /code ]-------------------------------------------------------------------- Wenn man Beispielsweise diese Funktion die Strings "foo" und "bar" vergleichen lässt, so arbeitet sie kürzer, als bei einem Vergleich von "bar" und "baz": +----------------------------------------------------------------------------+ | Laufzeit der oben dokumentierten Funktion vulnequal bei 1.000.000 Aufrufen | +----------------------------------------------------------------------------+ | Argumente | Laufzeit [s] | Normiert | +----------------------------------------------------------------------------+ | 'xy', 'bar' | 0.2954 | 0.3552 | | 'foo','bar' | 0.8317 | 1 | | 'boo','baz' | 0.9824 | 1.1812 | | 'bar','baz' | 1.1165 | 1.3424 | +----------------------------------------------------------------------------+ Mir stellte sich daraufhin die Frage, wie aufwendig es ist, eine Lücke dieser Art in einem realen Service auszunutzen, bei dem ein Großteil der Latenz dem Netzwerk und eventuellen Festplatten/Datenbankzugriffen geschuldet ist. Deshalb bastelte ich mir ein kleines Script, dass keinen Anspruch auf Realitätsnähe erhebt, es aber schon erlaubt, den Einfluss des Netzwerks zu erforschen. ---[ code type:python ]--------------------------------------------------------- mport SocketServer as sserv import sys class vulnserver(sserv.BaseRequestHandler): key = sys.argv[1] def handle(self): while True: try: login = self.request.recv(1024) if vulnequal(self.key,login): self.request.send("success\n") else: self.request.send("fail\n") except: break server = sserv.TCPServer(("",50542), vulnserver) server.serve_forever() ---[ /code ]-------------------------------------------------------------------- 100.000 Anfragen benötigten nun ~380s. Dabei läuft die Vergleichsfunktion nur 0.02954s. Nun kam ich auf die Idee, nur die Zeitdifferenz zwischen Eintreffen des ACK's und der Antwort zu messen. Bei einer mit Wireshark mitgesnifften Anfrage betrug diese 0.659ms, die Zeit zwischen Anfrage und Antwort betrug hingegen 2.165ms. Leider wird nur bei der ersten Anfrage ACK und Antwort getrennt gesendet. Um sinnvoll weiterbasteln zu können, bräuchte ich nun ein solides Wissen über gebräuchliche TCP-Stacks. Cool, schon der zweite Blogeintrag endet erbärmlich.