Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Tutorials und Kurse (https://www.delphipraxis.net/36-tutorials-und-kurse/)
-   -   SSL/TLS-Kommunikation aus Indy 10 mit Wireshark entschlüsseln (https://www.delphipraxis.net/199976-ssl-tls-kommunikation-aus-indy-10-mit-wireshark-entschluesseln.html)

Codehunter 7. Mär 2019 09:19

SSL/TLS-Kommunikation aus Indy 10 mit Wireshark entschlüsseln
 
Hallo!

Aus ursprünglich einer Frage von mir im Netzwerke-Board ist ein kleines Projekt entstanden.

Wenn man mittels Indy mit externen Diensten Daten austauscht, hat man es heutzutage oftmals mit SSL/TLS-gesicherten Verbindungen zu tun. Gerade beim Debugging kann das ziemlich hinderlich sein, wenn der Server eine Anfrage abweist und man nur sehr umständlich heraus findet, woran es scheitert.

Die meisten modernen Webbrowser unterstützen das Schreiben einer NSS-Datei. Dank dessen konnte ich verschlüsselte Verbindungen mittels Wireshark einsehen. Beispielsweise das Projekt FMC wurde dadurch überhaupt erst möglich.

Nun wollte ich gerne mit eigenen HTTP-Clienten ebenso verfahren und eine solche NSS-Datei erstellen. Das erwies sich als erstaunlich schwierig, weil es in der von mir verwendeten Indy-10-Bibliothek einen Bug gab.

Nachdem das aus der Welt geschafft war, ist das Erstellen einer NSS-Datei mit Indy eigentlich ganz einfach. Auf das Grundgerüst eines HTTPS-Clients gehe ich hier nicht weiter ein, dafür gibt es bereits Tutorials. Letzten Endes hat man eine TIdSSLIOHandlerSocketOpenSSL-Instanz. Diese verfügt über ein Ereignis namens OnStatusInfoEx. Dieses Ereignis verwenden wir für unser SSL-Keylog.

Delphi-Quellcode:
uses
  IdSSLOpenSSL, IdSSLOpenSSLHeaders;
const
  FNSSFilePath = 'C:\Log\SSLKEYLOG.LOG';
var
  FLastNSSLine: AnsiString;

procedure TForm1.DoSSLStatusInfoEx(ASender: TObject;
  const AsslSocket: PSSL; const AWhere, Aret: Integer; const AType,
  AMsg: string);
var
  LMasterByte, LRandomByte: Byte;
  LRandomChar: AnsiChar;
  LMasterSecret, LClientRandom, LLogLine: AnsiString;
  LMode: Word;
  LFileStream: TFileStream;
  LS3: Pssl3_state;
begin
  if AWhere = SSL_CB_HANDSHAKE_DONE then begin
    LS3 := AsslSocket^.s3;
    Dec(PByte(LS3)); // <-- Bug in IdSSLOpenSSLHeaders.pas Deklaration write_mac_secret : array [0..EVP_MAX_MD_SIZE] of TIdAnsiChar; // muss [0..EVP_MAX_MD_SIZE - 1] lauten
    for LRandomChar in LS3.client_random do begin
      LRandomByte := Ord(LRandomChar);
      LClientRandom := LClientRandom + AnsiString(Format('%2.2x', [LRandomByte]));
    end;
    for LMasterByte in AsslSocket^.session^.master_key do begin
      LMasterSecret := LMasterSecret + AnsiString(Format('%2.2x', [LMasterByte]));
    end;
    LLogLine := 'CLIENT_RANDOM ' + LClientRandom + ' ' +
                LMasterSecret + sLineBreak;
    if LLogLine <> FLastNSSLine then begin
      LMode := (fmOpenReadWrite or fmShareDenyNone);
      if DirectoryExists(ExtractFilePath(FNSSFilePath)) then begin
         if not FileExists(FNSSFilePath) then begin
          LMode := fmCreate or LMode;
        end;
        LFileStream := TFileStream.Create(FNSSFilePath, LMode);
        try
          LFileStream.Position := LFileStream.Size;
          LFileStream.Write(LLogLine[1], Length(LLogLine));
          Exit;
        finally
          FLastNSSLine := LLogLine;
          FreeAndNil(LFileStream);
        end;
      end;
    end;
  end;
end;
OnStatusInfoEx wird während einer Verbindung mehrfach aufgerufen. Deshalb prüfen wir den Verbindungsstatus mit
Delphi-Quellcode:
if AWhere = SSL_CB_HANDSHAKE_DONE
auf den richtigen Moment. Anschließend erzeugen wir mittels zweier Schleifen aus den beiden Arrays
Delphi-Quellcode:
AsslSocket^.s3.client_random
und
Delphi-Quellcode:
AsslSocket^.session^.master_key
zwei Strings und bauen daraus die neue Zeile für die NSS-Datei zusammen. Diese schaut dann in etwa so aus:
Code:
CLIENT_RANDOM 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
Die SSL-Schlüssel werden jedoch nicht bei jeder weiteren HTTPS-Anfrage, die wir mit unserem Indy-Client absetzen, neu ausgehandelt sondern teilweise wiederverwendet. Damit wir also nicht mehrfach die selben NSS-Einträge schreiben, benutze ich hier einfach eine globale AnsiString-Variable
Delphi-Quellcode:
FLastNSSLine
und prüfe ob diese mit der neuen Zeile identisch ist. Es ist übrigens durchaus wichtig, AnsiStrings zu verwenden und nicht etwa Strings. Dies würde der Compiler auch mit mehreren Implizite-Umwandlung-Warnungen quittieren.

Die NSS-Datei wird mittels Pfadangabe (hier die globale Konstante
Delphi-Quellcode:
FNSSFilePath
an einem bestimmten Platz gespeichert. Anstelle der Konstante sollte man sinnigerweise die Environment-Variable %SSLKEYLOGFILE% auswerten. Diese wird z.B. auch vom Firefox-Browser verwendet. Aber Vorsicht: Firefox und den eigenen Client nicht parallel laufen lassen, weil der Browser die Datei Write-Exclusive öffnet. Wir öffnen sie jedoch Shared-Read-Write, sodass wir zumindest keine anderen Anwendungen blockieren.

Schließlich muss Wireshark noch konfiguriert werden, die soeben erstellte NSS-Datei zur Entschlüsselung von SSL/TLS-Verbindungen zu nutzen.

Der oben schon erwähnte Bug in Indy 10 befand sich in der IdSSLOpenSSLHeaders.pas und wurde am 3. Dezember 2018 in den Indy-Quellen behoben. Daher ist es erforderlich, die Zeile
Delphi-Quellcode:
Dec(PByte(LS3));
zu löschen, wenn man den Indy-10-Quellenstand vom 3.12.2018 oder später verwendet. Delphi 10.3 Rio wurde jedoch noch mit dem fehlerhaften Stand ausgeliefert.


Unterstützt wird aktuell SSLv1 bis SSLv3 sowie TLS 1.0 bis TLS 1.2. Zum jetzigen Zeitpunkt ist TLS 1.3 noch kein Thema, weil dafür größere Umbauten an Indy notwendig sind.

So, das wars dann auch schon. Happy SSL/TLS-Hacking! ;-)

Euer Cody


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:03 Uhr.

Powered by vBulletin® Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2019 by Daniel R. Wolf