def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") zipinfos = self.get_infos(path) self.extract_zip(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(zipinfos): # Take the first one. file_name = 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) return self.execute(file_path, self.options.get("arguments"))
def init_regkeys(self, regkeys): """Initializes the registry to avoid annoying popups, configure settings, etc. @param regkeys: the root keys, subkeys, and key/value pairs. """ for rootkey, subkey, values in regkeys: key_handle = CreateKey(rootkey, subkey) for key, value in values.items(): if isinstance(value, str): SetValueEx(key_handle, key, 0, REG_SZ, value) elif isinstance(value, int): SetValueEx(key_handle, key, 0, REG_DWORD, value) elif isinstance(value, dict): self.init_regkeys([ [rootkey, "%s\\%s" % (subkey, key), value], ]) else: raise CuckooPackageError("Invalid value type: %r" % value) CloseKey(key_handle)
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") dll_64 = self.options.get("dll_64") free = self.options.get("free") suspended = True if free: suspended = False kernel_analysis = self.options.get("kernel_analysis", False) if kernel_analysis is not False: kernel_analysis = True 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 is_64bit = p.is_64bit() if not kernel_analysis: if is_64bit: p.inject(INJECT_QUEUEUSERAPC, interest) else: p.inject(INJECT_QUEUEUSERAPC, interest) p.resume() p.close() return p.pid
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") exe_regex = re.compile('(\.exe|\.scr|\.msi|\.bat|\.lnk|\.js|\.jse|\.vbs|\.vbe|\.wsf)$',flags=re.IGNORECASE) zipinfos = self.get_infos(path) self.extract_zip(path, root, password, 0) 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) log.debug("file_name: \"%s\"" % (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) elif file_name.lower().endswith(".msi"): msi_path = self.get_path("msiexec.exe") msi_args = "/I \"{0}\"".format(file_path) return self.execute(msi_path, msi_args, file_path) elif file_name.lower().endswith((".js", ".jse", ".vbs", ".vbe", ".wsf")): wscript = self.get_path_app_in_path("wscript.exe") wscript_args = "\"{0}\"".format(file_path) return self.execute(wscript, wscript_args, file_path) else: return self.execute(file_path, self.options.get("arguments"), file_path)
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") gw = self.options.get("setgw", None) u = Utils() if gw: u.set_default_gw(gw) 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 run(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") 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 CAPE. Please run the " "CAPE 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, ], startupinfo=self.startupinfo, shell=True) # 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) if self.enabled: return True return False
def _execute_app(self): """Execute sample via activity manager. @raise CuckooError: failed to execute sample. """ log.info("Executing sample on the device via the activity manager.") try: args = [ "/system/bin/sh", "/system/bin/am", "start", "-n", "%s/%s" % (self.package, self.activity) ] p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode: raise OSError(err.decode()) log.info("Executed package activity: %s", out.decode()) except OSError as e: raise CuckooPackageError("Error executing package activity: %s" % e)
def start(self, path): free = self.options.get("free", False) dll = self.options.get("dll", None) 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 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 """ 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") p.debug_inject(interest, childprocess=False) p.resume() p.close() return p.pid
def start(self, url): free = self.options.get("free", False) dll = self.options.get("dll", None) 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 start(self, path): free = self.options.get("free", False) function = self.options.get("function", "DllMain") arguments = self.options.get("arguments", None) 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() p.resume() return p.pid else: return None
def execute(self, path, args): """Starts an executable for analysis. @param path: executable path @param args: executable arguments @return: process pid """ dll = self.options.get("dll") free = self.options.get("free") suspended = True if free: suspended = False p = Process() if not p.execute(path=path, args=args, suspended=suspended): raise CuckooPackageError("Unable to execute the initial process, " "analysis aborted.") if not free and suspended: p.inject(dll) p.resume() p.close() return p.pid
def get_package_activity_name_old(path): """Using the Android Asset Packaging Tool to extract from apk the package name and main activity""" log.info("getting package and main activity from sample : aapt dump badging "+path) str="" proc = subprocess.Popen(["aapt", "dump","badging", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) for s in proc.stdout.xreadlines(): str=str+s lines = str.split("\n") myDic = {} for line in lines: splitedline=line.split(":") if len(splitedline)==2: myKey,myValue=line.split(":") myDic[myKey]=myValue if not "package" in myDic or not 'launchable-activity' in myDic: #log.warning(str) raise CuckooPackageError("failed to get package and main activity from sample!") package = myDic['package'].split("'")[1] activity = myDic['launchable-activity'].split("'")[1] return package, activity
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 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 #requires bytes not str if not isinstance(password, bytes): password = password.encode("utf-8") # 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: 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): password = self.options.get("password") if password is None: password = b"" appdata = self.options.get("appdata") if appdata: root = os.environ["APPDATA"] else: root = os.environ["TEMP"] exe_regex = re.compile( '(\.exe|\.dll|\.scr|\.msi|\.bat|\.lnk|\.js|\.jse|\.vbs|\.vbe|\.wsf)$', flags=re.IGNORECASE) zipinfos = self.get_infos(path) self.extract_zip(path, root, password, 0) 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) log.debug("file_name: \"%s\"" % (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) elif file_name.lower().endswith(".msi"): msi_path = self.get_path("msiexec.exe") msi_args = "/I \"{0}\"".format(file_path) return self.execute(msi_path, msi_args, file_path) elif file_name.lower().endswith( (".js", ".jse", ".vbs", ".vbe", ".wsf")): wscript = self.get_path_app_in_path("wscript.exe") wscript_args = "\"{0}\"".format(file_path) return self.execute(wscript, wscript_args, file_path) elif file_name.lower().endswith(".dll"): rundll32 = self.get_path_app_in_path("rundll32.exe") function = self.options.get("function", "#1") arguments = self.options.get("arguments") dllloader = self.options.get("dllloader") dll_args = "\"{0}\",{1}".format(file_path, function) if arguments: dll_args += " {0}".format(arguments) if dllloader: newname = os.path.join(os.path.dirname(rundll32), dllloader) shutil.copy(rundll32, newname) rundll32 = newname return self.execute(rundll32, dll_args, file_path) elif file_name.lower().endswith(".ps1"): powershell = self.get_path_app_in_path("powershell.exe") args = "-NoProfile -ExecutionPolicy bypass -File \"{0}\"".format( path) return self.execute(powershell, args, file_path) else: if "." not in os.path.basename(file_path): new_path = file_path + ".exe" os.rename(file_path, new_path) file_path = new_path return self.execute(file_path, self.options.get("arguments"), file_path)
def process_unzipped_contents(self, unzipped_directory: str, json_filename: str) -> Tuple[str, str]: """Checks JSON to move the various files to.""" raw_json = extract_json_data(unzipped_directory, json_filename) json_dst_flds = raw_json.get("path_to_extract", {}) target_file = raw_json.get("target_file", "") # Enforce the requirement of having a specified file. No guessing. target_file = target_file or self.options.get("file") if not target_file: raise CuckooPackageError( "File must be specified in the JSON or the web submission UI!") elif not self.is_valid_extension(target_file): raise CuckooPackageError( "Invalid, unsupported or no extension recognised by zip_compound package" ) # In case the "file" submittion option is relative, we split here target_srcdir, target_name = os.path.split(target_file) # Note for 32bit samples: Even if JSON configutation specifies "System32", # wow64 redirection will still happen # Commented out since redirection-related issues should be uncommon. # if is_os_64bit(): # wow64 = c_ulong(0) # KERNEL32.Wow64DisableWow64FsRedirection(byref(wow64)) fin_target_path = os.path.join(unzipped_directory, target_file) # Move files that are specified in JSON file if json_dst_flds: for f, dst_fld in json_dst_flds.items(): oldpath = os.path.join(unzipped_directory, f) dst_fld = os.path.expandvars(dst_fld) create_custom_folders(dst_fld) # If a relative path is provided, take only the basename fname = os.path.split(f)[1] newpath = os.path.join(dst_fld, fname) # We cannot just shutil.move src dirs if src name == dst name. if os.path.isdir(oldpath): log.debug("Resolved Dir: %s for folder '%s'", dst_fld, fname) shutil.copytree(oldpath, newpath, dirs_exist_ok=True) shutil.rmtree(oldpath) else: log.debug("Resolved Dir: %s for file '%s'", dst_fld, fname) shutil.move(oldpath, newpath) if target_file.lower() == f.lower(): fin_target_path = newpath self.options["curdir"] = dst_fld log.debug("New curdir value: %s", self.options["curdir"]) # Only runs if a relative path is given for target file # Errors out if the file's containing folder is shifted # before shifting the target file first. if target_srcdir and not os.path.exists(fin_target_path): raise CuckooPackageError( "Error getting the correct path for the target file! \ Target file should be moved before moving its containing\ source folder") log.debug("Final target name: %s", target_name) log.info("Final target path: %s", fin_target_path) return target_name, fin_target_path
def start(self, path): """ @param path: 변환(분석)할 문서 파일의 경로 @return: 프로세스 ID """ # 확장자에 맞는 인쇄 명령을 찾음. # (ShellExecute에서 print 동사(verb) 사용 시 실행되는 명령어) file_path, file_ext = os.path.splitext(path) print_cmd = self.get_print_command(file_ext) if not print_cmd: raise CuckooPackageError('Not supported extension: ' + file_ext) # 인쇄 명령어에서 실행 프로그램의 경로와 각 파라미터를 분리 시킴. regex = re.compile(r'((?:(?:[^\s"]+)|"(?:""|[^"])*")+)(?:\s|$)') found = regex.findall(print_cmd['command']) if len(found) < 2: raise CuckooPackageError('Wrong print command: ' + print_cmd['command']) # 프로그램 실행 경로 execute_path = found[0] if execute_path.startswith('"'): execute_path = execute_path[1:-1] # 양 끝의 "(큰따옴표)를 제거함. # 프로그램 파라미터 execute_param = [] for param in found[1:]: if param.startswith('"') and param.endswith('"'): param = param[1:-1] # 양 끝의 "(큰따옴표)를 제거함. execute_param.append(param.replace('%1', '{}'.format(path))) pid = self.execute(execute_path, args=execute_param) # 일부 프로그램(특히 MS 오피스)의 경우 # 인쇄를 위해서는 DDE 통신이 필요함. if print_cmd['ddeexec']: log.debug('Need dde!!') if not print_cmd['application']: raise CuckooPackageError('No dde server name. dde info: ' + str(print_cmd)) if not print_cmd['topic']: raise CuckooPackageError('No dde topic. dde info: ' + str(print_cmd)) time.sleep(5) # dde 서버가 실행되는 동안 기다림 try: log.debug('dde start') server = dde.CreateServer() log.debug('dde server create1') server.Create('PrintClient') log.debug('dde server create2') conversation = dde.CreateConversation(server) log.debug('dde conversation created') conversation.ConnectTo(print_cmd['application'], print_cmd['topic']) log.debug('dde connected') conversation.Exec(print_cmd['ddeexec'].replace('%1', path)) log.debug('dde exec.') except Exception as e: log.error(str(e)) return pid
def start_package(self, config): """Start an analysis package. @param config: a dictionary containing at least a target category, options dictionary, and target string or list of targets returns a package id """ pkgname = config.get("package") if not pkgname: log.info( "No analysis package provided, trying to automatically find a " "matching package" ) pkg = choose_package(config) else: pkg = get_package_class(pkgname) if pkgname and not pkg: raise CuckooPackageError( "Could not find analysis package '%r'" % pkgname ) if not pkg: category = config.get("category") raise CuckooPackageError( "No valid analysis package available for target category '%s'." "%s" % ( category, config.get("file_name") if category == "file" else "" ) ) log.info("Using analysis package '%s'", pkg.__name__) options = config.get("options", {}) or {} pkg_instance = pkg(options=options, analyzer=self) category = config.get("category") if category == "file": target = os.path.join(os.environ["TEMP"], config.get("file_name")) pkg_instance.move_curdir(target) elif category == "archive": zippath = os.path.join(os.environ["TEMP"], config.get("file_name")) zipfile.ZipFile(zippath).extractall(os.environ["TEMP"]) if not options.get("filename"): raise CuckooPackageError( "No filename specified to open after unpacking archive" ) target = os.path.join(os.environ["TEMP"], options.get("filename")) elif category == "url": target = config.get("target") else: raise CuckooPackageError( "Unknown category '%s' specified" % category ) pids = pkg_instance.start(target) if pids: self.plist.add_pids(pids) self.pkg_counter += 1 pkg_id = str(config.get("pkg_id") or self.pkg_counter) self.packages[pkg_id] = pkg_instance return { "pkg_id": pkg_id, "name": pkg_instance.__class__.__name__, "pids": pkg_instance.pids_targets }
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") analysis = self.options.get("analysis") kernel_mode = self.options.get("kernelmode") kernel_pipe = self.options.get("kernel_logpipe", "\\\\.\\ThunderDefPipe") log_pipe = self.options.get("forwarderpipe") dispatcher_pipe = self.options.get("dispatcherpipe") driver_options = self.options.get("driver_options") package = type(self).__name__ # Kernel analysis overrides the free argument. if analysis == "kernel": free = True source = source or self.options.get("from") mode = mode or self.options.get("mode") if not trigger and self.options.get("trigger"): if self.options["trigger"] == "exefile": trigger = "file:%s" % path # Setup pre-defined registry keys. self.init_regkeys(self.REGKEYS) # check preloaded apps self._check_preloaded_apps() p = Process() if not p.execute(path=path, args=args, dll=dll, free=free, kernel_mode=kernel_mode, kernel_pipe=kernel_pipe, forwarder_pipe=log_pipe, dispatcher_pipe=dispatcher_pipe, destination=self.options.get("destination", ("localhost", 1)), curdir=self.curdir, source=source, mode=mode, maximize=maximize, env=env, trigger=trigger, driver_options=driver_options, package=package): raise CuckooPackageError( "Unable to execute the initial process, analysis aborted.") return p.pid
def start(self, path): root = os.environ["TEMP"] password = self.options.get("password") exe_regex = re.compile( r"(\.exe|\.scr|\.msi|\.bat|\.lnk|\.js|\.jse|\.vbs|\.vbe|\.wsf)$", flags=re.IGNORECASE) dll_regex = re.compile(r"(\.dll|\.ocx)$", flags=re.IGNORECASE) zipinfos = self.get_infos(path) self.extract_zip(path, root, password, 0) file_name = self.options.get("file") # If no file name is provided via option, take the first file. if file_name is None: # 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 if file_name is None: for f in zipinfos: if dll_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: %s", file_name) else: raise CuckooPackageError("Empty ZIP archive") file_path = os.path.join(root, file_name) log.debug('file_name: "%s"', file_name) if file_name.lower().endswith(".lnk"): cmd_path = self.get_path("cmd.exe") cmd_args = f'/c start /wait "" "{file_path}"' return self.execute(cmd_path, cmd_args, file_path) elif file_name.lower().endswith(".msi"): msi_path = self.get_path("msiexec.exe") msi_args = f'/I "{file_path}"' return self.execute(msi_path, msi_args, file_path) elif file_name.lower().endswith( (".js", ".jse", ".vbs", ".vbe", ".wsf")): wscript = self.get_path_app_in_path("wscript.exe") wscript_args = f'"{file_path}"' return self.execute(wscript, wscript_args, file_path) elif file_name.lower().endswith((".dll", ".ocx")): rundll32 = self.get_path_app_in_path("rundll32.exe") function = self.options.get("function", "#1") arguments = self.options.get("arguments") dllloader = self.options.get("dllloader") dll_args = f'"{file_path}",{function}' if arguments: dll_args += f" {arguments}" if dllloader: newname = os.path.join(os.path.dirname(rundll32), dllloader) shutil.copy(rundll32, newname) rundll32 = newname return self.execute(rundll32, dll_args, file_path) elif file_name.lower().endswith(".ps1"): powershell = self.get_path_app_in_path("powershell.exe") args = f'-NoProfile -ExecutionPolicy bypass -File "{path}"' return self.execute(powershell, args, file_path) else: return self.execute(file_path, self.options.get("arguments"), file_path)