From 6951d901827e9d31a8c09a3a30d6d65ac25514c7 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sat, 24 Sep 2016 11:22:47 +0200 Subject: extcap/ssh-tcpdump: example remote tcpdump Requires Python 3.4, but it can be adapted for older versions. It demonstrates how "easy" it is to capture remotely over SSH when only tcpdump is installed without dumpcap (in that case you could use sshdump). Note that on stopping/restarting captures, you still get some stderr messages ("Dropped privileges", but that can be ignored). See also https://ask.wireshark.org/questions/55768/remote-interface-linux --- extcap/ssh-tcpdump | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100755 extcap/ssh-tcpdump diff --git a/extcap/ssh-tcpdump b/extcap/ssh-tcpdump new file mode 100755 index 0000000..02fcca6 --- /dev/null +++ b/extcap/ssh-tcpdump @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# Capture from a remote server over SSH which has tcpdump but no dumpcap. +# +# After copying this file to the extcap folder (see Help -> About +# for location), you can use the name "ssh-tcpdump:" followed by the interface +# name: +# +# SSHHOST=peterw@someserver wireshark -i ssh-tcpdump:eth0 -p -k +# +# Note: interface names are currently hard-coded (lo and eth0). + +#from __future__ import print_function + +import argparse, os, sys +from shlex import quote +from signal import SIGINT + +parser = argparse.ArgumentParser() + +# Actions +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-interface', metavar='IFACE') + +parser.add_argument('--extcap-capture-filter', metavar='CFILTER') +parser.add_argument('--fifo', metavar='FIFO') + +IFACE_PREFIX = 'ssh-tcpdump:' +# TODO currently hard-coded interfaces, maybe add preferences? +ifaces = [ + 'lo', + 'eth0', +] + +def extcap_interfaces(): + print("extcap {version=1.0}") + for iface in ifaces: + print("interface {value=%s%s}{display=Remote tcpdump: %s}" % ( + IFACE_PREFIX, iface, iface + )) + +def extcap_dlts(iface): + # Required for the interface to show up in the interace + print("dlt {number=147}{name=USER0}{display=Remote capture dependent DLT}") + +# TODO consider configuration option for setting the SSH host? +def extcap_config(iface): + pass + +def redirect_stdout(outfile): + STDOUT_FILENO = 1 + try: os.close(STDOUT_FILENO) + except OSError: pass + fd = os.open(outfile, os.O_WRONLY, 0o600) + # TODO this requires Py 3.4, maybe old version can use dup2 + os.set_inheritable(fd, True) + if fd != STDOUT_FILENO: + os.dup2(fd, STDOUT_FILENO) + os.close(fd) + +def extcap_capture(iface, cfilter, outfile): + ssh_host = os.getenv("SSHHOST") + if not ssh_host: + raise RuntimeError("Missing SSHHOST") + ssh_user = os.getenv('SSHUSER') + if not ssh_user: + if '@' in ssh_host: + ssh_user = ssh_host.split('@')[0] + else: + ssh_user = os.getenv('USER') + tcpdump_args = [ + "sudo", + "tcpdump", + "-i", iface, + "-p", + "-U", + "-w", "-", + ] + # Change to a less-privileged user + if ssh_user: + tcpdump_args += ["-Z", ssh_user] + if cfilter: + tcpdump_args += [cfilter] + args = [ + "ssh", ssh_host, + " ".join(quote(c) for c in tcpdump_args), + ] + #import subprocess; subprocess.call(["ls", "-l", "/proc/%d/fd/" % os.getpid()], stdout=2) + redirect_stdout(outfile) + os.execvp(args[0], args) + +def main(): + args = parser.parse_args() + if args.extcap_interfaces: + return extcap_interfaces() + + if not args.extcap_interface: + parser.error('Missing --extcap-interface option') + + iface = args.extcap_interface[len(IFACE_PREFIX):] + + if args.extcap_dlts: + return extcap_dlts(iface) + elif args.extcap_config: + return extcap_config(iface) + elif args.capture: + if not args.fifo: + parser.error('Missing --fifo option for --capture') + return extcap_capture(iface, args.extcap_capture_filter, args.fifo) + else: + parser.error('Missing action') + return 1 + +if __name__ == '__main__': + try: + sys.exit(main()) + except KeyboardInterrupt: + sys.exit(128 + SIGINT) + except OSError as e: + print(e, file=sys.stderr) + sys.exit(1) -- cgit v1.2.1