|
| 1 | +OpenSSH keylog file |
| 2 | +-------------------- |
| 3 | + |
| 4 | +WARNING: Do not enable in production environments. This exposes session secrets. |
| 5 | + |
| 6 | +Since OpenSSH is using Diffie-Hellman methode to comunicate on public network |
| 7 | +it's necessry to log somewhere the session cookie and the computed shared_secret |
| 8 | +to be able to decrypt traffic |
| 9 | + |
| 10 | +Note that TLS 1.2+ is using the same methode (generate an SSLKEYLOGFILE) for |
| 11 | +traffic decryption. |
| 12 | + |
| 13 | +Since TLS1.2+ using ECDHE_* cipher the `private_key` is not enough to decrypt |
| 14 | +trafic because the `session_keys` are needed and are not derived from the |
| 15 | +private_key |
| 16 | + |
| 17 | +In 2025, Operating Systems TLS backend (GnuTLS or OpenSSL,...) usually provide |
| 18 | +a `keylog file` feature to help users dumping necessary informations to decrypt |
| 19 | +TLS traffic |
| 20 | + |
| 21 | +As OpenSSH seems to use session keys approximatively the same way TLS 1.2+ is |
| 22 | +doing, the purpose of this feature is to add a keylog file feature to SSH client |
| 23 | +so anyone connecting a remote SSH server would be able to retrieve the computed |
| 24 | +shared_secret (or to derive it from the session private_key) |
| 25 | + |
| 26 | + |
| 27 | +KEYLOG file format |
| 28 | +------------------ |
| 29 | + |
| 30 | +One of the main goal of this feature is to be able to decrypt live SSH traffic |
| 31 | +with a tool like Wireshark / Tshark (https://gitlab.com/wireshark/wireshark). |
| 32 | + |
| 33 | +So the keylog file format will be the one described in Wireshark / Tshark ssh |
| 34 | +packet dissector (in wireshark/epan/dissectors/packet-ssh.c): |
| 35 | + |
| 36 | +Extract from Wireshark SSH dissector packet-ssh.c |
| 37 | + |
| 38 | + /* File format: each line follows the format "<cookie> <type> <key>". |
| 39 | + * <cookie> is the hex-encoded (client or server) 16 bytes cookie |
| 40 | + * (32 characters) found in the SSH_MSG_KEXINIT of the endpoint whose |
| 41 | + * private random is disclosed. |
| 42 | + * <type> is either SHARED_SECRET or PRIVATE_KEY depending on the |
| 43 | + * type of key provided. PRIVAT_KEY is only supported for DH, |
| 44 | + * DH group exchange, and ECDH (including Curve25519) key exchanges. |
| 45 | + * <key> is the private random number that is used to generate the DH |
| 46 | + * negotiation (length depends on algorithm). In RFC4253 it is called |
| 47 | + * x for the client and y for the server. |
| 48 | + * For openssh and DH group exchange, it can be retrieved using |
| 49 | + * DH_get0_key(kex->dh, NULL, &server_random) |
| 50 | + * for groupN in file kexdh.c function kex_dh_compute_key |
| 51 | + * for custom group in file kexgexs.c function input_kex_dh_gex_init |
| 52 | + * For openssh and curve25519, it can be found in function kex_c25519_enc |
| 53 | + * in variable server_key. One may also provide the shared secret |
| 54 | + * directly if <type> is set to SHARED_SECRET. |
| 55 | + * |
| 56 | + * Example: |
| 57 | + * 90d886612f9c35903db5bb30d11f23c2 PRIVATE_KEY DEF830C22F6C927E31972FFB20B46C96D0A5F2D5E7BE5A3A8804D6BFC431619ED10AF589EEDFF4750DEA00EFD7AFDB814B6F3528729692B1F2482041521AE9DC |
| 58 | + */ |
| 59 | + |
| 60 | + |
| 61 | +Formatting note: |
| 62 | +---------------- |
| 63 | + |
| 64 | +As OpenSSH is computing the SHARED_SECRET for us, it's easier to log this session |
| 65 | +SHARED_SECRET in replacement of the session PRIVATE_KEY which will add complexity |
| 66 | +as you will have to compute the SHARED_SECRET yourself |
| 67 | + |
| 68 | +So the format of the keylog file is: |
| 69 | + |
| 70 | +<cookie> SHARED_SECRET <shared_secret> |
| 71 | +Example: |
| 72 | +a73e1ead2159740ae07a394b402e4acd SHARED_SECRET 2adf18b3dd7eb58f6d14b8256b9c8ee394e2f0d7b0c8b06fbcbc1ad41c331042 |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +Keylog file first goal: |
| 77 | +----------------------- |
| 78 | + |
| 79 | +The first goal of adding this support to OpenSSH is to be able to do live traffic |
| 80 | +decryption without any MIM (Man-In-the-Middle) proxy using a capture tool like Tshark |
| 81 | +which is Wireshark command line tool using a command like (output in a tcpdump style): |
| 82 | + |
| 83 | + tshark -i <interface> \ |
| 84 | + -n -l -t ad \ |
| 85 | + -o ssh.keylog_file:/path/to/keylog_file.log \ |
| 86 | + -o ssh.desegment_buffers:TRUE \ |
| 87 | + -o tcp.desegment_tcp_streams:TRUE \ |
| 88 | + -f 'host <src_host> and host <dst_host> and port 22' \ |
| 89 | + -T fields \ |
| 90 | + -e frame.number \ |
| 91 | + -e frame.time_relative \ |
| 92 | + -e ip.src \ |
| 93 | + -e ip.dst \ |
| 94 | + -e tcp.srcport \ |
| 95 | + -e tcp.dstport \ |
| 96 | + -e tcp.len \ |
| 97 | + -e _ws.col.Protocol \ |
| 98 | + -e _ws.col.Info \ |
| 99 | + -e ssh.cookie \ |
| 100 | + -e ssh.payload |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | +How to use keylog file feature of OpenSSH ? |
| 105 | +--------------------------------------------- |
| 106 | + |
| 107 | +Simply export to environment file path and file name in SSHKEYLOGFILE variable |
| 108 | + |
| 109 | +Example: |
| 110 | + export SSHKEYLOGFILE=~/ssh_keylog.log |
| 111 | + ssh user@host |
| 112 | + |
| 113 | +And during session, you can see the cookie and the shared_secret logged to file |
| 114 | +~/ssh_keylog.log |
| 115 | + |
| 116 | +For example: |
| 117 | + cat ~/ssh_keylog.log |
| 118 | + 86f79664772735ddec07368663614c2c SHARED_SECRET 01bc538348137ed3a7fe2e720d00b6f66b06280da58a82c33a299b70f5d0f523 |
| 119 | + 79947161e967ab0200403669c94f1548 SHARED_SECRET f18497c66ec6993a1d769734b657a0cd2dd19659684097e1af606fabef039a32 |
| 120 | + 3122e0b88007d52e45593c21d7c2d104 SHARED_SECRET d19a874efd715276022c16e6b7b3a8777f993be4c8323d387e3fc844868de75b |
| 121 | + ... |
| 122 | + |
| 123 | + |
| 124 | +OpenSSH rekeying: |
| 125 | +----------------- |
| 126 | + |
| 127 | +When a "rekey" occurs, the new cookie and the new shared_secret are logged in the |
| 128 | +keylog file. |
| 129 | +It can be easily tested sith a command like: |
| 130 | + |
| 131 | + ssh -F none -vvv -o RekeyLimit=1K $USER@localhost ls / |
| 132 | + # run a `tail -f` on keylog file from another terminal to see key logging in progress |
| 133 | + |
| 134 | + |
| 135 | + |
| 136 | +Extended keylog file: |
| 137 | +--------------------- |
| 138 | + |
| 139 | +For those who need more detailed informations, you can also set SSHEXTKEYLOGFILE |
| 140 | +environment variable which produce a mode detailed file using format: |
| 141 | + |
| 142 | +<cookie> <optionnal 'REKEY' term> KEX_ALG <kex algo> SHARED_SECRET <shared_secret> |
| 143 | + |
| 144 | +For example: |
| 145 | + cat ~/ssh_ext_keylog.log |
| 146 | + 4f1a61641d8864b1a941531f9638c68b KEX_ALG sntrup761x25519-sha512 SHARED_SECRET a6de23b55f6462494385e4f891035dee45ed1b7f4283e3929aa7bd362ecd295a |
| 147 | + 54b9ba5193a4a8f0d01cf095a2b20d3b REKEY KEX_ALG sntrup761x25519-sha512 SHARED_SECRET a17e5303f10e753b94527fe9463cc41d914be2a8339d65137afa86ad6c99ef65 |
| 148 | + 8a3e42e48f007a22af4b929988048e43 REKEY KEX_ALG sntrup761x25519-sha512 SHARED_SECRET 59753eebb9db89657f5add6fdc063fedaab8fa33a330031b6f2adf76f97f6267 |
| 149 | + |
| 150 | + |
| 151 | +How to use extended keylog file feature of OpenSSH ? |
| 152 | +----------------------------------------------------- |
| 153 | + |
| 154 | +Simply export to environment file path and file name in SSHEXTKEYLOGFILE variable |
| 155 | + |
| 156 | +Example: |
| 157 | + export SSHEXTKEYLOGFILE=~/ssh_ext_keylog.log |
| 158 | + ssh user@host |
| 159 | + |
| 160 | +And during session, you can see the cookie, algo, rekey and the shared_secret logged to file |
| 161 | +~/ssh_ext_keylog.log |
| 162 | + |
| 163 | + |
| 164 | +DEBUG: |
| 165 | +------ |
| 166 | + |
| 167 | +You can enable DEBUG_KEX_COOKIE to validate that the cookie stored in keylog file is OK |
| 168 | + |
| 169 | +To enable this debug flag, do: |
| 170 | + ./configure CFLAGS="-DDEBUG_KEX_COOKIE" |
| 171 | + make |
| 172 | + |
| 173 | +NOTES: |
| 174 | +------ |
| 175 | + |
| 176 | +This feature log the shared_secret for algo: |
| 177 | + |
| 178 | +DH: |
| 179 | +- diffie-hellman-group1-sha1 |
| 180 | +- diffie-hellman-group14-sha1 |
| 181 | +- diffie-hellman-group14-sha256 |
| 182 | +- diffie-hellman-group16-sha512 |
| 183 | +- diffie-hellman-group18-sha512 |
| 184 | +- diffie-hellman-group-exchange-sha1 |
| 185 | +- diffie-hellman-group-exchange-sha256 |
| 186 | + |
| 187 | +ECDH: |
| 188 | +- ecdh-sha2-nistp256 |
| 189 | +- ecdh-sha2-nistp384 |
| 190 | +- ecdh-sha2-nistp521 |
| 191 | + |
| 192 | +ED25519 / KEMs |
| 193 | +- curve25519-sha256 |
| 194 | +- sntrup761x25519-sha512 |
| 195 | +- mlkem768x25519-sha256 |
| 196 | + |
| 197 | +It can be tested with command: (here algo is curve25519-sha256) |
| 198 | +ssh -F none -o KexAlgorithms=curve25519-sha256 -o RekeyLimit=1K ${USER}@localhost ls / 2>&1 >/dev/null |
| 199 | + |
| 200 | + |
| 201 | +DEVELOPEMENT NOTES: |
| 202 | +------------------- |
| 203 | + |
| 204 | +To enable this feature, the following files where patched: |
| 205 | + |
| 206 | +kex.h: modifying 'kex' structure to store session cookie in kex structure |
| 207 | + declaring helper action: sshlog_keylog_file |
| 208 | + |
| 209 | +kex.c: adding helper action: sshlog_keylog_file |
| 210 | + copying the cookie to 'kex' structure |
| 211 | + |
| 212 | +kexc25519.c: modifying kexc25519_shared_key_ext to take 'kex' structure and to call sshlog_keylog_file |
| 213 | + modifying all calls to kexc25519_shared_key_ext (adding 'kex' structure) |
| 214 | + adding skip logging for hybrid KEMs like sntrup761x25519 and mlkem768x25519 |
| 215 | + calling function sshlog_keylog_file in kexc25519_shared_key_ext for algo curve25519-sha256 |
| 216 | + |
| 217 | +kexsntrup761x25519.c: calling function sshlog_keylog_file in kex_kem_sntrup761x25519_dec |
| 218 | + |
| 219 | +kexmlkem768x25519.c : calling function sshlog_keylog_file in kex_kem_mlkem768x25519_dec |
| 220 | + |
| 221 | +kexdh.c: calling function sshlog_keylog_file in kex_dh_compute_key |
| 222 | + |
| 223 | +kexecdh.c: calling function sshlog_keylog_file in kex_ecdh_dec_key_group |
| 224 | + |
| 225 | + |
| 226 | +WARNING: |
| 227 | +-------- |
| 228 | + |
| 229 | +Do not enable in production environments. This exposes session secrets. |
| 230 | + |
| 231 | + |
| 232 | +----------------------------------------------------------------------- |
0 commit comments