From 85905082db58d71768a5e56f9f4cc69d851a031a Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Sun, 4 May 2025 11:38:55 +0200 Subject: [PATCH] Add a debug helper for reading traffic with Wireshark (#1627) Wireshark can already helpfully dissect the initial SSH handshake. When given the session keys, it can also dissect the encrypted traffic for inspection/debugging. This adds a helper in Debug mode to write out that information in the format Wireshark requires. Usage is to set `SshNetLoggingConfiguration.WiresharkKeyLogFilePath` before connecting, and supply the same value to Wireshark in Edit -> Preferences -> Protocols -> SSH -> "Key log filename". The description of the format is at https://wiki.wireshark.org/SSH#key-log-format --- CONTRIBUTING.md | 8 ++++++++ src/Renci.SshNet/Session.cs | 12 +++++++++++- src/Renci.SshNet/SshNetLoggingConfiguration.cs | 12 ++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db38582f..af7ba3c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,3 +41,11 @@ The tests always log to the console. See the [Logging documentation](https://ssh ### Wireshark Wireshark is able to dissect initial connection packets, such as key exchange, before encryption happens. Enter "ssh" as the display filter. See https://wiki.wireshark.org/SSH.md for more information. + +The Debug build of SSH.NET has helpers to also allow dissection of the encrypted traffic by dumping the session keys in a format that Wireshark understands. Set a value for `SshNetLoggingConfiguration.WiresharkKeyLogFilePath` before connecting, and supply the same value to Wireshark in Edit -> Preferences -> Protocols -> SSH -> "Key log filename". + +```c# +using Renci.SshNet; + +SshNetLoggingConfiguration.WiresharkKeyLogFilePath = @"C:\tmp\sshkeylogfile.txt"; +``` diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index c5356db8..800c0d5d 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -320,7 +320,7 @@ namespace Renci.SshNet /// Gets the client init message. /// /// The client init message. - public Message ClientInitMessage { get; private set; } + public KeyExchangeInitMessage ClientInitMessage { get; private set; } /// /// Gets the server version string. @@ -1582,6 +1582,16 @@ namespace Renci.SshNet _clientCompression = _keyExchange.CreateCompressor(); _serverDecompression = _keyExchange.CreateDecompressor(); +#if DEBUG + if (SshNetLoggingConfiguration.WiresharkKeyLogFilePath is string path + && _keyExchange is KeyExchange kex) + { + System.IO.File.AppendAllText( + path, + $"{ToHex(ClientInitMessage.Cookie)} SHARED_SECRET {ToHex(kex.SharedKey)}{Environment.NewLine}"); + } +#endif + // Dispose of old KeyExchange object as it is no longer needed. _keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; _keyExchange.Dispose(); diff --git a/src/Renci.SshNet/SshNetLoggingConfiguration.cs b/src/Renci.SshNet/SshNetLoggingConfiguration.cs index 4add75c9..fa8581b3 100644 --- a/src/Renci.SshNet/SshNetLoggingConfiguration.cs +++ b/src/Renci.SshNet/SshNetLoggingConfiguration.cs @@ -22,5 +22,17 @@ namespace Renci.SshNet ThrowHelper.ThrowIfNull(loggerFactory); LoggerFactory = loggerFactory; } + +#if DEBUG + /// + /// Gets or sets the path to which to write session secrets which + /// Wireshark can read and use to inspect encrypted traffic. + /// + /// + /// To configure in Wireshark, go to Edit -> Preferences -> Protocols + /// -> SSH and set the same value for "Key log filename". + /// + public static string? WiresharkKeyLogFilePath { get; set; } +#endif } }