![]() |
MD5-Hash // Digest Authenticate
Hallo,
ich habe mal ein wenig mit Asterisk rumgepielt und würde mich jetzt gerne per SIP anmelden. Aber irgendwie schaffe ich es nicht den richtigen Hash zu ermitteln. Folgender (unschöner) Source sollte genau das machen was ich aus den entsprechenden RFC's und OpenSource-SoftPhone's raus gelesen habe:
Delphi-Quellcode:
Hash1 = User:Realm:Password
var
tmp: THash_MD5; test: string; Pass: string; Realm: string; DP: String; UserName: string; Method: string; Uri: string; Nonce: string; HA1: pbytearray; HA2: pbytearray; begin test := LabeledEdit10.Text; UserName := '21702'; DP := ':'; Realm := 'asterisk'; Pass := '20712'; with THash_MD5.Create do try Init; Calc(UserName[1], Length(UserName)); Calc(dp[1], 1); Calc(Realm[1], Length(Realm)); Calc(dp[1], 1); Calc(Pass[1], Length(Pass)); Done; HA1 := Digest; Init; Method := 'REGISTER'; Calc(Method[1], Length(Method)); Calc(dp[1], 1); Uri := '"sip:192.168.1.14"'; Calc(Uri[1], Length(Uri)); Done; HA2 := Digest; Init; Calc(ha1, SizeOf(ha1)); Calc(dp[1], 1); Nonce := '5e6feac9'; Calc(Nonce[1], Length(Nonce)); Calc(dp[1], 1); Calc(ha2, SizeOf(ha2)); Calc(dp[1], 1); LabeledEdit11.Text := DigestStr(TFOrmat_HEXL); finally Free; end; Hash2 = Methode:"URI" Antwort-Hash = Hash1:Nonce:Hash2 Als Antwort-Hash bekomme ich "0123456789abcdeffedcba9876543210" sollte aber "a6ea9f6ccf4c05149c8282c8d41b18fe" sein. Die Werte (Nonce und Hash) habe ich von einem SoftPhone, welches sich erfolgreich angemeldet hat.
Delphi-Quellcode:
Kann mir jemand weiter helfen? Ich habe auch mal die md5.pas von Dimka Maslov ausprobiert. Mit der erhalte ich andere, aber ebenfalls falsche, Werte. Mir ist nicht einmal bewusst wo der fehler liegt, in der Logik oder in meiner Verwendung der MD5-Routinen.
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.107:5060;branch=z9hG4bK-d8754z-465f934b6b5ec75c-1---d8754z-;received=192.168.1.107;rport=5060 From: <sip:21702@192.168.1.14>;tag=d43ed52a To: <sip:21702@192.168.1.14>;tag=as21df2254 Call-ID: MmVkYmRlNDUyZGQyY2JhZjUyNWRkOTJkOTdmNzQ3MWI.@localhost CSeq: 1 REGISTER User-Agent: Asterisk PBX Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="5e6feac9" Content-Length: 0 REGISTER sip:192.168.1.14 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.107:5060;branch=z9hG4bK-d8754z-070fdd727b336e5c-1---d8754z-;rport Max-Forwards: 70 Contact: <sip:21702@192.168.1.107:5060> To: <sip:21702@192.168.1.14> From: <sip:21702@192.168.1.14>;tag=d43ed52a Call-ID: MmVkYmRlNDUyZGQyY2JhZjUyNWRkOTJkOTdmNzQ3MWI.@localhost CSeq: 2 REGISTER Expires: 3600 Allow: INFO, MESSAGE, REFER, NOTIFY, SUBSCRIBE, INVITE, ACK, BYE, CANCEL, OPTIONS User-Agent: AstaUa Authorization: Digest username="21702",realm="asterisk",nonce="5e6feac9",uri="sip:192.168.1.14",response="a6ea9f6ccf4c05149c8282c8d41b18fe",algorithm=MD5 Content-Length: 0 |
Re: MD5-Hash // Digest Authenticate
da kann dir am besten Hagen helfen (negaH), unser Experte darin^^
|
Re: MD5-Hash // Digest Authenticate
kann es sein, dass du Hash(user)+Hash(realm)+Hash(pw) machst, obowlh du Hash(User+realm+pw) machen solltest?
|
Re: MD5-Hash // Digest Authenticate
Ha1 und HA2 sind deklariert -> PByteArray.
1.) beim nachfolgendem .Calc(HA1, SizeOf(HA1)) berechnets du also den Hash über einen Zeiger mit 4 Bytes Länge ? 2.) HA1/HA2 := .Digest; zeigen auf eine interne Datenstruktur = Buffer des Hash Objectes. Schaue dir mal den Inhalt von HA1^ und HA2^ an, du wirst feststellen das sie gleich sind. .Digest gibt dier also den Zugriff über einen Zeiger auf die internen Register der Hash Funktion und nicht die darin enthaltenen Daten als Kopie, das macht .DigestStr(). Nach dem .Init/.Done steht in diesem internen Buffer der Initialisierungswert der Hash Funktion, und rate mal was bei MD5 da drinnen steht nach dem .Done ? Es ist 0123456789abcdeffedcba9876543210 ;) Du arbeitest also mit Referenzen, statt den gewünschten Daten. 3.) die beiden Hash1 und Hash2 sind in HEXL formatiert, du benutzt sie in binärer Repräsentation, eben Zeiger auf Digest des Hashs. 4.) die SIP wird nicht in Anführungsstriche gesetzt 5.) es geht im DEC viel einfacher zu coden ;) Mache es so:
Delphi-Quellcode:
so komme ich auf den Wert den du suchst. Davon mal absehen das man schon versucht hat es einigermaßen sicher zu machen, also kryptographisch betrachtet, ist es immer noch nicht ideal gelösst.
begin
Response := THash_MD5.CalcBinary(THash_MD5.CalcBinary('21702:asterisk:20712', TFormat_HEXL) + ':5e6feac9:' + THash_MD5.CalcBinary('REGISTER:sip:192.168.1.14', TFormat_HEXL), TFormat_HEXL); end; 1.) man sollte einen Salt für die Berechnung von Hash1 vorsehen der mindestens 16 Bytes lang ist, so wie jetzt kann man Rainbow Tabellen anlegen 2.) man sollte den Nonce ebenfalls 16 Bytes vorsehen, und in binär statt HEX dem Hash berechnen 3.) diese Salts sollten in den Daten immer am Anfang stehen, nicht mittendreinnen noch hintendran, wegens Lawineneffekt der Hashfunktion 4.) wenn man den Hash berechnet so sollte man diese Berechnung mehrmals ausführen, immer vom Hash vom Hash vom Hash und das ca. 1000mal. Durch diese iterative Anwednung der "langsammen" Hashfunktionen vervielfacht sich der Aufwand einer Brute Force Attacke. Also wir machen 1x 1000 mal den Hash, der Angreifer aber ~2^64x 1000mal das gleiche um mit 50% Wahrscheinlichkeit zu knacken. Die Länge der Salts/Nonce sollte immer 16 Bytes = 128 Bits nicht unterschreiten. Im Grunde sollten sie exakt so groß sein wie die interne Arbeitsbreite der Hashfunktion, das ist ideal, also nicht weniger und nicht mehr. Damit ist die Wahrscheinlichkeitsverteilung zwischen Salt-Zufallsbits gleich der Arbeitsbreite der Hashfunktion, also 50/50. Somit kann ein Angreifer eben keine Informationen mehr zuordnen zum Salt oder den Daten im resultierenden Hashdigest. KDFs -> Key Derivation Functions arbeiten dann exakt so und sind bei weitem sicherer als obige Methode. Also für Hash1 sollte man eine KDF benutzen, -> THash.KDFx(UserName + ':' + UserPassword, RandomSalt), mal anschauen im Source :), und für Hash2 kann man die bisherige Methode benutzen, und für den Response sollte man wieder eine KDF benutzen. Das sähe so aus KDF(KDF(Username:Userpassword, PasswordSalt) || MGF(MethodName:SIP'), NonceSalt), wobei man im Datnprotokoll Passwordsalt und Noncesalt lesbar mit überträgt. Ok, ich weis das du das nicht beeinflussen kannst ;) ist also nur mal so am Rande eine Analyse eines bestehenden Protokolles. Um das nochmal genauer so erklären. Der 2. hash hat den Aufbau -> REGISTER:sip:IP. Die Anzahl der wählbaren Methoden wird sehr beschränkt sein, zb. REGISTER, UPDATE und CHANGE == 3 Stück. Danach folgt immer SIP also nur eine Möglichkeit. Wir wissen also schonmal REGSITER:sip: + CHANGE:sip: + UPDATE:sip:. Danach folgt eine IP Addresse. Davon dürfen viele mögliche IP Adressen garnicht benutzt werden, zb. 192.xxx.xxx.xxx oder 127:xxx:xxx:xxxx oder 255:xxx:xxx:xxx und meistens wissen wir sogar das ein Benutzer bei T-Online seinen Account hat und somit können wir den IP Bereich nochmals einschränken. Im besten Falle kennen wir als Angreifer sogar diese IP komplett. Ergo: ohne Probleme könnten wir den 2. Hash errechnen als Angreifer, oder wir legen eine Rainbow Tabelle im Vorhinein an mit allen möglichen IP Addressen, das ist dann eine Datenbank in der Größe einer DVD in der wir dann live und online sofort zu einem Hash die IP Addresse auslesen können. Ergo: für den 2. Hash wird es so schnelle Angriffsmöglichkeiten geben das es defakto sinnlos ist darüber in obiger Form einen Hash zu berechnen, man könnte diese Daten gleich unverschlüsselt übertragen. Ich schätz mal das dies auch der Fall ist und der 2. Hash nur als Bestätigung von Serverseite gilt. Wenn nicht dann sollte man eine KDF benutzen. Also man erzeugt einen 16 Bytes langen Zufallswert zieht nun von Zufallwert:REGISTER:sip:IP einen Hash und dann ca. 100mal von dem Zufallswert:Vorgänger-Hash nochmals den hash iterativ. Dann überträgt man den Zufallswert und den letzendlichen Hashwert zum Server. Nun gibts keine Angriffsmöglichkeit mehr denn der Angreifer benötigt nun zu jedem der Zufallswerte eine eigene Rainbow Tabelle, das macht in diesem Falle also 2^128 DVDs mit Rainbow Tabellen, 2^128 Stück rechne das mal aus, ich schätze mal das alle diese DVD übereinander gestapelt durch unsere komplette Galaxie reichen würde ;) Wie man sieht hat so ein popeliger 16 Bytes Zufallswert enorme Konsequenzen. Im Idealfall knackt man die alte Methode sofort weil man die IP des benutzers kennt oder enorm schnell weil man den Provider des User kennt. Im sicheren Falle mit Salt und KDF knackt man es nie da der Aufwand gigantisch ist, eben ein rießen Stapel mit DVDs im Falle einer Rainbow Tabelle, die heutzutage bester Stand der Kryptoanalyse in diesem Falle darstellt. Noch viel wichtiger wäre aber der 1. Hash über Password + Username zu verändern. Denn knackt dies ein Angreifer dann ist die Wahrscheinlichkeit enorm hoch das diese Daten vom Benutzer auch bei seinen anderen Accounts wie Online Banking usw. benutzt werden. Der Angreifer hat damit Zugriff auf noch viel mehr Daten des Benutzers. Also für den 1. Hash sollte man unbedingt eine KDF + Salt benutzen. Gruß Hagen |
Re: MD5-Hash // Digest Authenticate
Danke, Hagen. Da hat mal wieder das Kind in mir zu geschlagen, das Heute lieber mit SIP spielen wollte anstatt sich das benötigte Wissen über deine DEC-Lib an zu eignen.
Im Zweifelsfall ignorieren: Direkt noch mal ein Dank für all deine Post's hier. Ich lese deine Antworten und Kommentare gerne. Sie sind immer Kompetent und lösen nicht nur das angesprochene Problem sondern vermitteln gleich etwas Hintergundinfos. Wenn ich noch einen drauf setzen darf: Solltest du ein Buch schreiben, ich würd's lesen. |
Re: MD5-Hash // Digest Authenticate
Zitat:
|
Re: MD5-Hash // Digest Authenticate
unter:
![]() gibt es einen vollständige, INDY9 basierende SIP Implementation for free... Gruss Hinnack |
Re: MD5-Hash // Digest Authenticate
Die hatte ich auch gefunden aber ich bin zu dem schluß gekommen: Selber machen wird einfacher :-D Dummer Scherz beiseite, mir geht es ja nicht darum zwei, drei Komponenten zu einem SoftPhone zu verbinden. Es ist nur eine Freizeit-Spielerei.
Aber Danke für den Tipp. |
Re: MD5-Hash // Digest Authenticate
er hat aber vieles in INDY gefixt, vielleicht hilft dir das...
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz