├── .gitignore ├── README.md └── net-sniffer.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple building block script for inspecting packets flying across an interface/pcap file. Concatenates fragmented packets. Prints a 1 line packet summary if the packet has a TCP layer and a load. Meant to be built out to find specific information from packets. 2 | 3 | 4 | Auto-detect the interface to sniff 5 | 6 | ```sudo python net-sniffer.py``` 7 | 8 | 9 | Choose eth0 as the interface 10 | 11 | ```sudo python net-sniffer.py -i eth0``` 12 | 13 | 14 | Read from pcap 15 | 16 | ```sudo python net-sniffer.py -p pcapfile``` 17 | -------------------------------------------------------------------------------- /net-sniffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from os import geteuid, devnull 4 | import logging 5 | logging.getLogger("scapy.runtime").setLevel(logging.ERROR) 6 | from scapy.all import * 7 | conf.verb=0 8 | from sys import exit 9 | import argparse 10 | import signal 11 | from base64 import b64decode 12 | from urllib import unquote 13 | from subprocess import Popen, PIPE 14 | from collections import OrderedDict 15 | from IPython import embed 16 | 17 | DN = open(devnull, 'w') 18 | pkt_frag_loads = OrderedDict() 19 | 20 | def parse_args(): 21 | """Create the arguments""" 22 | parser = argparse.ArgumentParser() 23 | parser.add_argument("-i", "--interface", help="Choose an interface") 24 | parser.add_argument("-p", "--pcap", help="Parse info from a pcap file; -p ") 25 | return parser.parse_args() 26 | 27 | def iface_finder(): 28 | try: 29 | ipr = Popen(['/sbin/ip', 'route'], stdout=PIPE, stderr=DN) 30 | for line in ipr.communicate()[0].splitlines(): 31 | if 'default' in line: 32 | l = line.split() 33 | iface = l[4] 34 | return iface 35 | except Exception: 36 | exit('[-] Could not find an internet active interface; please specify one with -i ') 37 | 38 | def frag_remover(ack, load): 39 | ''' 40 | Keep the OrderedDict of frag loads from getting too large 41 | 3 points of limit: number of IP:port keys, number of different acks, and len of ack 42 | Number of ip_ports < 50 43 | Number of acks per ip:port < 25 44 | Number of chars in load < 5,000 45 | ''' 46 | global pkt_frag_loads 47 | 48 | # Keep the number of IP:port mappings below 50 49 | # last=False pops the oldest item rather than the latest 50 | while len(pkt_frag_loads) > 50: 51 | pkt_frag_loads.popitem(last=False) 52 | 53 | # Loop through a deep copy dict but modify the original dict 54 | copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) 55 | for ip_port in copy_pkt_frag_loads: 56 | if len(copy_pkt_frag_loads[ip_port]) > 0: 57 | # Keep 25 ack:load's per ip:port 58 | while len(copy_pkt_frag_loads[ip_port]) > 25: 59 | pkt_frag_loads[ip_port].popitem(last=False) 60 | 61 | # Recopy the new dict to prevent KeyErrors for modifying dict in loop 62 | copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) 63 | for ip_port in copy_pkt_frag_loads: 64 | # Keep the load less than 75,000 chars 65 | for ack in copy_pkt_frag_loads[ip_port]: 66 | if len(copy_pkt_frag_loads[ip_port][ack]) > 5000: 67 | # If load > 5,000 chars, just keep the last 200 chars 68 | pkt_frag_loads[ip_port][ack] = pkt_frag_loads[ip_port][ack][-200:] 69 | 70 | def frag_joiner(ack, src_ip_port, load): 71 | ''' 72 | Keep a store of previous fragments in an OrderedDict named pkt_frag_loads 73 | ''' 74 | for ip_port in pkt_frag_loads: 75 | if src_ip_port == ip_port: 76 | if ack in pkt_frag_loads[src_ip_port]: 77 | # Make pkt_frag_loads[src_ip_port][ack] = full load 78 | old_load = pkt_frag_loads[src_ip_port][ack] 79 | concat_load = old_load + load 80 | return OrderedDict([(ack, concat_load)]) 81 | 82 | return OrderedDict([(ack, load)]) 83 | 84 | def pkt_parser(pkt): 85 | ''' 86 | Start parsing packets here 87 | ''' 88 | global pkt_frag_loads 89 | 90 | # Get rid of Ethernet pkts with just a raw load cuz these are usually network controls like flow control 91 | if pkt.haslayer(Ether) and pkt.haslayer(Raw) and not pkt.haslayer(IP): 92 | pass 93 | 94 | elif pkt.haslayer(TCP) and pkt.haslayer(Raw): 95 | print pkt.summary() 96 | 97 | ack = str(pkt[TCP].ack) 98 | src_ip_port = str(pkt[IP].src) + ':' + str(pkt[TCP].sport) 99 | load = pkt[Raw].load 100 | frag_remover(ack, load) 101 | pkt_frag_loads[src_ip_port] = frag_joiner(ack, src_ip_port, load) 102 | full_load = pkt_frag_loads[src_ip_port][ack] 103 | 104 | ########################################### 105 | # DO PACKET INSPECTION HERE USING full_load 106 | ########################################### 107 | 108 | 109 | def main(args): 110 | 111 | ############################### DEBUG ############### 112 | # Hit Ctrl-C while program is running and you can see 113 | # whatever variable you want within the IPython cli 114 | #def signal_handler(signal, frame): 115 | # embed() 116 | # sniff(iface=conf.iface, prn=pkt_parser, store=0) 117 | #signal.signal(signal.SIGINT, signal_handler) 118 | ##################################################### 119 | 120 | # Check for root 121 | if geteuid(): 122 | exit('[-] Please run as root') 123 | 124 | #Find the active interface 125 | if args.interface: 126 | conf.iface = args.interface 127 | else: 128 | conf.iface = iface_finder() 129 | 130 | # Read packets from either pcap or interface 131 | if args.pcap: 132 | try: 133 | pcap = rdpcap(pcap_file) 134 | except Exception: 135 | exit('[-] Could not open %s' % pcap_file) 136 | for pkt in pcap: 137 | pkt_parser(pkt) 138 | else: 139 | sniff(iface=conf.iface, prn=pkt_parser, store=0) 140 | 141 | 142 | if __name__ == "__main__": 143 | main(parse_args()) 144 | --------------------------------------------------------------------------------