diff options
-rwxr-xr-x | exportpdu.py | 45 | ||||
-rwxr-xr-x | extcap/ssh-tcpdump | 4 | ||||
-rw-r--r-- | lua/tls-alerts.lua | 136 | ||||
-rwxr-xr-x | sync-build.sh | 10 |
4 files changed, 165 insertions, 30 deletions
diff --git a/exportpdu.py b/exportpdu.py index cb910d7..62c3fb0 100755 --- a/exportpdu.py +++ b/exportpdu.py @@ -1,18 +1,19 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import struct # So slow... let's import what we need. #from scapy.all import * +from scapy.config import conf from scapy.fields import StrField from scapy.packet import Packet from scapy.utils import wrpcap -# From epan/exported_pdu.h +# From wsutil/exported_pdu_tlvs.h (used in epan/exported_pdu.h) EXP_PDU_TAG_END_OF_OPT = 0 EXP_PDU_TAG_OPTIONS_LENGTH = 10 EXP_PDU_TAG_LINKTYPE = 11 -EXP_PDU_TAG_PROTO_NAME = 12 -EXP_PDU_TAG_HEUR_PROTO_NAME = 13 +EXP_PDU_TAG_DISSECTOR_NAME = 12 +EXP_PDU_TAG_HEUR_DISSECTOR_NAME = 13 EXP_PDU_TAG_DISSECTOR_TABLE_NAME = 14 EXP_PDU_TAG_IPV4_SRC = 20 EXP_PDU_TAG_IPV4_DST = 21 @@ -27,24 +28,14 @@ EXP_PDU_TAG_ORIG_FNO = 30 EXP_PDU_TAG_DVBCI_EVT = 31 EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL = 32 EXP_PDU_TAG_COL_PROT_TEXT = 33 +EXP_PDU_TAG_TCP_INFO_DATA = 34 +EXP_PDU_TAG_P2P_DIRECTION = 35 +EXP_PDU_TAG_COL_INFO_TEXT = 36 -class TagField(StrField): - def __init__(self, name, default): - StrField.__init__(self, name, default) - - def m2i(self, pkt, x): - tag_type, tag_len = struct.unpack_from('!HH', x) - x = x[4:] - if tag_len > len(x): - # XXX error? - return - tag_data, x = x[:tag_len], x[tag_len:] - return[tag_type, tag_data] +# For backwards compatibility, since Wireshark v4.1.0rc0-197-ge5951765d8. +EXP_PDU_TAG_PROTO_NAME = EXP_PDU_TAG_DISSECTOR_NAME +EXP_PDU_TAG_HEUR_PROTO_NAME = EXP_PDU_TAG_HEUR_DISSECTOR_NAME - def i2m(self, pkt, x): - tag_type, tag_data = x - tag_len = len(tag_data) - return struct.pack('!HH', tag_type, tag_len) + tag_data class TagsField(StrField): islist = 1 @@ -69,6 +60,15 @@ class TagsField(StrField): def _convert_data(self, tag_type, tag_data): if type(tag_data) is int: return struct.pack('!I', tag_data) + # Wireshark pads some strings to align them at four bytes. Although not + # strictly necessary for use in Wireshark, replicate it. See + # https://gitlab.com/wireshark/wireshark/-/issues/19284 + tag_len = len(tag_data) + if tag_type in (EXP_PDU_TAG_DISSECTOR_NAME, + EXP_PDU_TAG_HEUR_DISSECTOR_NAME, + EXP_PDU_TAG_DISSECTOR_TABLE_NAME) and (tag_len & 3): + pad_len = 4 - (tag_len & 3) + tag_data += pad_len * b'\0' return tag_data def i2m(self, pkt, x): @@ -85,6 +85,9 @@ class WiresharkUpperPdu(Packet): name = "WiresharkUpperPdu" fields_desc = [ TagsField("tags", []) ] +DLT_WIRESHARK_UPPER_PDU = 252 +conf.l2types.register(DLT_WIRESHARK_UPPER_PDU, WiresharkUpperPdu) + udp_bootp = WiresharkUpperPdu(tags = [ (EXP_PDU_TAG_DISSECTOR_TABLE_NAME, b'udp.port'), #(EXP_PDU_TAG_PORT_TYPE, 3), # UDP (3) @@ -101,7 +104,7 @@ ip_udp = WiresharkUpperPdu(tags = [ def make_pcap(filename, pkt): # Link Type: Wireshark Upper PDU export (252) - wrpcap(filename, pkt, linktype=252) + wrpcap(filename, pkt, linktype=DLT_WIRESHARK_UPPER_PDU) parser = argparse.ArgumentParser() parser.add_argument("filename") diff --git a/extcap/ssh-tcpdump b/extcap/ssh-tcpdump index 02fcca6..d04b5e0 100755 --- a/extcap/ssh-tcpdump +++ b/extcap/ssh-tcpdump @@ -22,6 +22,7 @@ parser.add_argument('--extcap-interfaces', action='store_true') parser.add_argument('--extcap-dlts', action='store_true') parser.add_argument('--extcap-config', action='store_true') parser.add_argument('--capture', action='store_true') +parser.add_argument('--extcap-version') parser.add_argument('--extcap-interface', metavar='IFACE') @@ -72,13 +73,14 @@ def extcap_capture(iface, cfilter, outfile): else: ssh_user = os.getenv('USER') tcpdump_args = [ - "sudo", "tcpdump", "-i", iface, "-p", "-U", "-w", "-", ] + if ssh_user != 'root': + tcpdump_args = ["sudo"] + tcpdump_args # Change to a less-privileged user if ssh_user: tcpdump_args += ["-Z", ssh_user] diff --git a/lua/tls-alerts.lua b/lua/tls-alerts.lua new file mode 100644 index 0000000..be79dc9 --- /dev/null +++ b/lua/tls-alerts.lua @@ -0,0 +1,136 @@ +-- +-- Wireshark listener to identify unusual TLS Alerts and associated domains. +-- Author: Peter Wu <peter@lekensteyn.nl> +-- +-- Load in Wireshark, then open the Tools -> TLS Alerts menu, or use tshark: +-- +-- $ tshark -q -Xlua_script:tls-alerts.lua -r some.pcapng +-- shavar.services.mozilla.com 1x Bad Certificate (42) +-- aus5.mozilla.org 3x Bad Certificate (42), 1x Unknown CA (48) +-- + +--local quic_stream = Field.new("quic.stream") +local tls_sni = Field.new("tls.handshake.extensions_server_name") +local tls_alert = Field.new("tls.alert_message.desc") + +-- Map from TCP stream -> SNI +local snis +-- Map from SNI -> (map of alerts -> counts) +local alerts + +local tw +local function reset_stats() + snis = {} + alerts = {} + if gui_enabled() then + tw:clear() + end +end + +local function tap_packet(pinfo, tvb, tcp_info) + local tcp_stream = tcp_info.th_stream + if not tcp_stream then + print('TCP stream somehow not found, is this QUIC? pkt=' .. pinfo.number) + return + end + + local f_sni = tls_sni() + if f_sni then + snis[tcp_stream] = f_sni.value + end + -- Ignore "Close Notify (0)" alerts since these are not unusual. + local f_alert = tls_alert() + if f_alert and f_alert.value ~= 0 then + local sni = snis[tcp_stream] or string.format("<unknown SNI on tcp.stream==%d>", tcp_stream) + -- Store counters for SNI -> Alerts mappings + local sni_alerts = alerts[sni] + if not alerts[sni] then + sni_alerts = {} + alerts[sni] = sni_alerts + end + local count = sni_alerts[f_alert.display] + if not count then + sni_alerts[f_alert.display] = 1 + else + sni_alerts[f_alert.display] = count + 1 + end + end +end + +local function round_to_multiple_of(val, multiple) + local rest = val % multiple + if rest == 0 then + return val + else + return val - rest + multiple + end +end + +local function output_all(callback, need_newline) + -- Align the domain to a multiple of four columns + local max_length = 16 + for sni in pairs(alerts) do + if #sni > max_length then + max_length = round_to_multiple_of(#sni + 1, 4) - 1 + end + end + local fmt = "%-" .. max_length .. "s %s" + if need_newline then fmt = fmt .. "\n" end + + for sni, alert_counts in pairs(alerts) do + table.sort(alert_counts, function(a, b) return a > b end) + local all_alerts + for alert, count in pairs(alert_counts) do + local sep = "" + local chunk = string.format("%dx %s", count, alert) + if all_alerts then + all_alerts = all_alerts .. ", " .. chunk + else + all_alerts = chunk + end + end + callback(string.format(fmt, sni, all_alerts)) + end +end + +-- Called periodically in the Wireshark GUI +local function gui_draw() + tw:clear() + output_all(function(text) + tw:append(text .. "\n") + end) +end + +-- Called at the end of tshark +local function cli_draw() + output_all(print) +end + +local function activate_tap() + -- Match TLS Client Hello with SNI extension or TLS alerts. + local tap = Listener.new("tcp", "(tls.handshake.type==1 and tls.handshake.extensions_server_name) or tls.alert_message") + + if gui_enabled() then + tw = TextWindow.new("TLS Alerts") + tw:set_atclose(function() + tap:remove() + tw = nil + end) + tap.draw = gui_draw + else + tap.draw = cli_draw + end + + tap.packet = tap_packet + tap.reset = reset_stats + reset_stats() + if gui_enabled() then + retap_packets() + end +end + +if gui_enabled() then + register_menu("TLS Alerts", activate_tap, MENU_TOOLS_UNSORTED) +else + activate_tap() +end diff --git a/sync-build.sh b/sync-build.sh index bcd4c47..19da4e3 100755 --- a/sync-build.sh +++ b/sync-build.sh @@ -50,13 +50,6 @@ CXX=${CXX:-c++} # "<optimized out>". # -O1 -g -gdwarf-4 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer _default_flags=-fdiagnostics-color -if $CC --version | grep -qE 'clang version ([89]|[1-9][0-9])'; then - # Require Clang and at least LLD 8.0 to avoid broken binaries and crashes. - # https://bugs.llvm.org/show_bug.cgi?id=37303 - _default_flags+=\ -fuse-ld=lld -else - _default_flags+=\ -fuse-ld=gold -fi # -fdebug-prefix-map is supported in GCC since 2007 (?), but only in Clang 3.8 # In GDB, use "dir /tmp/wireshark" to add the source directory anyway. # -fmacro-prefix-map and -ffile-prefix-map were added in GCC 8. Hopefully it @@ -123,13 +116,14 @@ if $force_cmake || [ ! -e $builddir/CMakeCache.txt ]; then -DCMAKE_INSTALL_PREFIX=/tmp/wsroot \ -DENABLE_SMI=0 \ -DCMAKE_BUILD_TYPE=Debug \ - -DDISABLE_WERROR=1 \ + -DENABLE_WERROR=0 \ -DENABLE_ASAN=1 \ -DENABLE_UBSAN=1 \ $remotesrcdir \ -DCMAKE_LIBRARY_PATH=$LIBDIR \ -DCMAKE_C_FLAGS=$(printf %q "$CFLAGS") \ -DCMAKE_CXX_FLAGS=$(printf %q "$CXXFLAGS") \ + -DCMAKE_{EXE,SHARED,MODULE}_LINKER_FLAGS=-fuse-ld=lld \ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ $(printf ' %q' "${cmake_options[@]}") fi && |