sisniff.py 12 KB

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