sisniff.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #!/usr/bin/python2
  2. # (c) 2017 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch>
  3. from scapy.all import *
  4. import pwd
  5. import os
  6. import re
  7. import glob
  8. import sys
  9. import string
  10. import fcntl
  11. import struct
  12. import commands
  13. import argparse
  14. VERSION = "0.71"
  15. PROC_TCP4 = "/proc/net/tcp"
  16. PROC_UDP4 = "/proc/net/udp"
  17. PROC_TCP6 = "/proc/net/tcp6"
  18. PROC_UDP6 = "/proc/net/udp6"
  19. PROC_PACKET = "/proc/net/packet"
  20. TSERV = dict((TCP_SERVICES[k], k) for k in TCP_SERVICES.keys())
  21. USERV = dict((UDP_SERVICES[k], k) for k in UDP_SERVICES.keys())
  22. tcp_payload_hdrs = ['GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT']
  23. numeric = False
  24. payloadH = False
  25. fillter = ""
  26. def get_conn_info(proto,hosts,ports):
  27. ''' returns: pid, exe, uid '''
  28. line_array = _proc4load(proto,hosts,ports)
  29. if line_array == 0:
  30. return ['?','?','?']
  31. '''
  32. try:
  33. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  34. except:
  35. uid = line_array[7]
  36. '''
  37. uid = 0
  38. inode = line_array[9] # Need the inode to get process pid.
  39. if inode == 0:
  40. return ['?','-','?']
  41. pid = _get_pid_of_inode(inode)
  42. if pid == "NoPid":
  43. return ["NoPid", "NoExe", uid]
  44. try: # try read the process name.
  45. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  46. except:
  47. exe = None
  48. #print str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid
  49. return [pid, exe, uid]
  50. def _proc4load(proto,hosts,ports):
  51. ''' Read the table of tcp/udp connections
  52. tcp/udp: "sl, local_address, rem_address, st, tx_queue rx_queue, tr tm->when, retrnsmt, uid , timeout, inode ,..."
  53. ---- TCP states
  54. enum {
  55. TCP_ESTABLISHED = 1,
  56. TCP_SYN_SENT,
  57. TCP_SYN_RECV,
  58. TCP_FIN_WAIT1,
  59. TCP_FIN_WAIT2,
  60. TCP_TIME_WAIT,
  61. TCP_CLOSE,
  62. TCP_CLOSE_WAIT,
  63. TCP_LAST_ACK,
  64. TCP_LISTEN,
  65. TCP_CLOSING, /* Now a valid state */
  66. TCP_NEW_SYN_RECV,
  67. TCP_MAX_STATES /* Leave at the end! */
  68. };
  69. ----------
  70. '''
  71. content = []
  72. if proto == 6:
  73. try:
  74. with open(PROC_TCP4,'r') as f:
  75. content = f.readlines()
  76. f.close()
  77. content.pop(0)
  78. except:
  79. print "open proc_tcp4 error"
  80. return 0
  81. if proto == 17:
  82. try:
  83. with open(PROC_UDP4,'r') as f:
  84. content = f.readlines()
  85. f.close()
  86. content.pop(0)
  87. except:
  88. print "open proc_udp4 error"
  89. return 0
  90. for line in content: # src
  91. line_array = _remove_empty(line.split(' '))
  92. if line_array[3] in ['04','05','06''07','08','09','0C','0D']: # not some state
  93. continue
  94. l_host,l_port = _convert_ipv4_port(line_array[1])
  95. # alt if l_host == '127.0.0.1' or l_host not in MYADDRS:
  96. if l_host not in MYADDRS:
  97. continue
  98. if str(l_port) == str(ports):
  99. #print l_host+" "+str(l_port)+" // "+host+" "+str(port)
  100. return line_array
  101. #print "no entry in procfile found!"
  102. return 0
  103. def _convert_ipv4_port(array):
  104. host,port = array.split(':')
  105. return _ip(host),_hex2dec(port)
  106. def _hex2dec(s):
  107. return str(int(s,16))
  108. def _ip(s):
  109. ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))]
  110. return '.'.join(ip)
  111. def _ip6(s):
  112. 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]]
  113. #print '66666:', ':'.join(ip), s
  114. return ':'.join(ip)
  115. def _remove_empty(array):
  116. return [x for x in array if x !='']
  117. def _get_pid_of_inode(inode):
  118. for item in glob.glob('/proc/[0-9]*/fd/[0-9]*'):
  119. try:
  120. if re.search(inode,os.readlink(item)):
  121. return item.split('/')[2]
  122. except:
  123. pass
  124. return "NoPid"
  125. def _resolve_ip(host):
  126. """
  127. resolve ip und update dictionary res_cache {'ip': 'name'}.
  128. If resolution for a ip failed, 'name' is n_try ... 0.
  129. """
  130. try:
  131. hname = socket.gethostbyaddr(host)[0]
  132. res_cache[host] = str(hname)
  133. return str(hname)
  134. except:
  135. res_cache[host] = str(host)
  136. return str(host)
  137. def check_root():
  138. if os.getuid() == 0:
  139. return True
  140. else:
  141. return False
  142. ## Define our Custom Action function
  143. def doPackets(packet):
  144. program = "-"
  145. pid = "-"
  146. uid = "-"
  147. o_proto = ""
  148. o_dport = "none"
  149. o_sport = "none"
  150. flags = ""
  151. # only local addresses
  152. if packet[0][1].src in MYADDRS:
  153. conn_addr = packet[0][1].src
  154. if packet.haslayer(TCP) or packet.haslayer(UDP):
  155. conn_port = packet[0][2].sport
  156. o_dir = 1
  157. else:
  158. conn_addr = packet[0][1].dst
  159. if packet.haslayer(TCP) or packet.haslayer(UDP):
  160. conn_port = packet[0][2].dport
  161. o_dir = 0
  162. if packet.haslayer(TCP) or packet.haslayer(UDP):
  163. # logemol casch
  164. c_hash = conn_addr+'.'+str(conn_port)
  165. if not any(x[0] == c_hash for x in conn_cache):
  166. # get the connection info from packet
  167. spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port)
  168. if re.match("^[0-9]+$", spid):
  169. program = sexe
  170. pid = spid
  171. uid = suid
  172. # update cache
  173. if len(conn_cache) > cc_maxlen:
  174. conn_cache.pop(0)
  175. conn_cache.append([c_hash,program,pid])
  176. #print conn_cache
  177. else:
  178. program = "-"
  179. pid = "-"
  180. uid = "-"
  181. else:
  182. # me honds fom casch
  183. indx = [x[0] for x in conn_cache].index(c_hash)
  184. program = conn_cache[indx][1]
  185. pid = conn_cache[indx][2]
  186. uid = 0
  187. # cache aktualisieren
  188. renew = conn_cache.pop(indx)
  189. conn_cache.append(renew)
  190. o_payload = ""
  191. if packet.haslayer(UDP):
  192. o_proto = "UDP"
  193. try:
  194. o_dport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
  195. except:
  196. o_dport = str(packet[0][2].dport)
  197. try:
  198. o_sport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
  199. except:
  200. o_sport = str(packet[0][2].sport)
  201. flags = ""
  202. #o_payload = packet[0].sprintf('%10s,UDP.payload%')
  203. if packet.haslayer(TCP):
  204. o_proto = "TCP"
  205. try:
  206. o_dport = "\033[1m"+TSERV[packet[0][2].dport]+"\033[0m"
  207. except:
  208. o_dport = str(packet[0][2].dport)
  209. try:
  210. o_sport = "\033[1m"+TSERV[packet[0][2].sport]+"\033[0m"
  211. except:
  212. o_sport = str(packet[0][2].sport)
  213. flags = packet[0].sprintf('%3s,TCP.flags%')
  214. if payloadH == True:
  215. if packet.haslayer(Raw):
  216. tpld = packet[0].sprintf('%TCP.payload%')
  217. if re.match("^GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*", tpld[0:8]):
  218. request_line, gaga = tpld.split('\r\n', 1)
  219. o_payload = str(request_line)
  220. #o_payload = tpld[0:20]
  221. if o_dir == 1:
  222. if numeric == False:
  223. if res_cache.has_key(packet[0][1].dst):
  224. rem_name = res_cache[packet[0][1].dst]
  225. else:
  226. rem_name = _resolve_ip(packet[0][1].dst)
  227. else:
  228. rem_name = packet[0][1].dst
  229. 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
  230. else:
  231. if numeric == False:
  232. if res_cache.has_key(packet[0][1].src):
  233. rem_name = res_cache[packet[0][1].src]
  234. else:
  235. rem_name = _resolve_ip(packet[0][1].src)
  236. else:
  237. rem_name = packet[0][1].src
  238. 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
  239. ## -- Ond denn s'Hooptprogramm
  240. # root check
  241. if not check_root():
  242. print("This program needs root privileges !\nThats because of reading the /proc filesystem and using libpcap functions.\nSo I give up\n")
  243. conf.sniff_promisc=0
  244. conf.sniff_promisc=0
  245. print conf
  246. #sys.exit()
  247. # get the interfaces
  248. ifaces = commands.getoutput("ls /sys/class/net")
  249. iface_list = ifaces.split('\n')
  250. print
  251. # commandline params
  252. parser = argparse.ArgumentParser(description='sisniff V'+VERSION)
  253. parser.add_argument('-i', help="Interface (mandatory)", choices=iface_list, required=True)
  254. parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true")
  255. parser.add_argument('-pH', help="Show HTTP Payload", action="store_true")
  256. parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str)
  257. args = parser.parse_args()
  258. iface = args.i
  259. if args.n:
  260. numeric = True
  261. if args.pH:
  262. payloadH = True
  263. if args.filter:
  264. fillter = " and (" + args.filter + ")"
  265. print "> Applying Filter: \"ip" + fillter + "\""
  266. # local addresses
  267. MYADDRS = _remove_empty(commands.getoutput("hostname -I").split(' '))
  268. MYADDRS.append('0.0.0.0')
  269. MYADDRS.append('127.0.0.1')
  270. print "> My IP-Addresses: " + str(MYADDRS)
  271. # confirmed connections cache (ringboffer)
  272. conn_cache = []
  273. cc_maxlen = 20
  274. # resolver cache
  275. res_cache = {}
  276. n_try = 3
  277. print
  278. print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]"
  279. print "-------------------------------------------------------------------------------"
  280. # sniff, filtering for IP traffic
  281. sniff(filter="ip"+fillter,iface=iface,prn=doPackets)
  282. ## -- oond denn isch schloss