sisniff.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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.80"
  15. PROC_TCP4 = "/proc/net/tcp"
  16. PROC_UDP4 = "/proc/net/udp"
  17. PROC_ICMP4 = "/proc/net/icmp"
  18. PROC_TCP6 = "/proc/net/tcp6"
  19. PROC_UDP6 = "/proc/net/udp6"
  20. PROC_PACKET = "/proc/net/packet"
  21. # Services
  22. TSERV = dict((TCP_SERVICES[k], k) for k in TCP_SERVICES.keys())
  23. USERV = dict((UDP_SERVICES[k], k) for k in UDP_SERVICES.keys())
  24. # IP Protocol Numbers (dec)
  25. IPPROTO_ICMP = 1
  26. IPPROTO_TCP = 6
  27. IPROTOP_IGP = 9
  28. IPPROTO_UDP = 17
  29. nostate = set(['04','05','06''07','08','09','0C','0D'])
  30. tcp_payload_hdrs = ['GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT']
  31. numeric = False
  32. payloadH = False
  33. payloadHl = False
  34. fillter = ""
  35. def get_conn_info(proto,hosts,ports):
  36. ''' returns: pid, exe, uid '''
  37. uid = 0
  38. line_array = _proc4load(proto,hosts,ports)
  39. if line_array == 0:
  40. return ['?','?','?']
  41. '''
  42. try:
  43. uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
  44. except:
  45. uid = line_array[7]
  46. '''
  47. inode = str(line_array[9])
  48. if inode == "0":
  49. return ['.','.','.']
  50. pid = _get_pid_of_inode(inode) # try get a pid
  51. if pid == "NoPid":
  52. #print ">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array)
  53. return ['-', '-', uid]
  54. try: # try read the process name.
  55. exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
  56. except:
  57. exe = None
  58. #print str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid
  59. return [pid, exe, uid]
  60. def _proc4load(proto,hosts,ports):
  61. ''' Read the table of tcp/udp connections
  62. tcp/udp: "sl, local_address, rem_address, st, tx_queue rx_queue, tr tm->when, retrnsmt, uid , timeout, inode ,..."
  63. ---- TCP states from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h?id=HEAD
  64. enum {
  65. TCP_ESTABLISHED = 1,
  66. TCP_SYN_SENT,
  67. TCP_SYN_RECV,
  68. TCP_FIN_WAIT1,
  69. TCP_FIN_WAIT2,
  70. TCP_TIME_WAIT,
  71. TCP_CLOSE,
  72. TCP_CLOSE_WAIT,
  73. TCP_LAST_ACK,
  74. TCP_LISTEN,
  75. TCP_CLOSING, /* Now a valid state */
  76. TCP_NEW_SYN_RECV,
  77. TCP_MAX_STATES /* Leave at the end! */
  78. };
  79. ----------
  80. '''
  81. #xhosts = _ip_hexrev(hosts)
  82. xports = _dec2hex(ports)
  83. if proto == IPPROTO_UDP:
  84. try:
  85. with open(PROC_UDP4,'r') as f:
  86. next(f)
  87. for line in f:
  88. line_arrayu = _remove_empty(line.split(' '))
  89. l_xhost,l_xport = line_arrayu[1].split(':')
  90. if l_xhost not in xMYADDRS:
  91. continue
  92. if l_xport == xports:
  93. return line_arrayu
  94. return 0
  95. except:
  96. print "open proc_udp4 error"
  97. return 0
  98. elif proto == IPPROTO_TCP:
  99. try:
  100. with open(PROC_TCP4,'r') as f:
  101. next(f)
  102. for line in f:
  103. line_arrayt = _remove_empty(line.split(' '))
  104. if line_arrayt[3] in nostate: # not some TCP state
  105. continue
  106. l_xhost,l_xport = line_arrayt[1].split(':')
  107. if l_xhost not in xMYADDRS:
  108. continue
  109. if l_xport == xports:
  110. return line_arrayt
  111. return 0
  112. except:
  113. print "open proc_tcp error"
  114. return 0
  115. elif proto == IPPROTO_ICMP:
  116. try:
  117. with open(PROC_ICMP4,'r') as f:
  118. next(f)
  119. for line in f:
  120. line_arrayi = _remove_empty(line.split(' '))
  121. l_xhost,l_xport = line_arrayi[1].split(':')
  122. if l_xhost not in xMYADDRS:
  123. continue
  124. if l_xport == xports:
  125. return line_arrayi
  126. return 0
  127. except:
  128. print "open proc_icmp4 error"
  129. return 0
  130. return 0
  131. def _convert_ipv4_port(array):
  132. host,port = array.split(':')
  133. return _ip(host),_hex2dec(port)
  134. def _hex2dec(s):
  135. return str(int(s,16))
  136. def _dec2hex(p):
  137. return hex(int(p)).split('x')[-1].upper()
  138. def _ip(s):
  139. ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))]
  140. return '.'.join(ip)
  141. def _ip6(s):
  142. 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]]
  143. #print '66666:', ':'.join(ip), s
  144. return ':'.join(ip)
  145. def _ip_hexrev(ip):
  146. return ''.join([hex(int(x)+256)[3:] for x in ip.split('.')][::-1]).upper()
  147. def _remove_empty(array):
  148. return [x for x in array if x != '']
  149. def _get_pid_of_inode(inode):
  150. s_term = r'^socket\:\['+ inode +r'\]$'
  151. for item in glob.iglob('/proc/[0-9]*/fd/[0-9]*'):
  152. try:
  153. if re.match(s_term,os.readlink(item)):
  154. return item.split('/')[2]
  155. except:
  156. pass
  157. return "NoPid"
  158. def _resolve_ip(host):
  159. """
  160. resolve ip und update dictionary res_cache {'ip': 'name'}.
  161. If resolution for a ip failed, 'name' is n_try ... 0.
  162. """
  163. try:
  164. hname = socket.gethostbyaddr(host)[0]
  165. res_cache[host] = str(hname)
  166. return str(hname)
  167. except:
  168. res_cache[host] = str(host)
  169. return str(host)
  170. def check_root():
  171. if os.getuid() == 0:
  172. return True
  173. else:
  174. return False
  175. ## Define our Custom Action function
  176. def doPackets(packet):
  177. program = "-"
  178. pid = "-"
  179. uid = "-"
  180. o_proto = ""
  181. o_dport = "none"
  182. o_sport = "none"
  183. flags = ""
  184. # only local addresses
  185. if packet[0][1].src in MYADDRS:
  186. conn_addr = packet[0][1].src
  187. if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):
  188. try:
  189. conn_port = packet[0][2].sport
  190. except:
  191. conn_port = 99999
  192. o_dir = 1
  193. else:
  194. conn_addr = packet[0][1].dst
  195. if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):
  196. try:
  197. conn_port = packet[0][2].dport
  198. except:
  199. conn_port = 99999
  200. o_dir = 0
  201. if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP): # grrr, no info in /proc/net/icmp so far. or packet.haslayer(ICMP):
  202. # logemol casch
  203. c_hash = conn_addr+'=:='+str(conn_port)
  204. if not any(x[0] == c_hash for x in conn_cache):
  205. # get the connection info from packet
  206. spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port)
  207. if re.match("^[0-9]+$", spid):
  208. program = sexe
  209. pid = spid
  210. uid = suid
  211. # update cache
  212. if len(conn_cache) >= cc_maxlen:
  213. conn_cache.pop(0)
  214. conn_cache.append([c_hash,program,pid])
  215. #print conn_cache
  216. else:
  217. program = sexe
  218. pid = spid
  219. uid = suid
  220. else:
  221. # me honds fom casch
  222. indx = [x[0] for x in conn_cache].index(c_hash)
  223. program = conn_cache[indx][1]
  224. pid = conn_cache[indx][2]
  225. uid = 0
  226. # cache aktualisieren
  227. renew = conn_cache.pop(indx)
  228. conn_cache.append(renew)
  229. if program != filter_prog:
  230. return
  231. o_payload = ""
  232. if packet.haslayer(UDP):
  233. o_proto = "UDP"
  234. try:
  235. o_dport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
  236. except:
  237. o_dport = str(packet[0][2].dport)
  238. try:
  239. o_sport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
  240. except:
  241. o_sport = str(packet[0][2].sport)
  242. flags = ""
  243. #o_payload = packet[0].sprintf('%10s,UDP.payload%')
  244. elif packet.haslayer(TCP):
  245. o_proto = "TCP"
  246. try:
  247. o_dport = "\033[1m"+TSERV[packet[0][2].dport]+"\033[0m"
  248. except:
  249. o_dport = str(packet[0][2].dport)
  250. try:
  251. o_sport = "\033[1m"+TSERV[packet[0][2].sport]+"\033[0m"
  252. except:
  253. o_sport = str(packet[0][2].sport)
  254. flags = packet[0].sprintf('%3s,TCP.flags%')
  255. if payloadH == True:
  256. if packet.haslayer(Raw):
  257. #tpld = packet[0].sprintf('%TCP.payload%')
  258. tpld = packet[0][TCP].load
  259. if re.match("^GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*", tpld[0:8]):
  260. if payloadHl == True:
  261. o_payload = str(tpld)
  262. else:
  263. request_line, gaga = tpld.split('\r\n', 1)
  264. o_payload = str(request_line)
  265. #o_payload = tpld[0:20]
  266. elif packet.haslayer(ICMP):
  267. o_proto = "ICMP"
  268. if conn_port == 99999:
  269. o_dport = "-"
  270. o_sport = "-"
  271. else:
  272. try:
  273. o_dport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
  274. except:
  275. o_dport = str(packet[0][2].sport)
  276. try:
  277. o_sport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
  278. except:
  279. o_sport = str(packet[0][2].dport)
  280. flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]"
  281. else:
  282. o_proto = "UNKNOWN"
  283. if o_dir == 1:
  284. if numeric == False:
  285. if res_cache.has_key(packet[0][1].dst):
  286. rem_name = res_cache[packet[0][1].dst]
  287. else:
  288. rem_name = _resolve_ip(packet[0][1].dst)
  289. else:
  290. rem_name = packet[0][1].dst
  291. #return "\033[1m "+str(packet[0].time)+" "+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
  292. 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
  293. else:
  294. if numeric == False:
  295. if res_cache.has_key(packet[0][1].src):
  296. rem_name = res_cache[packet[0][1].src]
  297. else:
  298. rem_name = _resolve_ip(packet[0][1].src)
  299. else:
  300. rem_name = packet[0][1].src
  301. 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
  302. ## -- Ond denn s'Hooptprogramm
  303. # root check
  304. if not check_root():
  305. print("This program needs root privileges !\nThats because of reading the /proc filesystem and using libpcap functions.\nSo I give up\n")
  306. conf.sniff_promisc=0
  307. conf.sniff_promisc=0
  308. #print conf
  309. sys.exit()
  310. # get the interfaces
  311. ifaces = commands.getoutput("ls /sys/class/net")
  312. iface_list = ifaces.split('\n')
  313. print
  314. # commandline params
  315. parser = argparse.ArgumentParser(description='sisniff V'+VERSION)
  316. parser.add_argument('-i', help="Interface (required)", choices=iface_list, required=True)
  317. parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true")
  318. parser.add_argument('-p', help="Filter by program name", type=str, metavar='program')
  319. parser.add_argument('-pH', help="Show HTTP Payload", action="store_true")
  320. parser.add_argument('-pHl', help="Show HTTP Payload, long output", action="store_true")
  321. parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str)
  322. args = parser.parse_args()
  323. iface = args.i
  324. if args.n:
  325. numeric = True
  326. if args.pH:
  327. payloadH = True
  328. if args.pHl:
  329. payloadH = True
  330. payloadHl = True
  331. if args.filter:
  332. fillter = " and (" + args.filter + ")"
  333. print "> Applying Filter: \"ip" + fillter + "\""
  334. if args.p:
  335. filter_prog = args.p
  336. # local addresses
  337. MYADDRS = _remove_empty([os.popen('ip addr show '+iface).read().split("inet ")[1].split("/")[0]])
  338. MYADDRS.append('0.0.0.0')
  339. MYADDRS.append('127.0.0.1')
  340. xMYADDRS = [_ip_hexrev(x) for x in MYADDRS]
  341. print "> My IP-Addresses: " + str(MYADDRS)
  342. # confirmed connections cache (ringboffer)
  343. conn_cache = []
  344. cc_maxlen = 20
  345. # resolver cache
  346. res_cache = {}
  347. n_try = 3
  348. print
  349. print "Prog/PID mavericks: ?/? = No entry in /proc/net/xxx; -/- = No PID for Inode found; ./. = Inode=0;"
  350. print
  351. print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]"
  352. print "-------------------------------------------------------------------------------"
  353. # sniff, filtering for IP traffic
  354. sniff(filter="ip"+fillter,iface=iface,prn=doPackets, store=0)
  355. ## -- oond denn isch schloss