sisniff.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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.81"
  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. try:
  230. filter_prog
  231. except:
  232. pass
  233. else:
  234. if program != filter_prog:
  235. return
  236. o_payload = ""
  237. if packet.haslayer(UDP):
  238. o_proto = "UDP"
  239. try:
  240. o_dport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
  241. except:
  242. o_dport = str(packet[0][2].dport)
  243. try:
  244. o_sport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
  245. except:
  246. o_sport = str(packet[0][2].sport)
  247. flags = ""
  248. #o_payload = packet[0].sprintf('%10s,UDP.payload%')
  249. elif packet.haslayer(TCP):
  250. o_proto = "TCP"
  251. try:
  252. o_dport = "\033[1m"+TSERV[packet[0][2].dport]+"\033[0m"
  253. except:
  254. o_dport = str(packet[0][2].dport)
  255. try:
  256. o_sport = "\033[1m"+TSERV[packet[0][2].sport]+"\033[0m"
  257. except:
  258. o_sport = str(packet[0][2].sport)
  259. flags = packet[0].sprintf('%3s,TCP.flags%')
  260. if payloadH == True:
  261. if packet.haslayer(Raw):
  262. #tpld = packet[0].sprintf('%TCP.payload%')
  263. tpld = packet[0][TCP].load
  264. if re.match("^GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*", tpld[0:8]):
  265. if payloadHl == True:
  266. o_payload = str(tpld)
  267. else:
  268. request_line, gaga = tpld.split('\r\n', 1)
  269. o_payload = str(request_line)
  270. #o_payload = tpld[0:20]
  271. elif packet.haslayer(ICMP):
  272. o_proto = "ICMP"
  273. if conn_port == 99999:
  274. o_dport = "-"
  275. o_sport = "-"
  276. else:
  277. try:
  278. o_dport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
  279. except:
  280. o_dport = str(packet[0][2].sport)
  281. try:
  282. o_sport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
  283. except:
  284. o_sport = str(packet[0][2].dport)
  285. flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]"
  286. else:
  287. o_proto = "UNKNOWN"
  288. if o_dir == 1:
  289. if numeric == False:
  290. if res_cache.has_key(packet[0][1].dst):
  291. rem_name = res_cache[packet[0][1].dst]
  292. else:
  293. rem_name = _resolve_ip(packet[0][1].dst)
  294. else:
  295. rem_name = packet[0][1].dst
  296. #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
  297. 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
  298. else:
  299. if numeric == False:
  300. if res_cache.has_key(packet[0][1].src):
  301. rem_name = res_cache[packet[0][1].src]
  302. else:
  303. rem_name = _resolve_ip(packet[0][1].src)
  304. else:
  305. rem_name = packet[0][1].src
  306. 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
  307. ## -- Ond denn s'Hooptprogramm
  308. # root check
  309. if not check_root():
  310. print("This program needs root privileges !\nThats because of reading the /proc filesystem and using libpcap functions.\nSo I give up\n")
  311. conf.sniff_promisc=0
  312. conf.sniff_promisc=0
  313. #print conf
  314. sys.exit()
  315. # get the interfaces
  316. ifaces = commands.getoutput("ls /sys/class/net")
  317. iface_list = ifaces.split('\n')
  318. print
  319. # commandline params
  320. parser = argparse.ArgumentParser(description='sisniff V'+VERSION)
  321. parser.add_argument('-i', help="Interface (required)", choices=iface_list, required=True)
  322. parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true")
  323. parser.add_argument('-p', help="Filter by program name", type=str, metavar='program')
  324. parser.add_argument('-pH', help="Show HTTP Payload", action="store_true")
  325. parser.add_argument('-pHl', help="Show HTTP Payload, long output", action="store_true")
  326. parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str)
  327. args = parser.parse_args()
  328. iface = args.i
  329. if args.n:
  330. numeric = True
  331. if args.pH:
  332. payloadH = True
  333. if args.pHl:
  334. payloadH = True
  335. payloadHl = True
  336. if args.filter:
  337. fillter = " and (" + args.filter + ")"
  338. print "> Applying Filter: \"ip" + fillter + "\""
  339. if args.p:
  340. filter_prog = args.p
  341. # local addresses
  342. MYADDRS = _remove_empty([os.popen('ip addr show '+iface).read().split("inet ")[1].split("/")[0]])
  343. MYADDRS.append('0.0.0.0')
  344. MYADDRS.append('127.0.0.1')
  345. xMYADDRS = [_ip_hexrev(x) for x in MYADDRS]
  346. print "> My IP-Addresses: " + str(MYADDRS)
  347. # confirmed connections cache (ringboffer)
  348. conn_cache = []
  349. cc_maxlen = 20
  350. # resolver cache
  351. res_cache = {}
  352. n_try = 3
  353. print
  354. print "Prog/PID mavericks: ?/? = No entry in /proc/net/xxx; -/- = No PID for Inode found; ./. = Inode=0;"
  355. print
  356. print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]"
  357. print "-------------------------------------------------------------------------------"
  358. # sniff, filtering for IP traffic
  359. sniff(filter="ip"+fillter,iface=iface,prn=doPackets, store=0)
  360. ## -- oond denn isch schloss