Sisniff: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Sigi (Diskussion | Beiträge) KKeine Bearbeitungszusammenfassung |
Sigi (Diskussion | Beiträge) KKeine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
{{DISPLAYTITLE:sisniff}} | {{DISPLAYTITLE:sisniff}} | ||
Ein Netzwerk-Sniffer der für jedes Packet nebst Adresse und Port die lokal verbundene Anwendung und deren PID ermittelt und anzeigt.<br /> | Ein Netzwerk-Sniffer der für jedes Packet nebst Adresse und Port die '''lokal verbundene Anwendung und deren PID''' ermittelt und anzeigt.<br /> | ||
Es wird TCP, UDP und ICMP unterstützt.<br /> | Es wird TCP, UDP und ICMP unterstützt, für IPv4 und IPv6.<br /> | ||
Der Sniffer akzeptiert Filter wie sie bei tcpdump üblich sind.<br /> | Der Sniffer akzeptiert Filter wie sie bei tcpdump üblich sind.<br /> | ||
Bei HTTP Verbindungen kann ausserdem ein Teil der Payload angezeigt werden. | Bei HTTP Verbindungen kann ausserdem ein Teil der Payload angezeigt werden. | ||
[[Datei:Terminal_059.png|400px|thumb|right|sisniff Image]] | [[Datei:Terminal_059.png|400px|thumb|right|sisniff Image]] | ||
Die Option <code>-h</code> gibt eine Argumenteübersicht und listet die verfügbaren Interfaces auf. | Die Option <code>-h</code> gibt eine Argumenteübersicht und listet die verfügbaren Interfaces auf. | ||
<small><pre> | <small><pre> | ||
# ./sisniff | # ./sisniff -h | ||
usage: sisniff | usage: sisniff [-h] -i {eth0,lo,wlan0} [-n] [-p program|not-program] [-4] [-6] [-pH] [-pHl] [filter] | ||
sisniff V1.00 | |||
2017-2019 by sigi <https://wiki.zweiernet.ch/wiki/sisniff> | |||
positional arguments: | positional arguments: | ||
filter | filter Filter (BPF syntax) on top of IP (in dbl-quotes "...") | ||
optional arguments: | optional arguments: | ||
-h, --help show this help message and exit | -h, --help show this help message and exit | ||
-i {eth0,lo | -i {eth0,lo,wlan0} Interface (required) | ||
-n Do not resolve IP-Addresses | -n Do not resolve IP-Addresses | ||
-p program|not-program | |||
Filter by program name ([not-] negates) | |||
-4 Only IPv4 | |||
-6 Only IPv6 | |||
-pH Show HTTP Payload | -pH Show HTTP Payload | ||
-pHl Show HTTP Payload, long output | |||
</pre></small> | </pre></small> | ||
<br /><p> | <br /><p> | ||
;Downloads der aktuellen Version: | ;Downloads der aktuellen Version: | ||
: Source: https://git.zweiernet.ch/sigi/sisniff/raw/master/sisniff | : Source: https://git.zweiernet.ch/sigi/sisniff/raw/master/sisniff<br /> | ||
: gzipped: https://git.zweiernet.ch/sigi/sisniff/archive/master.tar.gz<br> | : gzipped: https://git.zweiernet.ch/sigi/sisniff/archive/master.tar.gz<br> | ||
: | : Git Projektseite: https://git.zweiernet.ch/sigi/sisniff<br /> | ||
<br /> | <br /> | ||
<br /> | <br /> | ||
{{IBox|i| | {{IBox|i| | ||
Da <code>sisniff | Da <code>sisniff</code> auf der Scapy <code>sniff()</code>-Funktion aufsetzt, wird Scapy > 2.x benötigt: | ||
:Unter Debian/Ubuntu: <code>apt-get install scapy</code> | :Unter Debian/Ubuntu: <code>apt-get install scapy</code> | ||
:pip/pip3: <code>pip install scapy</code> | |||
:Andere: http://www.secdev.org/projects/scapy/ | :Andere: http://www.secdev.org/projects/scapy/ | ||
}} | }} | ||
<br /> | <br /> | ||
Python Code: | |||
<syntaxhighlight lang="python"> | <small><syntaxhighlight lang="python"> | ||
#!/usr/bin/ | #!/usr/bin/env python | ||
# (c) 2017 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch> | # (c) 2017-2019 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch> | ||
# | |||
# All Rights reserved. | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License as | |||
# published by the Free Software Foundation. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
from scapy.all import * | from scapy.all import * | ||
| Zeile 54: | Zeile 75: | ||
import fcntl | import fcntl | ||
import struct | import struct | ||
import argparse | import argparse | ||
if sys.version_info.major == 2: | |||
import commands as subprocess | |||
elif sys.version_info.major == 3: | |||
import subprocess | |||
VERSION = " | def _to_str(inp): | ||
if sys.version_info.major == 2: | |||
return inp | |||
else: | |||
return "".join( chr(x) for x in inp) | |||
VERSION = "1.00" | |||
PROC_TCP4 = "/proc/net/tcp" | PROC_TCP4 = "/proc/net/tcp" | ||
| Zeile 80: | Zeile 111: | ||
numeric = False | numeric = False | ||
payloadH = False | payloadH = False | ||
payloadHl = False | |||
fillter = "" | fillter = "" | ||
def get_conn_info(proto,hosts,ports): | def get_conn_info(proto,hosts,ports,ipvers): | ||
''' returns: pid, exe, uid ''' | ''' returns: pid, exe, uid ''' | ||
uid = 0 | uid = 0 | ||
line_array = _proc4load(proto,hosts,ports) | line_array = _proc4load(proto,hosts,ports,ipvers) | ||
if line_array == 0: | if line_array == 0: | ||
| Zeile 105: | Zeile 137: | ||
if pid == "NoPid": | if pid == "NoPid": | ||
#print ">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array) | #print(">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array)) | ||
return ['-', '-', uid] | return ['-', '-', uid] | ||
| Zeile 113: | Zeile 145: | ||
exe = None | exe = None | ||
#print str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid | #print(str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid) | ||
return [pid, exe, uid] | return [pid, exe, uid] | ||
def _proc4load(proto,hosts,ports): | def _proc4load(proto,hosts,ports,ipvers): | ||
''' Read the table of tcp/udp connections | ''' 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/udp: "sl, local_address, rem_address, st, tx_queue rx_queue, tr tm->when, retrnsmt, uid , timeout, inode ,..." | ||
---- TCP states from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h?id=HEAD | ---- TCP states from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h?id=HEAD | ||
enum { | 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! */ | |||
}; | }; | ||
---------- | ---------- | ||
| Zeile 145: | Zeile 177: | ||
if proto == IPPROTO_UDP: | if proto == IPPROTO_UDP: | ||
try: | try: | ||
with open( | procv = PROC_UDP4 | ||
if ipvers == 6: | |||
procv = PROC_UDP6 | |||
with open(procv,'r') as f: | |||
next(f) | next(f) | ||
for line in f: | for line in f: | ||
| Zeile 157: | Zeile 192: | ||
return 0 | return 0 | ||
except: | except: | ||
print "open proc_udp4 error" | print("open proc_udp4 error") | ||
return 0 | return 0 | ||
elif proto == IPPROTO_TCP: | elif proto == IPPROTO_TCP: | ||
try: | try: | ||
with open( | procv = PROC_TCP4 | ||
if ipvers == 6: | |||
procv = PROC_TCP6 | |||
with open(procv,'r') as f: | |||
next(f) | next(f) | ||
for line in f: | for line in f: | ||
| Zeile 175: | Zeile 213: | ||
return 0 | return 0 | ||
except: | except: | ||
print "open proc_tcp error" | print("open proc_tcp error") | ||
return 0 | return 0 | ||
elif proto == IPPROTO_ICMP: | elif proto == IPPROTO_ICMP: | ||
try: | try: | ||
with open( | procv = PROC_ICMP4 | ||
if ipvers == 6: | |||
procv = PROC_ICMP6 | |||
with open(procv,'r') as f: | |||
next(f) | next(f) | ||
for line in f: | for line in f: | ||
| Zeile 192: | Zeile 233: | ||
return 0 | return 0 | ||
except: | except: | ||
print "open proc_icmp4 error" | print("open proc_icmp4 error") | ||
return 0 | return 0 | ||
| Zeile 212: | Zeile 253: | ||
def _ip6(s): | def _ip6(s): | ||
ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[ | ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[14:16],s[12:14],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]] | ||
return ':'.join(ip) | return ':'.join(ip) | ||
def _ip_hexrev(ip): | def _ip_hexrev(ip): | ||
return ''.join([hex(int(x)+256)[3:] for x in ip.split('.')][::-1]).upper() | return ''.join([hex(int(x)+256)[3:] for x in ip.split('.')][::-1]).upper() | ||
# IPv6 /proc/net/tcp6 format from expanded ip-address | |||
def _to_v6_proc(s): | |||
s = s.replace(":", "") | |||
ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[14:16],s[12:14],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]] | |||
return ''.join(ip).upper() | |||
def expand_v6(ip): | |||
ipa = ip.split(':') # liste | |||
if '' in ipa: | |||
if ipa.count('') > 1: # korr ::1 or ::: | |||
for i in range(ipa.count('')-1): | |||
ipa.remove('') | |||
miss = 8 - len(ipa) +1 | |||
for i in range(miss): | |||
ipa.insert(ipa.index('')+i, '0000') | |||
ipa.remove('') | |||
return ':'.join(["%04x" % x for x in [int(x, 16) for x in ipa]]) | |||
else: | |||
return ':'.join(["%04x" % x for x in [int(x, 16) for x in ipa]]) | |||
def _remove_empty(array): | def _remove_empty(array): | ||
| Zeile 285: | Zeile 349: | ||
if not any(x[0] == c_hash for x in conn_cache): | if not any(x[0] == c_hash for x in conn_cache): | ||
# get the connection info from packet | # get the connection info from packet | ||
spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port) | if packet[0][1].version == 4: | ||
spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port, packet[0][1].version) | |||
elif packet[0][1].version == 6: | |||
spid,sexe,suid = get_conn_info(packet[0][1].nh, conn_addr, conn_port, packet[0][1].version) | |||
if re.match("^[0-9]+$", spid): | if re.match("^[0-9]+$", spid): | ||
program = sexe | program = sexe | ||
| Zeile 295: | Zeile 362: | ||
conn_cache.pop(0) | conn_cache.pop(0) | ||
conn_cache.append([c_hash,program,pid]) | conn_cache.append([c_hash,program,pid]) | ||
else: | else: | ||
program = sexe | program = sexe | ||
| Zeile 309: | Zeile 375: | ||
renew = conn_cache.pop(indx) | renew = conn_cache.pop(indx) | ||
conn_cache.append(renew) | conn_cache.append(renew) | ||
try: | |||
filter_prog | |||
except: | |||
pass | |||
else: | |||
if filter_prog.startswith('not-'): | |||
if program == filter_prog[4:]: | |||
return | |||
else: | |||
if program != filter_prog: | |||
return | |||
o_payload = "" | o_payload = "" | ||
if packet.haslayer(UDP): | if packet.haslayer(UDP): | ||
o_proto = "UDP" | o_proto = "UDP" | ||
| Zeile 323: | Zeile 402: | ||
o_sport = str(packet[0][2].sport) | o_sport = str(packet[0][2].sport) | ||
flags = "" | flags = "" | ||
#o_payload = packet[0].sprintf('%10s,UDP.payload%') | #o_payload = _to_str(packet[0].sprintf('%10s,UDP.payload%')) | ||
elif packet.haslayer(TCP): | elif packet.haslayer(TCP): | ||
o_proto = "TCP" | o_proto = "TCP" | ||
| Zeile 337: | Zeile 416: | ||
if payloadH == True: | if payloadH == True: | ||
if packet.haslayer(Raw): | if packet.haslayer(Raw): | ||
tpld = packet[0].sprintf('%TCP.payload%') | #tpld = packet[0].sprintf('%TCP.payload%') | ||
if re.match( | tpld = _to_str(packet[0][TCP].load) | ||
tpldhead = tpld[0:8] | |||
#print("tpld:" + tpldhead) | |||
if re.match(r'GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*', tpldhead): | |||
if payloadHl == True: | |||
o_payload = str(tpld) | |||
else: | |||
request_line, gaga = tpld.split('\r\n', 1) | |||
o_payload = str(request_line) | |||
#o_payload = tpld[0:20] | #o_payload = tpld[0:20] | ||
elif packet.haslayer(ICMP): | elif packet.haslayer(ICMP): | ||
| Zeile 358: | Zeile 443: | ||
flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]" | flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]" | ||
else: | else: | ||
o_proto = "UNKNOWN" | layerukn = packet[0][1].getlayer(1) | ||
if layerukn is None: | |||
o_proto = "UNKNOWN" | |||
else: | |||
#print("Layer:", xxl1.name) | |||
o_proto = layerukn.name | |||
#o_proto = "UNKNOWN" | |||
if packet[0][1].version == 4: | |||
packlen = str(packet[0][1].len) | |||
if packet[0][1].version == 6: | |||
packlen = str(packet[0][1].plen) | |||
if o_dir == 1: | if o_dir == 1: | ||
if numeric == False: | if numeric == False: | ||
if res_cache.has_key(packet[0][1].dst): | #if res_cache.has_key(packet[0][1].dst): | ||
if packet[0][1].dst in res_cache: | |||
rem_name = res_cache[packet[0][1].dst] | rem_name = res_cache[packet[0][1].dst] | ||
else: | else: | ||
| Zeile 368: | Zeile 466: | ||
else: | else: | ||
rem_name = packet[0][1].dst | 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 | #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 | ||
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:" + packlen + " : " + o_payload | |||
else: | else: | ||
if numeric == False: | if numeric == False: | ||
if res_cache.has_key(packet[0][1].src): | #if res_cache.has_key(packet[0][1].src): | ||
if packet[0][1].src in res_cache: | |||
rem_name = res_cache[packet[0][1].src] | rem_name = res_cache[packet[0][1].src] | ||
else: | else: | ||
| Zeile 379: | Zeile 479: | ||
rem_name = packet[0][1].src | 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:" + | 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:" + packlen + " : " + o_payload | ||
| Zeile 390: | Zeile 490: | ||
conf.sniff_promisc=0 | conf.sniff_promisc=0 | ||
conf.sniff_promisc=0 | conf.sniff_promisc=0 | ||
sys.exit() | sys.exit() | ||
# get the interfaces | # get the interfaces | ||
ifaces = | ifaces = subprocess.getoutput("ls /sys/class/net") | ||
iface_list = ifaces.split('\n') | iface_list = ifaces.split('\n') | ||
print | rfilter = "ip or ip6" | ||
print("") | |||
# commandline params | # commandline params | ||
parser = argparse.ArgumentParser(description='sisniff V'+VERSION) | parser = argparse.ArgumentParser(description='sisniff V'+VERSION+"\n2017-2019 by sigi <https://wiki.zweiernet.ch/wiki/sisniff>", | ||
parser.add_argument('-i', help="Interface ( | formatter_class=argparse.RawDescriptionHelpFormatter) | ||
parser.add_argument('-i', help="Interface (required)", choices=iface_list, required=True) | |||
parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true") | parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true") | ||
parser.add_argument('-p', help='Filter by program name ([not-] negates)', type=str, metavar='program|not-program') | |||
parser.add_argument('-4', dest='v4', help="Only IPv4", action="store_true") | |||
parser.add_argument('-6', dest='v6', help="Only IPv6", action="store_true") | |||
parser.add_argument('-pH', help="Show HTTP Payload", action="store_true") | parser.add_argument('-pH', help="Show HTTP Payload", action="store_true") | ||
parser.add_argument('-pHl', help="Show HTTP Payload, long output", action="store_true") | |||
parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str) | parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str) | ||
args = parser.parse_args() | args = parser.parse_args() | ||
| Zeile 408: | Zeile 513: | ||
if args.n: | if args.n: | ||
numeric = True | numeric = True | ||
if args.v4: | |||
rfilter = "ip" | |||
if args.v6: | |||
rfilter = "ip6" | |||
if args.pH: | if args.pH: | ||
payloadH = True | payloadH = True | ||
if args.pHl: | |||
payloadH = True | |||
payloadHl = True | |||
if args.filter: | if args.filter: | ||
fillter = " and (" + args.filter + ")" | fillter = " and (" + args.filter + ")" | ||
print "> Applying Filter: \" | print("> Applying Filter: \"" + rfilter + fillter + "\"") | ||
if args.p: | |||
filter_prog = args.p | |||
# local addresses | # local addresses | ||
MYADDRS = _remove_empty( | if args.v6: | ||
MYADDRS.append('0.0.0.0') | MYADDRS=[] | ||
MYADDRS.append('127.0.0.1') | xMYADDRS = [] | ||
xMYADDRS = [_ip_hexrev(x) for x in MYADDRS] | else: | ||
print "> My IP-Addresses: " + str(MYADDRS) | MYADDRS = _remove_empty(os.popen("ip addr show " + iface + " | egrep 'inet ' | awk '{{print $2}}' | awk -F'/' '{{print $1}}'").read().split()) | ||
MYADDRS.append('0.0.0.0') | |||
MYADDRS.append('127.0.0.1') | |||
xMYADDRS = [_ip_hexrev(x) for x in MYADDRS] | |||
if args.v4: | |||
MYADDRS6=[] | |||
else: | |||
MYADDRS6 = _remove_empty(os.popen("ip addr show " + iface + " | egrep 'inet6' | grep -vi fe80 | awk '{{print $2}}' | awk -F'/' '{{print $1}}'").read().split()) | |||
MYADDRS6.append(':::') | |||
MYADDRS6.append('::1') | |||
MYADDRS = MYADDRS + MYADDRS6 | |||
xMYADDRS = xMYADDRS + [_to_v6_proc(expand_v6(x)) for x in MYADDRS6] | |||
print("> My IP-Addresses: " + str(MYADDRS)) | |||
# confirmed connections cache (ringboffer) | # confirmed connections cache (ringboffer) | ||
| Zeile 428: | Zeile 555: | ||
res_cache = {} | res_cache = {} | ||
n_try = 3 | n_try = 3 | ||
print | print("") | ||
print "Prog/PID mavericks: ?/? = No entry in /proc/net/xxx; -/- = No PID for Inode found; ./. = Inode=0;" | print("Prog/PID mavericks: \033[1m?/?\033[0m = No entry in /proc/net/xxx; \033[1m-/-\033[0m = No PID for Inode found; \033[1m./.\033[0m = Inode=0;") | ||
print | print("") | ||
print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]" | print("Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]") | ||
print "-------------------------------------------------------------------------------" | print("-------------------------------------------------------------------------------") | ||
# sniff, filtering for IP traffic | # sniff, filtering for IP traffic | ||
sniff(filter= | sniff(filter=rfilter+fillter,iface=iface,prn=doPackets, store=0) | ||
## -- oond denn isch schloss | ## -- oond denn isch schloss | ||
</syntaxhighlight></small> | |||
</syntaxhighlight> | |||
Version vom 16. Dezember 2019, 14:40 Uhr
Ein Netzwerk-Sniffer der für jedes Packet nebst Adresse und Port die lokal verbundene Anwendung und deren PID ermittelt und anzeigt.
Es wird TCP, UDP und ICMP unterstützt, für IPv4 und IPv6.
Der Sniffer akzeptiert Filter wie sie bei tcpdump üblich sind.
Bei HTTP Verbindungen kann ausserdem ein Teil der Payload angezeigt werden.
Die Option -h gibt eine Argumenteübersicht und listet die verfügbaren Interfaces auf.
# ./sisniff -h
usage: sisniff [-h] -i {eth0,lo,wlan0} [-n] [-p program|not-program] [-4] [-6] [-pH] [-pHl] [filter]
sisniff V1.00
2017-2019 by sigi <https://wiki.zweiernet.ch/wiki/sisniff>
positional arguments:
filter Filter (BPF syntax) on top of IP (in dbl-quotes "...")
optional arguments:
-h, --help show this help message and exit
-i {eth0,lo,wlan0} Interface (required)
-n Do not resolve IP-Addresses
-p program|not-program
Filter by program name ([not-] negates)
-4 Only IPv4
-6 Only IPv6
-pH Show HTTP Payload
-pHl Show HTTP Payload, long output
- Downloads der aktuellen Version
- Source: https://git.zweiernet.ch/sigi/sisniff/raw/master/sisniff
- gzipped: https://git.zweiernet.ch/sigi/sisniff/archive/master.tar.gz
- Git Projektseite: https://git.zweiernet.ch/sigi/sisniff
| i |
Da
|
Python Code:
#!/usr/bin/env python
# (c) 2017-2019 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch>
#
# All Rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
from scapy.all import *
import pwd
import os
import re
import glob
import sys
import string
import fcntl
import struct
import argparse
if sys.version_info.major == 2:
import commands as subprocess
elif sys.version_info.major == 3:
import subprocess
def _to_str(inp):
if sys.version_info.major == 2:
return inp
else:
return "".join( chr(x) for x in inp)
VERSION = "1.00"
PROC_TCP4 = "/proc/net/tcp"
PROC_UDP4 = "/proc/net/udp"
PROC_ICMP4 = "/proc/net/icmp"
PROC_TCP6 = "/proc/net/tcp6"
PROC_UDP6 = "/proc/net/udp6"
PROC_PACKET = "/proc/net/packet"
# Services
TSERV = dict((TCP_SERVICES[k], k) for k in TCP_SERVICES.keys())
USERV = dict((UDP_SERVICES[k], k) for k in UDP_SERVICES.keys())
# IP Protocol Numbers (dec)
IPPROTO_ICMP = 1
IPPROTO_TCP = 6
IPROTOP_IGP = 9
IPPROTO_UDP = 17
nostate = set(['04','05','06''07','08','09','0C','0D'])
tcp_payload_hdrs = ['GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT']
numeric = False
payloadH = False
payloadHl = False
fillter = ""
def get_conn_info(proto,hosts,ports,ipvers):
''' returns: pid, exe, uid '''
uid = 0
line_array = _proc4load(proto,hosts,ports,ipvers)
if line_array == 0:
return ['?','?','?']
'''
try:
uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID.
except:
uid = line_array[7]
'''
inode = str(line_array[9])
if inode == "0":
return ['.','.','.']
pid = _get_pid_of_inode(inode) # try get a pid
if pid == "NoPid":
#print(">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array))
return ['-', '-', 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,ipvers):
''' 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 from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h?id=HEAD
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! */
};
----------
'''
#xhosts = _ip_hexrev(hosts)
xports = _dec2hex(ports)
if proto == IPPROTO_UDP:
try:
procv = PROC_UDP4
if ipvers == 6:
procv = PROC_UDP6
with open(procv,'r') as f:
next(f)
for line in f:
line_arrayu = _remove_empty(line.split(' '))
l_xhost,l_xport = line_arrayu[1].split(':')
if l_xhost not in xMYADDRS:
continue
if l_xport == xports:
return line_arrayu
return 0
except:
print("open proc_udp4 error")
return 0
elif proto == IPPROTO_TCP:
try:
procv = PROC_TCP4
if ipvers == 6:
procv = PROC_TCP6
with open(procv,'r') as f:
next(f)
for line in f:
line_arrayt = _remove_empty(line.split(' '))
if line_arrayt[3] in nostate: # not some TCP state
continue
l_xhost,l_xport = line_arrayt[1].split(':')
if l_xhost not in xMYADDRS:
continue
if l_xport == xports:
return line_arrayt
return 0
except:
print("open proc_tcp error")
return 0
elif proto == IPPROTO_ICMP:
try:
procv = PROC_ICMP4
if ipvers == 6:
procv = PROC_ICMP6
with open(procv,'r') as f:
next(f)
for line in f:
line_arrayi = _remove_empty(line.split(' '))
l_xhost,l_xport = line_arrayi[1].split(':')
if l_xhost not in xMYADDRS:
continue
if l_xport == xports:
return line_arrayi
return 0
except:
print("open proc_icmp4 error")
return 0
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 _dec2hex(p):
return hex(int(p)).split('x')[-1].upper()
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[14:16],s[12:14],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]]
return ':'.join(ip)
def _ip_hexrev(ip):
return ''.join([hex(int(x)+256)[3:] for x in ip.split('.')][::-1]).upper()
# IPv6 /proc/net/tcp6 format from expanded ip-address
def _to_v6_proc(s):
s = s.replace(":", "")
ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[14:16],s[12:14],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]]
return ''.join(ip).upper()
def expand_v6(ip):
ipa = ip.split(':') # liste
if '' in ipa:
if ipa.count('') > 1: # korr ::1 or :::
for i in range(ipa.count('')-1):
ipa.remove('')
miss = 8 - len(ipa) +1
for i in range(miss):
ipa.insert(ipa.index('')+i, '0000')
ipa.remove('')
return ':'.join(["%04x" % x for x in [int(x, 16) for x in ipa]])
else:
return ':'.join(["%04x" % x for x in [int(x, 16) for x in ipa]])
def _remove_empty(array):
return [x for x in array if x != '']
def _get_pid_of_inode(inode):
s_term = r'^socket\:\['+ inode +r'\]$'
for item in glob.iglob('/proc/[0-9]*/fd/[0-9]*'):
try:
if re.match(s_term,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) or packet.haslayer(ICMP):
try:
conn_port = packet[0][2].sport
except:
conn_port = 99999
o_dir = 1
else:
conn_addr = packet[0][1].dst
if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):
try:
conn_port = packet[0][2].dport
except:
conn_port = 99999
o_dir = 0
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):
# 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
if packet[0][1].version == 4:
spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port, packet[0][1].version)
elif packet[0][1].version == 6:
spid,sexe,suid = get_conn_info(packet[0][1].nh, conn_addr, conn_port, packet[0][1].version)
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])
else:
program = sexe
pid = spid
uid = suid
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)
try:
filter_prog
except:
pass
else:
if filter_prog.startswith('not-'):
if program == filter_prog[4:]:
return
else:
if program != filter_prog:
return
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 = _to_str(packet[0].sprintf('%10s,UDP.payload%'))
elif 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%')
tpld = _to_str(packet[0][TCP].load)
tpldhead = tpld[0:8]
#print("tpld:" + tpldhead)
if re.match(r'GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*', tpldhead):
if payloadHl == True:
o_payload = str(tpld)
else:
request_line, gaga = tpld.split('\r\n', 1)
o_payload = str(request_line)
#o_payload = tpld[0:20]
elif packet.haslayer(ICMP):
o_proto = "ICMP"
if conn_port == 99999:
o_dport = "-"
o_sport = "-"
else:
try:
o_dport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
except:
o_dport = str(packet[0][2].sport)
try:
o_sport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
except:
o_sport = str(packet[0][2].dport)
flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]"
else:
layerukn = packet[0][1].getlayer(1)
if layerukn is None:
o_proto = "UNKNOWN"
else:
#print("Layer:", xxl1.name)
o_proto = layerukn.name
#o_proto = "UNKNOWN"
if packet[0][1].version == 4:
packlen = str(packet[0][1].len)
if packet[0][1].version == 6:
packlen = str(packet[0][1].plen)
if o_dir == 1:
if numeric == False:
#if res_cache.has_key(packet[0][1].dst):
if packet[0][1].dst in res_cache:
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(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
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:" + packlen + " : " + o_payload
else:
if numeric == False:
#if res_cache.has_key(packet[0][1].src):
if packet[0][1].src in res_cache:
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:" + packlen + " : " + 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
sys.exit()
# get the interfaces
ifaces = subprocess.getoutput("ls /sys/class/net")
iface_list = ifaces.split('\n')
rfilter = "ip or ip6"
print("")
# commandline params
parser = argparse.ArgumentParser(description='sisniff V'+VERSION+"\n2017-2019 by sigi <https://wiki.zweiernet.ch/wiki/sisniff>",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-i', help="Interface (required)", choices=iface_list, required=True)
parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true")
parser.add_argument('-p', help='Filter by program name ([not-] negates)', type=str, metavar='program|not-program')
parser.add_argument('-4', dest='v4', help="Only IPv4", action="store_true")
parser.add_argument('-6', dest='v6', help="Only IPv6", action="store_true")
parser.add_argument('-pH', help="Show HTTP Payload", action="store_true")
parser.add_argument('-pHl', help="Show HTTP Payload, long output", 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.v4:
rfilter = "ip"
if args.v6:
rfilter = "ip6"
if args.pH:
payloadH = True
if args.pHl:
payloadH = True
payloadHl = True
if args.filter:
fillter = " and (" + args.filter + ")"
print("> Applying Filter: \"" + rfilter + fillter + "\"")
if args.p:
filter_prog = args.p
# local addresses
if args.v6:
MYADDRS=[]
xMYADDRS = []
else:
MYADDRS = _remove_empty(os.popen("ip addr show " + iface + " | egrep 'inet ' | awk '{{print $2}}' | awk -F'/' '{{print $1}}'").read().split())
MYADDRS.append('0.0.0.0')
MYADDRS.append('127.0.0.1')
xMYADDRS = [_ip_hexrev(x) for x in MYADDRS]
if args.v4:
MYADDRS6=[]
else:
MYADDRS6 = _remove_empty(os.popen("ip addr show " + iface + " | egrep 'inet6' | grep -vi fe80 | awk '{{print $2}}' | awk -F'/' '{{print $1}}'").read().split())
MYADDRS6.append(':::')
MYADDRS6.append('::1')
MYADDRS = MYADDRS + MYADDRS6
xMYADDRS = xMYADDRS + [_to_v6_proc(expand_v6(x)) for x in MYADDRS6]
print("> My IP-Addresses: " + str(MYADDRS))
# confirmed connections cache (ringboffer)
conn_cache = []
cc_maxlen = 20
# resolver cache
res_cache = {}
n_try = 3
print("")
print("Prog/PID mavericks: \033[1m?/?\033[0m = No entry in /proc/net/xxx; \033[1m-/-\033[0m = No PID for Inode found; \033[1m./.\033[0m = Inode=0;")
print("")
print("Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]")
print("-------------------------------------------------------------------------------")
# sniff, filtering for IP traffic
sniff(filter=rfilter+fillter,iface=iface,prn=doPackets, store=0)
## -- oond denn isch schloss