websocat icon indicating copy to clipboard operation
websocat copied to clipboard

SSLKEYLOGFILE support to be able to decrypt TLS

Open nbanb opened this issue 9 months ago • 14 comments

Hi @vi

Hope you're doing well !

Except a mistake from me (highly possible), I haven't seen support of SSLKEYLOGFILE which is required to decrypt TLSv1.2+ traffic using Elliptic Curves like cipher ECDHE_*

What I expect is to be able to do:

export WS_SSLKEYLOGFILE=~/websocat-tls-keys.log SSL_CERT_FILE=/path/to/my/ca/cert websocat wss://...

And to catpure traffic in the mean time:

sudo tcpdump -nnei any -vvvttttXXX 'host <server_ip> and host <client_ip> and port <api_port>' -w ~/api-traffic.pcap

And after I can pass the 2 files ~/websocat-tls-keys.log and ~/api-traffic.pcap to tshark or wireshark to decrypt TLS stream.

Does such feature exist in websocat ? If no and if it's been added in the future, it could be nice (sometimes required) to customize the name of the SSLKEYLOGFILE ENV variable like $WS_SSLKEYLOGFILE . Why ? for example curl match from ENV the variable $SSLKEYLOGFILE but when using curl and websocat in the same script, it would be better to be able to use a custom ENV variable for websocat to get 2 distinct files: 1 for curl SSLKEYLOGFILE and 1 for websocat SSLKEYLOGFILE.

Thanks Kind regards nbanba

nbanb avatar Apr 18 '25 08:04 nbanb

Currently SSLKEYLOGFILE is not recognized neither in Websocat1 nor in Websocat4.

I'll consider adding SSLKEYLOGFILE support in Websocat4 when I'll be extending TLS features in it.


Currently as a workaround you can use an LD_PRELOAD tool combined with external tool for TLS:

$ LD_PRELOAD=/opt/libsslkeylog.so SSLKEYLOGFILE=/tmp/mykeylog /opt/websocat -b --ws-c-uri=ws://ws.vi-server.org/mirror/ - ws-c:cmd:'socat - ssl:ws.vi-server.org:443'
123
123
qwerty
qwerty
$ cat /tmp/mykeylog-20250419073636_25514
2025-04-19T07:36:36Z 192.236.209.31:443 192.168.22.7:42208 ws.vi-server.org 1301 ...

vi avatar Apr 19 '25 07:04 vi

Hi @vi

Thanks for answer and the workarround.

Having a look at the rust code show that websocat is using native-tls which currently don't support SSLKEYLOGFILE natively as it seems to be a wrapper on the TLS backend of the operating system. I think patching native-tls would be mandatory to add SSLKEYLOGFILE support in the actual code and will only work on OS using openssl >1.1.1 TLS backend.

Another way could be to rewrite some part of websocat with rustls which seems to natively support SSLKEYLOGFILE.

Don't know if there is a better way to proceed..

Kind regards nbanba

nbanb avatar Apr 19 '25 08:04 nbanb

There is some native support of rustls in Websocat4 (not by default).

You can build it with appropriate Cargo features and try.

vi avatar Apr 19 '25 08:04 vi

Ok I will try Hope all features of websocat version 1 I'm using in my code would be available in websocat4 Going back here after testing Thanks again ! nbanba

nbanb avatar Apr 19 '25 09:04 nbanb

Websocat1 features that are actually used by users are a priority for porting to Websocat4. You can put reactions in #276 to vote feature you use to be available in Websocat4 sooner.

vi avatar Apr 19 '25 12:04 vi

Quickly added SSLKEYLOGFILE support to Websocat4's rustls connector:

websocat4$ SSLKEYLOGFILE=abcde cargo run  --no-default-features -F rustls -- --enable-sslkeylog wss://ws.vi-server.org/mirror/
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target/debug/websocat --enable-sslkeylog 'wss://ws.vi-server.org/mirror/'`
1234
1234
websocat4$ cat abcde
CLIENT_HANDSHAKE_TRAFFIC_SECRET ... ...
SERVER_HANDSHAKE_TRAFFIC_SECRET ... ...
CLIENT_TRAFFIC_SECRET_0 ... ...
SERVER_TRAFFIC_SECRET_0 ... ...
EXPORTER_SECRET ... ...

Note that server side of TLS is not implemented in Websocat4 at all so far.

vi avatar Apr 19 '25 12:04 vi

Hi @vi

Very nice job ! You're always here supporting websocat + trying to troubleshoot user's issues, and your open-source mindset is really appreciable, huge THANKS !

I will test building websocat4 tomorrow (my time is CET (FR)). If all websocat overlays/functions I'm currently using in my BASH library code are actually available, stable and ~transparent using websocat4, I will concider migrating the library dependencies from websocat1 to websocat4... I think it won't be as easy as the target API addressed by the BASH library I maintien is highly deployed (~10M) in end user's routers in Europa. ' Be sure I understand that supporting websocat4 in replacement of websocat1 will be a requirement in a close future and so, I'm starting to work on it.

I'm coming back here after testing websocat4 and it's keylog file new feature 🙂 😉

Thanks again for the effort and your continuous support,

Kind regards nbanba

nbanb avatar Apr 19 '25 18:04 nbanb

There are may be subtle differences between Websocat1 and Websocat4 even with features that are supported in Websocat4 already.

You can post here what combinations of features you use and why and maybe I'll advice whether it is worth to migrate to Websocat4 or better wait.


Be sure I understand that supporting websocat4 in replacement of websocat1 will be a requirement in a close future and so, I'm starting to work on it.

There may be further releases of Websocat1 (e.g. to update embedded OpenSSL), but significant new features are not planned for Websocat1.

Pull requests may still be merged to Websocat1; high-priority security fixes also to be provided to Websocat1 if needed.

If some easy (or at least easy-seeming) feature is missing in Websocat4, but prevents some important use case / deployment and is cumbersome to work around, a dedicated Github issue may be opened about the lack of that feature.

vi avatar Apr 19 '25 20:04 vi

Hi @vi

Thanks for answer and help

You can post here what combinations of features you use and why and maybe I'll advice whether it is worth to migrate to Websocat4 or better wait.

I'm using OpenSSL PROBE to pass my CA certificate bundle to websocat (in code rust code: #[cfg(feature = "openssl-probe")] to retrieve CERT from ENV )

cmdline1:

SSL_CERT_FILE=/dev/shm/fbx-wsul-cacert websocat -H "X-Fbx-App-Auth: $_SESSION_TOKEN" --origin https://fbx.****** --protocol "chat, superchat" -v --ping-interval 10 --no-close --buffer-size 524800 --text --base64 --binary-prefix B tcp-listen:127.0.0.1:2010 wss://

cmdline2:

SSL_CERT_FILE=/dev/shm/fbx-cacert-wsbgmon websocat  -H "X-Fbx-App-Auth: $_SESSION_TOKEN"  --origin https://fbx.***** --protocol "chat, superchat" -v --text --no-close --ping-interval 10 tcp-listen:127.0.0.1:3009 wss://

cmdline3:

SSL_CERT_FILE=/dev/shm/fbx-cacert-ws websocat -H "X-Fbx-App-Auth: $_SESSION_TOKEN" --origin https://fbx.***** --protocol "chat, superchat" -v -E --binary --ping-interval 30 tcp-listen:127.0.0.1:5900 wss://

cmdline4:

stty raw inlcr -igncr opost -echo; SSL_CERT_FILE=/dev/shm/fbx-cacert-ws websocat -H "X-Fbx-App-Auth: $_SESSION_TOKEN" --origin https://fbx.***** --protocol "chat, superchat" -v -E --binary --ping-interval 10 --byte-to-exit-on 11 exit_on_specific_byte:stdio: exit_on_specific_byte:wss://

And all form discussed together in the end of last year in issue #20, for example any combinaison of :
lengthprefixed: or --text-prefix/--binary-prefix/--ping-interval/--base64

for example using forms like:

printf '\x00\x00\x00\x14BI am binary message\x00\x00\x00\x12TI am text message' | /opt/websocat lengthprefixed:- -b ws://127.0.0.1:1234 --async-stdio --binary-prefix=B --text-prefix=T

But f I remember well, those overlays were not available in websocat4 in end of December 2024 .

Kind regards nbanba

nbanb avatar Apr 20 '25 08:04 nbanb

--ping-interval 10

Outgoing pings are not yet implemented in Websocat4. And Websocat1's implementation have some issues when there is a lot of traffic / congestion. It that can lead to abrupt disconnections.

exit_on_specific_byte:

Not yet implemented, but can be added reasonably easily.

exit_on_specific_byte:stdio: exit_on_specific_byte:wss://

Do you really need to trigger exit from both directions? I expected exit_on_specific_byte: to be on keyboard side, not in display one.

--text-prefix/--binary-prefix/--base64

Not yet implemented.

But lengthprefixed: is already implemented in a significantly extended form: in addition for packet length you can now also set flags to switch between text and binary mode (can substitute --text-prefix/--binary-prefix somewhat), allows to customize close message or send outgoing pings/pongs and manually split message in chunks.

lengthprefixed: is now a primary interface to craft arbitrary messages and (together with mock_streamsocket_:) serves for integration tests in Websocat.


It is also somewhat easier to insert custom, external processing steps with --filter / --filter-reverse. Specifying an echo server is a no-op filter, echo server with additional steps makes a filter.

External filter example imitating `exit_on_specific_byte:`
//! ```cargo
//! [dependencies]
//! tokio = { version = "1.44.2", features = ["macros", "rt", "net", "io-util"] }
//! ```

use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    net::TcpListener,
};

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let ss = TcpListener::bind("127.0.0.1:1555").await.unwrap();
    let byte_to_exit_on = b'Q';
    while let Ok((mut cs, _)) = ss.accept().await {
        tokio::spawn(async move {
            let mut buf = [0u8; 4096];
            loop {
                let Ok(n) = cs.read(&mut buf).await else {
                    break;
                };
                let buf = &buf[..n];
                if let Some(p) = buf.iter().position(|&x| x == byte_to_exit_on) {
                    let _ = cs.write_all(&buf[..p]).await;
                    break;
                } else {
                    if cs.write_all(buf).await.is_err() {
                        break;
                    }
                }
            }
        });
    }
}
websocat4 -b stdio: ws://127.0.0.1:1234 --filter=tcp:127.0.0.1:1555

--async-stdio

This specific flag is not yet implemented, but you can approximate it with using async-fd:0 instead of stdio:.

vi avatar Apr 20 '25 09:04 vi

Hi @vi

Thanks again for answer and help

I did forked websocat repository inclunding websocat4 branch to have a deeper look in it.

Do you really need to trigger exit from both directions? I expected exit_on_specific_byte: to be on keyboard side, not in display one.

Right at the moment there are some VM scripts which are able to logout + disconnect their own serial console websocket sessions when some particular commands are detected. (It's not one of the main features and could be easily deactivated until exit_on_specific_byte: issdued by a connected client keep working on keyboard/console sessions), but it's a feature I wouldn't remove in the future

Kind regards nbanba

nbanb avatar Apr 20 '25 11:04 nbanb

logout + disconnect ... when some particular commands are detected

Note: if this is a security feature then it may be not well-designed; It should allow specific commands, not deny specific commands.

vi avatar Apr 20 '25 12:04 vi

Hi @vi

logout + disconnect ... when some particular commands are detected

Note: if this is a security feature then it may be not well-designed; It should allow specific commands, not deny specific commands.

Sure it should be better designed but today it's only deny very few command (less than 20) like shutdown, poweroff, rm -rf /, dd of=/dev/vd*, dd of=/dev/mapper/*, lvremove, pvremove, mkinitrd + /boot modifications...

Only the most destructive command are denied and force websocket disconnection (kick user), and this system is really not perfect as confirmed linux users like us could easily workarround it, but it protect beginners from unrecoverable errors

It seems to be difficult (but not impossible) to invert the process and to allow thousand of commands... I will see, maybe I won't have to invent the wheel again if I can find an opensource product which is already doing this job.

Kind regards nbanba

nbanb avatar Apr 22 '25 09:04 nbanb

Typical way is to allow all commands (including destructive), but within a sandboxed environment (e.g. a virtual machine) with a properly constrained access and resource limits.

Make it easy to fix instead of hard to break, especially in school/education settings. Student can break the VM, but can also press a button to reset it back to original state.

(unless it's a deliberate game of "find one more novel way to break the system" whack-a-mole; to increase students' engagement).


It is very tricky to secure root shell by filtering text. For example, /usr/bin/perl can be used for legitimate reasons, but also can replace all commands cited above. And naively blocking shutdown command can lead to problems in doing e.g. man 2 shutdown to inspect documentation about a network socket function.

vi avatar Apr 22 '25 11:04 vi