sisniff
Aus Si:Wiki von Siegrist SystemLösungen - Informatik und Rezepte
Version vom 18. Juni 2017, 11:54 Uhr von Sigi (Diskussion | Beiträge)
Ein Netzwerk-Sniffer der für jedes Packet nebst Adresse und Port die lokal verbundene Anwendung und deren PID ermittelt und anzeigt.
Es wird TCP und UDP unterstützt.
Zusätzlich kann ein Filter definiert und die Requests/Responses von HTTP Verbindungen angezeigt werden.
Die Option -h
gibt eine Argumenteübersicht und listet die verfügbaren Interfaces auf.
# ./sisniff.py -h usage: sisniff.py [-h] -i {eth0,lo,tun0,wlan0} [-n] [-pH] [filter] positional arguments: filter Filter (BPF syntax) on top of IP (in dbl-quotes "...") optional arguments: -h, --help show this help message and exit -i {eth0,lo,tun0,wlan0} Interface (mandatory) -n Do not resolve IP-Addresses -pH Show HTTP Payload
Download der aktuellen Version: sisniff-0.7.tar.gz
Python2 Code of sisniff.py:
#!/usr/bin/python2 # (c) 2017 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch> from scapy.all import * import pwd import os import re import glob import sys import string import fcntl import struct import commands import argparse VERSION = "0.71" PROC_TCP4 = "/proc/net/tcp" PROC_UDP4 = "/proc/net/udp" PROC_TCP6 = "/proc/net/tcp6" PROC_UDP6 = "/proc/net/udp6" PROC_PACKET = "/proc/net/packet" TSERV = dict((TCP_SERVICES[k], k) for k in TCP_SERVICES.keys()) USERV = dict((UDP_SERVICES[k], k) for k in UDP_SERVICES.keys()) tcp_payload_hdrs = ['GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT'] numeric = False payloadH = False fillter = "" def get_conn_info(proto,hosts,ports): ''' returns: pid, exe, uid ''' line_array = _proc4load(proto,hosts,ports) if line_array == 0: return ['?','?','?'] ''' try: uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID. except: uid = line_array[7] ''' uid = 0 inode = line_array[9] # Need the inode to get process pid. if inode == 0: return ['?','-','?'] pid = _get_pid_of_inode(inode) if pid == "NoPid": return ["NoPid", "NoExe", uid] try: # try read the process name. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1] except: exe = None #print str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid return [pid, exe, uid] def _proc4load(proto,hosts,ports): ''' Read the table of tcp/udp connections tcp/udp: "sl, local_address, rem_address, st, tx_queue rx_queue, tr tm->when, retrnsmt, uid , timeout, inode ,..." ---- TCP states enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING, /* Now a valid state */ TCP_NEW_SYN_RECV, TCP_MAX_STATES /* Leave at the end! */ }; ---------- ''' content = [] if proto == 6: try: with open(PROC_TCP4,'r') as f: content = f.readlines() f.close() content.pop(0) except: print "open proc_tcp4 error" return 0 if proto == 17: try: with open(PROC_UDP4,'r') as f: content = f.readlines() f.close() content.pop(0) except: print "open proc_udp4 error" return 0 for line in content: # src line_array = _remove_empty(line.split(' ')) if line_array[3] in ['04','05','06''07','08','09','0C','0D']: # not some state continue l_host,l_port = _convert_ipv4_port(line_array[1]) # alt if l_host == '127.0.0.1' or l_host not in MYADDRS: if l_host not in MYADDRS: continue if str(l_port) == str(ports): #print l_host+" "+str(l_port)+" // "+host+" "+str(port) return line_array #print "no entry in procfile found!" return 0 def _convert_ipv4_port(array): host,port = array.split(':') return _ip(host),_hex2dec(port) def _hex2dec(s): return str(int(s,16)) def _ip(s): ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))] return '.'.join(ip) def _ip6(s): ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[12:14],s[14:16],s[10:12],s[8:10],s[22:24],s[20:22],s[18:20],s[16:18],s[30:32],s[28:30],s[26:28],s[24:26]] #print '66666:', ':'.join(ip), s return ':'.join(ip) def _remove_empty(array): return [x for x in array if x !=''] def _get_pid_of_inode(inode): for item in glob.glob('/proc/[0-9]*/fd/[0-9]*'): try: if re.search(inode,os.readlink(item)): return item.split('/')[2] except: pass return "NoPid" def _resolve_ip(host): """ resolve ip und update dictionary res_cache {'ip': 'name'}. If resolution for a ip failed, 'name' is n_try ... 0. """ try: hname = socket.gethostbyaddr(host)[0] res_cache[host] = str(hname) return str(hname) except: res_cache[host] = str(host) return str(host) def check_root(): if os.getuid() == 0: return True else: return False ## Define our Custom Action function def doPackets(packet): program = "-" pid = "-" uid = "-" o_proto = "" o_dport = "none" o_sport = "none" flags = "" # only local addresses if packet[0][1].src in MYADDRS: conn_addr = packet[0][1].src if packet.haslayer(TCP) or packet.haslayer(UDP): conn_port = packet[0][2].sport o_dir = 1 else: conn_addr = packet[0][1].dst if packet.haslayer(TCP) or packet.haslayer(UDP): conn_port = packet[0][2].dport o_dir = 0 if packet.haslayer(TCP) or packet.haslayer(UDP): # logemol casch c_hash = conn_addr+'.'+str(conn_port) if not any(x[0] == c_hash for x in conn_cache): # get the connection info from packet spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port) if re.match("^[0-9]+$", spid): program = sexe pid = spid uid = suid # update cache if len(conn_cache) > cc_maxlen: conn_cache.pop(0) conn_cache.append([c_hash,program,pid]) #print conn_cache else: program = "-" pid = "-" uid = "-" else: # me honds fom casch indx = [x[0] for x in conn_cache].index(c_hash) program = conn_cache[indx][1] pid = conn_cache[indx][2] uid = 0 # cache aktualisieren renew = conn_cache.pop(indx) conn_cache.append(renew) o_payload = "" if packet.haslayer(UDP): o_proto = "UDP" try: o_dport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m" except: o_dport = str(packet[0][2].dport) try: o_sport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m" except: o_sport = str(packet[0][2].sport) flags = "" #o_payload = packet[0].sprintf('%10s,UDP.payload%') if packet.haslayer(TCP): o_proto = "TCP" try: o_dport = "\033[1m"+TSERV[packet[0][2].dport]+"\033[0m" except: o_dport = str(packet[0][2].dport) try: o_sport = "\033[1m"+TSERV[packet[0][2].sport]+"\033[0m" except: o_sport = str(packet[0][2].sport) flags = packet[0].sprintf('%3s,TCP.flags%') if payloadH == True: if packet.haslayer(Raw): tpld = packet[0].sprintf('%TCP.payload%') if re.match("^GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*", tpld[0:8]): request_line, gaga = tpld.split('\r\n', 1) o_payload = str(request_line) #o_payload = tpld[0:20] if o_dir == 1: if numeric == False: if res_cache.has_key(packet[0][1].dst): rem_name = res_cache[packet[0][1].dst] else: rem_name = _resolve_ip(packet[0][1].dst) else: rem_name = packet[0][1].dst return "\033[1m"+str(program)+"\033[0m" +"/"+ str(pid) + " - " + o_proto + ": " + packet[0][1].src + ":" + o_sport + "\033[1m\033[31m ->>> \033[0m" + rem_name + ":" + o_dport + " " + flags + " Len:" + str(packet[0][1].len) + " : " + o_payload else: if numeric == False: if res_cache.has_key(packet[0][1].src): rem_name = res_cache[packet[0][1].src] else: rem_name = _resolve_ip(packet[0][1].src) else: rem_name = packet[0][1].src return "\033[1m"+str(program)+"\033[0m" +"/"+ str(pid) + " - " + o_proto + ": " + packet[0][1].dst + ":" + o_dport + "\033[1m\033[36m <<<- \033[0m" + rem_name + ":" + o_sport + " " + flags + " Len:" + str(packet[0][1].len) + " : " + o_payload ## -- Ond denn s'Hooptprogramm # root check if not check_root(): print("This program needs root privileges !\nThats because of reading the /proc filesystem and using libpcap functions.\nSo I give up\n") conf.sniff_promisc=0 conf.sniff_promisc=0 print conf #sys.exit() # get the interfaces ifaces = commands.getoutput("ls /sys/class/net") iface_list = ifaces.split('\n') print # commandline params parser = argparse.ArgumentParser(description='sisniff V'+VERSION) parser.add_argument('-i', help="Interface (mandatory)", choices=iface_list, required=True) parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true") parser.add_argument('-pH', help="Show HTTP Payload", action="store_true") parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str) args = parser.parse_args() iface = args.i if args.n: numeric = True if args.pH: payloadH = True if args.filter: fillter = " and (" + args.filter + ")" print "> Applying Filter: \"ip" + fillter + "\"" # local addresses MYADDRS = _remove_empty(commands.getoutput("hostname -I").split(' ')) MYADDRS.append('0.0.0.0') MYADDRS.append('127.0.0.1') print "> My IP-Addresses: " + str(MYADDRS) # confirmed connections cache (ringboffer) conn_cache = [] cc_maxlen = 20 # resolver cache res_cache = {} n_try = 3 print print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]" print "-------------------------------------------------------------------------------" # sniff, filtering for IP traffic sniff(filter="ip"+fillter,iface=iface,prn=doPackets) ## -- oond denn isch schloss