WebSocket: Introduce new analyzer and log

This adds a new WebSocket analyzer that is enabled with the HTTP upgrade
mechanism introduced previously. It is a first implementation in BinPac with
manual chunking of frame payload. Configuration of the analyzer is sketched
via the new websocket_handshake() event and a configuration BiF called
WebSocket::__configure_analyzer(). In short, script land collects WebSocket
related HTTP headers and can forward these to the analyzer to change its
parsing behavior at websocket_handshake() time. For now, however, there's
no actual logic that would change behavior based on agreed upon extensions
exchanged via HTTP headers (e.g. frame compression). WebSocket::Configure()
simply attaches a PIA_TCP analyzer to the WebSocket analyzer for dynamic
protocol detection (or a custom analyzer if set). The added pcaps show this
in action for tunneled ssh, http and https using wstunnel. One test pcap is
Broker's WebSocket traffic from our own test suite, the other is the
Jupyter websocket traffic from the ticket/discussion.

This commit further adds a basic websocket.log that aggregates the WebSocket
specific headers (Sec-WebSocket-*) headers into a single log.

Closes #3424
This commit is contained in:
Arne Welzel 2024-01-13 16:56:52 +01:00
parent 8ebd054abc
commit efc2681152
55 changed files with 1256 additions and 6 deletions

View file

@ -0,0 +1,13 @@
# @TEST-DOC: Test Broker WebSocket traffic.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/broker-websocket.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/websocket

View file

@ -0,0 +1,38 @@
# @TEST-DOC: Test WebSocket events.
#
# @TEST-EXEC: echo "jupyter-websocket.pcap" >>out
# @TEST-EXEC: zeek -b -r $TRACES/websocket/jupyter-websocket.pcap %INPUT >>out
# @TEST-EXEC: echo "wstunnel-http.pcap" >>out
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-http.pcap %INPUT >>out
# @TEST-EXEC: echo "broker-websocket.pcap" >>out
# @TEST-EXEC: zeek -b -r $TRACES//websocket/broker-websocket.pcap %INPUT >>out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/websocket
event websocket_handshake(c: connection, aid: count)
{
print "websocket_handshake", c$uid, aid, c$websocket;
}
event websocket_message(c: connection, is_orig: bool, opcode: count)
{
print "websocket_message", c$uid, is_orig, "opcode", WebSocket::opcodes[opcode];
}
event websocket_frame(c: connection, is_orig: bool, fin: bool, rsv: count, opcode: count, payload_len: count)
{
print "websocket_frame", c$uid, is_orig, "fin", fin, "rsv", rsv, "opcode", WebSocket::opcodes[opcode], "payload_len", payload_len;
}
event websocket_frame_data(c: connection, is_orig: bool, data: string)
{
print "websocket_frame_data", c$uid, is_orig, "len", |data|, "data", data[:120];
}
event websocket_close(c: connection, is_orig: bool, status: count, reason: string)
{
print "websocket_close", c$uid, is_orig, "status", status, "reason", reason;
}

View file

@ -0,0 +1,13 @@
# @TEST-DOC: Testing Jupyter WebSocket traffic.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/jupyter-websocket.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
#
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/websocket

View file

@ -0,0 +1,16 @@
# @TEST-DOC: Test HTTP connection tunneled within WebSocket using wstunnel. Seems something in the HTTP scripts gets confused :-/
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-http.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid host uri status_code user_agent < http.log > http.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff http.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/ssh
@load base/protocols/websocket

View file

@ -0,0 +1,16 @@
# @TEST-DOC: Test SSH connection tunneled within WebSocket using wstunnel.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-https.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid version server_name ssl_history < ssl.log > ssl.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/ssl
@load base/protocols/websocket

View file

@ -0,0 +1,21 @@
# @TEST-DOC: Test SSH connection tunneled within WebSocket using wstunnel, attaches HTTP analyzer instead of SSH.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-ssh.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f ssh.log
# @TEST-EXEC: test ! -f analyzer.log
@load base/protocols/conn
@load base/protocols/http
@load base/protocols/ssh
@load base/protocols/websocket
hook WebSocket::configure_analyzer(c: connection, aid: count, config: WebSocket::AnalyzerConfig)
{
print "WebSocket::configure_analyzer", c$uid, aid;
config$analyzer = Analyzer::ANALYZER_HTTP; # this is obviously wrong :-)
}

View file

@ -0,0 +1,22 @@
# @TEST-DOC: Test SSH connection tunneled within WebSocket using wstunnel, configure SSH analyzer via hook explicitly.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-ssh.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid client server auth_success auth_attempts kex_alg host_key_alg < ssh.log > ssh.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssh.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/ssh
@load base/protocols/websocket
hook WebSocket::configure_analyzer(c: connection, aid: count, config: WebSocket::AnalyzerConfig)
{
print "WebSocket::configure_analyzer", c$uid, aid;
config$analyzer = Analyzer::ANALYZER_SSH;
}

View file

@ -0,0 +1,16 @@
# @TEST-DOC: Test SSH connection tunneled within WebSocket using wstunnel.
#
# @TEST-EXEC: zeek -b -r $TRACES/websocket/wstunnel-ssh.pcap %INPUT
#
# @TEST-EXEC: zeek-cut -m ts uid history service < conn.log > conn.log.cut
# @TEST-EXEC: zeek-cut -m ts uid client server auth_success auth_attempts kex_alg host_key_alg < ssh.log > ssh.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssh.log.cut
# @TEST-EXEC: btest-diff websocket.log
# @TEST-EXEC: test ! -f analyzer.log
# @TEST-EXEC: test ! -f weird.log
@load base/protocols/conn
@load base/protocols/ssh
@load base/protocols/websocket