Browse Source

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
Rob Hague 5 months ago
parent
commit
85905082db
3 changed files with 31 additions and 1 deletions
  1. 8 0
      CONTRIBUTING.md
  2. 11 1
      src/Renci.SshNet/Session.cs
  3. 12 0
      src/Renci.SshNet/SshNetLoggingConfiguration.cs

+ 8 - 0
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";
+```

+ 11 - 1
src/Renci.SshNet/Session.cs

@@ -320,7 +320,7 @@ namespace Renci.SshNet
         /// Gets the client init message.
         /// </summary>
         /// <value>The client init message.</value>
-        public Message ClientInitMessage { get; private set; }
+        public KeyExchangeInitMessage ClientInitMessage { get; private set; }
 
         /// <summary>
         /// 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();

+ 12 - 0
src/Renci.SshNet/SshNetLoggingConfiguration.cs

@@ -22,5 +22,17 @@ namespace Renci.SshNet
             ThrowHelper.ThrowIfNull(loggerFactory);
             LoggerFactory = loggerFactory;
         }
+
+#if DEBUG
+        /// <summary>
+        /// Gets or sets the path to which to write session secrets which
+        /// Wireshark can read and use to inspect encrypted traffic.
+        /// </summary>
+        /// <remarks>
+        /// To configure in Wireshark, go to Edit -> Preferences -> Protocols
+        /// -> SSH and set the same value for "Key log filename".
+        /// </remarks>
+        public static string? WiresharkKeyLogFilePath { get; set; }
+#endif
     }
 }