|
@@ -0,0 +1,331 @@
|
|
|
+#!/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
|