def extract_zip(self, zip_path, extract_path, password=b"infected", recursion_depth=1): """Extracts a nested ZIP file. @param zip_path: ZIP path @param extract_path: where to extract @param password: ZIP password @param recursion_depth: how deep we are in a nested archive """ # Test if zip file contains a file named as itself. if self.is_overwritten(zip_path): log.debug("ZIP file contains a file with the same name, original is going to be overwritten") # TODO: add random string. new_zip_path = f"{zip_path}.old" shutil.move(zip_path, new_zip_path) zip_path = new_zip_path # requires bytes not str if not isinstance(password, bytes): password = password.encode() # Extraction. with ZipFile(zip_path, "r") as archive: # Check if the archive is encrypted for zip_info in archive.infolist(): is_encrypted = zip_info.flag_bits & 0x1 # If encrypted and the user didn't provide a password # set to default value if is_encrypted and password == b"": log.debug("Achive is encrypted and user did not provide a password, using default value: infected") password = b"infected" # Else, either password stays as user specified or archive is not encrypted try: archive.extractall(path=extract_path, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") from e except RuntimeError: try: archive.extractall(path=extract_path, pwd=password) except RuntimeError as e: raise CuckooPackageError(f"Unable to extract Zip file: {e}") from e finally: if recursion_depth < 4: # Extract nested archives. for name in archive.namelist(): if name.endswith(".zip"): # Recurse. try: self.extract_zip( os.path.join(extract_path, name), extract_path, password=password, recursion_depth=recursion_depth + 1, ) except BadZipfile: log.warning( "Nested zip file '%s' name end with 'zip' extension is not a valid zip. Skip extracting", name ) except RuntimeError as run_err: log.error("Error to extract nested zip file %s with details: %s", name, run_err)
def start(self, path): wscript = self.get_path() if not wscript: raise CuckooPackageError("Unable to find any WScript " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) suspended = True if free: suspended = False p = Process() if not p.execute(path=wscript, args="\"{0}\"".format(path), suspended=suspended): raise CuckooPackageError("Unable to execute initial WScript " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") with ZipFile(path, "r") as archive: zipinfos = archive.infolist() try: archive.extractall(path=root, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: archive.extractall(path=root, pwd="infected") except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) file_name = self.options.get("file") # If no file name is provided via option, take the first file. if not file_name: # No name provided try to find a better name. if len(zipinfos): # Take the first one. file_name = zipinfos[0].filename else: raise CuckooPackageError("Empty ZIP archive") file_path = os.path.join(root, file_name) return self.execute(file_path, self.options.get("arguments"))
def extract_rar(self, rar_path, extract_path, password): """Extracts a nested RAR file. @param rar_path: RAR path @param extract_path: where to extract @param password: RAR password """ # Test if rar file contains a file named as itself. if self.is_overwritten(rar_path): log.debug( "RAR file contains a file with the same name, original is going to be overwrite" ) # TODO: add random string. new_rar_path = rar_path + ".old" shutil.move(rar_path, new_rar_path) rar_path = new_rar_path # Extraction. with RarFile(rar_path, "r") as archive: try: archive.extractall(path=extract_path, pwd=password) except BadRarFile: raise CuckooPackageError("Invalid Rar file") except RuntimeError: try: archive.extractall(path=extract_path, pwd="infected") except RuntimeError as e: raise CuckooPackageError("Unable to extract Rar file: " "{0}".format(e)) finally: # Extract nested archives. for name in archive.namelist(): if name.endswith(".rar"): # Recurse. self.extract_rar(os.path.join(extract_path, name), extract_path, password)
def start(self, path): browser = self.get_path() if not browser: raise CuckooPackageError("Unable to find any browser " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) class_name = self.options.get("class", None) suspended = True if free: suspended = False html_path = self.make_html(path, class_name) p = Process() if not p.execute( path=browser, args="\"%s\"" % html_path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): word = self.get_path() if not word: raise CuckooPackageError("Unable to find any Microsoft " "Office Word executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=word, args="\"%s\"" % path, suspended=suspended): raise CuckooPackageError("Unable to execute initial Microsoft " "Office Word process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): powershell = self.get_path() if not powershell: raise CuckooPackageError( "Unable to find any PowerShell executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False args = "-NoProfile -ExecutionPolicy unrestricted -File \"{0}\"".format( path) p = Process() if not p.execute(path=powershell, args=args, suspended=suspended): raise CuckooPackageError( "Unable to execute initial PowerShell process, analysis aborted" ) if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def start(self, path): root = os.environ["TEMP"] with ZipFile(path, "r") as archive: try: archive.extractall(root) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: archive.extractall(path=root, pwd="infected") except RuntimeError as e: raise CuckooPackageError( "Unable to extract Zip file, unknown password?") file_path = os.path.join(root, self.options.get("file", "sample.exe")) free = self.options.get("free", False) args = self.options.get("arguments", None) suspended = True if free: suspended = False p = Process() if not p.execute(path=file_path, args=args, suspended=suspended): raise CuckooPackageError( "Unable to execute initial process, analysis aborted") if not free and suspended: p.inject() p.resume() return p.pid else: return None
def extract_zip(self, zip_path, extract_path, password, recursion_depth): """Extracts a nested ZIP file. @param zip_path: ZIP path @param extract_path: where to extract @param password: ZIP password @param recursion_depth: how deep we are in a nested archive """ # Test if zip file contains a file named as itself. if self.is_overwritten(zip_path): log.debug("ZIP file contains a file with the same name, original is going to be overwrite") # TODO: add random string. new_zip_path = zip_path + ".old" shutil.move(zip_path, new_zip_path) zip_path = new_zip_path # Extraction. with ZipFile(zip_path, "r") as archive: try: archive.extractall(path=extract_path, pwd=password) except BadZipfile: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: archive.extractall(path=extract_path, pwd="infected") except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) finally: if recursion_depth < 4: # Extract nested archives. for name in archive.namelist(): if name.endswith(".zip"): # Recurse. self.extract_zip(os.path.join(extract_path, name), extract_path, password, recursion_depth + 1)
def start(self, path): java = self.get_path() if not java: raise CuckooPackageError("Unable to find any Java " "executable available") dll = self.options.get("dll", None) free = self.options.get("free", False) class_path = self.options.get("class", None) suspended = True if free: suspended = False if class_path: args = "-cp \"%s\" %s" % (path, class_path) else: args = "-jar \"%s\"" % path p = Process() if not p.execute(path=java, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial Java " "process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def extract_zip(self, zip_path, extract_path, password=b"infected", recursion_depth=1): """Extracts a nested ZIP file. @param zip_path: ZIP path @param extract_path: where to extract @param password: ZIP password @param recursion_depth: how deep we are in a nested archive """ # Test if zip file contains a file named as itself. if self.is_overwritten(zip_path): log.debug( "ZIP file contains a file with the same name, original is going to be overwrite" ) # TODO: add random string. new_zip_path = zip_path + ".old" shutil.move(zip_path, new_zip_path) zip_path = new_zip_path if not isinstance(password, bytes): password = password.encode("utf-8") # Extraction. with ZipFile(zip_path, "r") as archive: try: archive.extractall(path=extract_path, pwd=password) except BadZipfile: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: archive.extractall(path=extract_path, pwd=password) except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) finally: if recursion_depth < 4: # Extract nested archives. for name in archive.namelist(): if name.endswith(".zip"): # Recurse. try: self.extract_zip( os.path.join(extract_path, name), extract_path, password=password, recursion_depth=recursion_depth + 1) except BadZipfile: log.warning( "Nested zip file '%s' name end with 'zip' extension is not a valid zip. Skip extracting" % name) except RuntimeError as run_err: log.error( "Error to extract nested zip file %s with details: %s" % name, run_err)
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password", None) default_file_name = "sample.exe" with ZipFile(path, "r") as archive: zipinfos = archive.infolist() try: archive.extractall(path=root, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: password = self.options.get("password", "infected") archive.extractall(path=root, pwd=password) except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) file_name = self.options.get("file", default_file_name) if file_name == default_file_name: #no name provided try to find a better name if len(zipinfos) > 0: #take the first one file_name = zipinfos[0].filename file_path = os.path.join(root, file_name) dll = self.options.get("dll", None) free = self.options.get("free", False) args = self.options.get("arguments", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=file_path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def extract_zip(self, zip_path, extract_path, password): """Extracts a nested ZIP file. @param zip_path: ZIP path @param extract_path: where to extract @param password: ZIP password """ # Test if zip file contains a file named as itself. if self.is_overwritten(zip_path): log.debug("ZIP file contains a file with the same name, " "original is going to be overwrite") # TODO: add random string. new_zip_path = zip_path + ".old" shutil.move(zip_path, new_zip_path) zip_path = new_zip_path # Extraction. with ZipFile(zip_path, "r") as archive: try: for zipinfo in archive.infolist(): try: zipinfo.filename = zipinfo.filename.decode( 'utf8').encode('utf8') except UnicodeDecodeError: zipinfo.filename = zipinfo.filename.decode( 'cp866').encode('utf8') archive.extract(zipinfo, path=extract_path, pwd=password) #archive.extractall(path=extract_path, pwd=password) except BadZipfile: raise CuckooPackageError("Invalid Zip file") except RuntimeError: try: #archive.extractall(path=extract_path, pwd="infected") for zipinfo in archive.infolist(): try: zipinfo.filename = zipinfo.filename.decode( 'utf8').encode('utf8') except UnicodeDecodeError: zipinfo.filename = zipinfo.filename.decode( 'cp866').encode('utf8') archive.extract(zipinfo, path=extract_path, pwd="infected") except RuntimeError as e: raise CuckooPackageError("Unable to extract Zip file: " "{0}".format(e)) finally: # Extract nested archives. for name in archive.namelist(): if name.endswith(".zip"): # Recurse. self.extract_zip(os.path.join(extract_path, name), extract_path, password)
def extract_zip(self, zip_path, extract_path, password, recursion_depth): """Extracts a nested ZIP file. @param zip_path: ZIP path @param extract_path: where to extract @param password: ZIP password @param recursion_depth: how deep we are in a nested archive """ # Test if zip file contains a file named as itself. if self.is_overwritten(zip_path): log.debug( "ZIP file contains a file with the same name, original is going to be overwritten" ) # TODO: add random string. new_zip_path = f"{zip_path}.old" shutil.move(zip_path, new_zip_path) zip_path = new_zip_path # Unpacker. with ZipFile(zip_path, "r") as archive: try: archive.extractall(path=extract_path, pwd=password) except BadZipfile as e: raise CuckooPackageError("Invalid Zip file") from e except RuntimeError: try: archive.extractall(path=extract_path, pwd="infected") except RuntimeError as e: raise CuckooPackageError( f"Unable to extract Zip file: {e}") from e finally: if recursion_depth < 4: # Extract nested archives. for name in archive.namelist(): if name.endswith(".zip"): # Recurse. try: self.extract_zip( os.path.join(extract_path, name), extract_path, password, recursion_depth + 1) except BadZipfile: log.warning( "Nested zip file '%s' name end with 'zip' extension is not a valid zip, skipping extraction", name, ) except RuntimeError as run_err: log.error( "Error to extract nested zip file %s with details: %s", name, run_err)
def debug(self, path, args, interest): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param interest: file of interest, passed to the cuckoomon config @return: process pid """ dll = self.options.get("dll") dll_64 = self.options.get("dll_64") gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True p = Process(options=self.options, config=self.config) if not p.execute( path=path, args=args, suspended=suspended, kernel_analysis=False): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") is_64bit = p.is_64bit() if is_64bit: p.debug_inject(dll_64, interest, childprocess=False) else: p.debug_inject(dll, interest, childprocess=False) p.resume() p.close() return p.pid
def execute(self, path, args, interest): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param interest: file of interest, passed to the cuckoomon config @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") suspended = True if free: suspended = False kernel_analysis = self.options.get("kernel_analysis", False) if kernel_analysis != False: kernel_analysis = True p = Process() if not p.execute(path=path, args=args, suspended=suspended, kernel_analysis=kernel_analysis): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if free: return None if not kernel_analysis: p.inject(dll, interest) p.resume() p.close() return p.pid
def get_path(self, application): for path in self._enum_paths(): if os.path.exists(path): return path raise CuckooPackageError("Unable to find any %s executable." % application)
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") exe_regex = re.compile('(\.exe|\.scr|\.msi|\.bat|\.lnk)$', flags=re.IGNORECASE) rarinfos = self.get_infos(path) self.extract_rar(path, root, password) file_name = self.options.get("file") # If no file name is provided via option, take the first file. if not file_name: # No name provided try to find a better name. if len(rarinfos): # Attempt to find a valid exe extension in the archive for f in rarinfos: if exe_regex.search(f.filename): file_name = f.filename break # Default to the first one if none found file_name = file_name if file_name else rarinfos[0].filename log.debug("Missing file option, auto executing: {0}".format( file_name)) else: raise CuckooPackageError("Empty RAR archive") file_path = os.path.join(root, file_name) return self.execute(file_path, self.options.get("arguments"), file_path)
def start(self, path): free = self.options.get("free", False) function = self.options.get("function", "DllMain") arguments = self.options.get("arguments", None) dll = self.options.get("dll", None) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False args = "{0},{1}".format(path, function) if arguments: args += " {0}".format(arguments) p = Process() if not p.execute(path="C:\\WINDOWS\\system32\\rundll32.exe", args=args, suspended=suspended): raise CuckooPackageError("Unable to execute rundll32, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def execute(self, path, args, mode=None, maximize=False, env=None, source=None, trigger=None): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param mode: monitor mode - which functions to instrument @param maximize: whether the GUI should start maximized @param env: additional environment variables @param source: parent process of our process @param trigger: trigger to indicate analysis start @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") source = source or self.options.get("from") mode = mode or self.options.get("mode") # Setup pre-defined registry keys. self.init_regkeys(self.REGKEYS) p = Process() if not p.execute(path=path, args=args, dll=dll, free=free, curdir=self.curdir, source=source, mode=mode, maximize=maximize, env=env, trigger=trigger): raise CuckooPackageError( "Unable to execute the initial process, analysis aborted." ) return p.pid
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False cmd_path = os.path.join(os.getenv("SystemRoot"), "system32", "cmd.exe") cmd_args = "/c start \"{0}\"".format(path) p = Process() if not p.execute(path=cmd_path, args=cmd_args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid else: return None
def start(self, target): """Run analysis package. @param target: sample path. """ self._install_app(target) pid = None if self.frida_client: try: # Spawn the app process with Frida.. pid = self.frida_client.spawn(self.package, self.activity) except CuckooFridaError as e: log.error("Failed to spawn application process with Frida: %s", e) if pid is None: # Try starting it via the activity manager. self._execute_app() timeout = 10 cnt = 0 while True: pid = self._get_pid() if pid is not None: break if cnt > timeout: raise CuckooPackageError( "Failed to execute application. Process not started.") else: time.sleep(1) self.add_pid(pid)
def start(self, path): free = self.options.get("free", False) args = self.options.get("arguments", None) dll = self.options.get("dll", None) gw = self.options.get("setgw",None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute initial process, " "analysis aborted") if not free and suspended: p.inject(dll) p.resume() self.run_ie() p.close() return p.pid else: self.run_ie() return None
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") exe_regex = re.compile('(\.exe|\.scr|\.msi|\.bat|\.lnk)$', flags=re.IGNORECASE) zipinfos = self.get_infos(path) self.extract_zip(path, root, password, 0) self.options["dll"] = "CAPE_PlugX_fuzzy.dll" file_name = self.options.get("file") # If no file name is provided via option, take the first file. if not file_name: # No name provided try to find a better name. if len(zipinfos): # Attempt to find a valid exe extension in the archive for f in zipinfos: if exe_regex.search(f.filename): file_name = f.filename break # Default to the first one if none found file_name = file_name if file_name else zipinfos[0].filename log.debug("Missing file option, auto executing: {0}".format( file_name)) else: raise CuckooPackageError("Empty ZIP archive") file_path = os.path.join(root, file_name) if file_name.lower().endswith(".lnk"): cmd_path = self.get_path("cmd.exe") cmd_args = "/c start /wait \"\" \"{0}\"".format(file_path) return self.execute(cmd_path, cmd_args, file_path) else: return self.execute(file_path, self.options.get("arguments"), file_path)
def start(self): if not self.enabled: return False bin_path = os.path.join(ROOT, "bin") self.procmon_exe = os.path.join(bin_path, "procmon.exe") self.procmon_pmc = os.path.join(bin_path, "procmon.pmc") self.procmon_pml = os.path.join(bin_path, "procmon.pml") self.procmon_xml = os.path.join(bin_path, "procmon.xml") if not os.path.exists(self.procmon_exe) or \ not os.path.exists(self.procmon_pmc): raise CuckooPackageError( "In order to use the Process Monitor functionality it is " "required to have Procmon setup with Cuckoo. Please run the " "Cuckoo Community script which will automatically fetch all " "related files to get you up-and-running." ) # Start process monitor in the background. subprocess.Popen([ self.procmon_exe, "/AcceptEula", "/Quiet", "/Minimized", "/BackingFile", self.procmon_pml, ]) # Try to avoid race conditions by waiting until at least something # has been written to the log file. while not os.path.exists(self.procmon_pml) or \ not os.path.getsize(self.procmon_pml): time.sleep(0.1)
def unzip(self, path): with ZipFile(path, "r") as archive: try: archive.extractall(path=".") except BaseException as exc: raise CuckooPackageError( "Something went wrong with the zipfile: {}".format(exc))
def execute(self, path, args, mode=None, maximize=False): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param mode: monitor mode - which functions to instrument @param maximize: whether the GUI should start maximized @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") source = self.options.get("from") # Setup pre-defined registry keys. self.init_regkeys(self.REGKEYS) p = Process() if not p.execute(path=path, args=args, dll=dll, free=free, curdir=self.curdir, source=source, mode=mode, maximize=maximize): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") return p.pid
def start(self, url): free = self.options.get("free", False) dll = self.options.get("dll", None) gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) suspended = True if free: suspended = False iexplore = os.path.join(os.getenv("ProgramFiles"), "Internet Explorer", "iexplore.exe") p = Process() if not p.execute( path=iexplore, args="\"%s\"" % url, suspended=suspended): raise CuckooPackageError("Unable to execute initial Internet " "Explorer process, analysis aborted") if not free and suspended: p.inject(dll) p.resume() return p.pid else: return None
def execute(self, path, args): p = Process() if not p.execute(path=path, args=args, suspended=True): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") return p.pid
def execute(self, path, args, interest): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @param interest: file of interest, passed to the cuckoomon config @return: process pid """ free = self.options.get("free", False) suspended = not free kernel_analysis = bool(self.options.get("kernel_analysis", False)) p = Process(options=self.options, config=self.config) if not p.execute(path=path, args=args, suspended=suspended, kernel_analysis=kernel_analysis): raise CuckooPackageError( "Unable to execute the initial process, analysis aborted") if free: return None if not kernel_analysis: p.inject(INJECT_QUEUEUSERAPC, interest) p.resume() p.close() return p.pid