def test_mark_config(): class sig(Signature): name = "foobar" def on_complete(self): self.mark_config({ "family": "foobar", "cnc": "thisiscnc.com", "url": [ "url1", "url2", ], }) return True rs = RunSignatures({ "metadata": {}, }) rs.signatures = sig(rs), sig(rs) rs.run() assert rs.results["metadata"] == { "cfgextr": [{ "family": "foobar", "cnc": [ "thisiscnc.com", ], "url": [ "url1", "url2", ], }], }
def test_signature_order(): class sig(object): enabled = True minimum = "2.0.0" maximum = None platform = "windows" marks = [] def __init__(self, caller): pass class sig1(sig): name = "sig1" order = 3 class sig2(sig): name = "sig2" order = 1 class sig3(sig): name = "sig3" order = 2 with mock.patch("cuckoo.core.plugins.cuckoo") as p: p.signatures = sig1, sig2, sig3 rs = RunSignatures({}) assert isinstance(rs.signatures[0], sig2) assert isinstance(rs.signatures[1], sig3) assert isinstance(rs.signatures[2], sig1)
def test_signature_severity(p): class sig(object): name = "foobar" matched = True severity = 42 marks = [] def init(self): pass def on_complete(self): pass def results(self): return self.__class__.__dict__ rs = RunSignatures({}) rs.signatures = sig(), rs.run() assert p.debug.call_count == 2 assert p.debug.call_args_list[1][1]["extra"] == { "action": "signature.match", "status": "success", "signature": "foobar", "severity": 42, }
def test_signature_order(): class sig(Signature): enabled = True minimum = "2.0.0" maximum = None platform = "windows" marks = [] def __init__(self, caller): pass class sig1(sig): name = "sig1" order = 3 class sig2(sig): name = "sig2" order = 1 class sig3(sig): name = "sig3" order = 2 set_cwd(tempfile.mkdtemp()) cuckoo_create() with mock.patch("cuckoo.core.plugins.cuckoo") as p: p.signatures = sig1, sig2, sig3 RunSignatures.init_once() rs = RunSignatures({}) assert isinstance(rs.signatures[0], sig2) assert isinstance(rs.signatures[1], sig3) assert isinstance(rs.signatures[2], sig1)
def test_load_signatures(): set_cwd(tempfile.mkdtemp()) cuckoo_create() shutil.rmtree(cwd("signatures")) shutil.copytree("tests/files/enumplugins", cwd("signatures")) sys.modules.pop("signatures", None) load_signatures() # Ensure that the Signatures are loaded in the global list. names = [] for sig in cuckoo.signatures: names.append(sig.__module__) assert "signatures.sig1" in names assert "signatures.sig2" in names assert "signatures.sig3" in names # Ensure that the Signatures are loaded in the RunSignatures object. RunSignatures.init_once() rs, names = RunSignatures({}), [] for sig in rs.signatures: names.append(sig.__class__.__name__) assert "Sig1" in names assert "Sig2" in names assert "Sig3" in names
def process_results(self): """Process the analysis results and generate the enabled reports.""" logger("Starting task reporting", action="task.report", status="pending") # TODO Refactor this function as currently "cuckoo process" has a 1:1 # copy of its code. TODO Also remove "archive" files. results = RunProcessing(task=self.task).run() RunSignatures(results=results).run() RunReporting(task=self.task, results=results).run() # If the target is a file and the user enabled the option, # delete the original copy. if self.task.category == "file" and self.cfg.cuckoo.delete_original: if not os.path.exists(self.task.target): log.warning( "Original file does not exist anymore: \"%s\": " "File not found.", self.task.target) else: try: os.remove(self.task.target) except OSError as e: log.error( "Unable to delete original file at path " "\"%s\": %s", self.task.target, e) # If the target is a file and the user enabled the delete copy of # the binary option, then delete the copy. if self.task.category == "file" and self.cfg.cuckoo.delete_bin_copy: if not os.path.exists(self.binary): log.warning( "Copy of the original file does not exist anymore: \"%s\": File not found", self.binary) else: try: os.remove(self.binary) except OSError as e: log.error( "Unable to delete the copy of the original file at path \"%s\": %s", self.binary, e) # Check if the binary in the analysis directory is an invalid symlink. If it is, delete it. if os.path.islink(self.storage_binary) and not os.path.exists( self.storage_binary): try: os.remove(self.storage_binary) except OSError as e: log.error( "Unable to delete symlink to the binary copy at path \"%s\": %s", self.storage_binary, e) log.info("Task #%d: reports generation completed", self.task.id, extra={ "action": "task.report", "status": "success", }) return True
def test_should_enable_signature(): rs = RunSignatures({}) rs.version = "2.0.0" class sig_not_enabled(object): enabled = False assert not rs.should_enable_signature(sig_not_enabled) class sig_empty_name(object): enabled = True name = None assert not rs.should_enable_signature(sig_empty_name) class sig_enable_false(object): enabled = True name = "enable_false" minimum = "2.0.0" maximum = None def enable(self): return False assert not rs.should_enable_signature(sig_enable_false()) class sig_enable_true(object): enabled = True name = "enable_true" minimum = "2.0.0" maximum = None platform = None def enable(self): return True assert rs.should_enable_signature(sig_enable_true()) class sig_empty_platform(object): enabled = True name = "empty_platform" minimum = "2.0.0" maximum = None platform = None assert rs.should_enable_signature(sig_empty_platform()) class sig_other_platform(object): enabled = True name = "other_platform" minimum = "2.0.0" maximum = None platform = "nope" assert not rs.should_enable_signature(sig_other_platform())
def test_cfgextr(): set_cwd(tempfile.mkdtemp()) cuckoo_create() class Trigger1(Extractor): yara_rules = "Trigger1" def handle_yara(self, filepath, match): self.push_config({ "family": "barfoo", "version": "baz", }) ExtractManager.init_once() mkdir(cwd(analysis=1)) em = ExtractManager(1) em.handle_yara( None, YaraMatch({ "name": "Trigger1", "meta": None, "offsets": None, "strings": [], })) assert len(em.items) == 1 results = { "extracted": em.results(), "metadata": {}, "info": {}, } RunSignatures(results).run() assert results == { "info": { "score": 10.0, }, "metadata": { "cfgextr": [{ "family": "barfoo", "version": "baz", }], }, "extracted": mock.ANY, "signatures": [], }
def test_should_enable_signature_empty_platform(): rs = RunSignatures({}) class sig_empty_platform(object): platform = None assert rs.should_enable_signature(sig_empty_platform()) class sig_other_platform(object): platform = "nope" assert not rs.should_enable_signature(sig_other_platform()) class sig_windows_platform(object): platform = "windows" assert rs.should_enable_signature(sig_windows_platform())
def process(self, signatures=True, reporting=True, processing_modules=[]): """Process, run signatures and reports the results for this task""" results = RunProcessing(task=self.task_dict).run( processing_list=processing_modules) if signatures: RunSignatures(results=results).run() if reporting: RunReporting(task=self.task_dict, results=results).run() if config("cuckoo:cuckoo:delete_original"): for target in self.targets: target.delete_original() if config("cuckoo:cuckoo:delete_bin_copy"): for target in self.targets: target.delete_copy() return True
class test_call_signature(): class sig(Signature): enabled = True name = "sig" minimum = "2.0.0" maximum = None platform = "windows" matched = False order = 1 def __init__(self, caller): pass def on_signature(self, sig): pass set_cwd(tempfile.mkdtemp()) cuckoo_create() with mock.patch("cuckoo.core.plugins.cuckoo") as p: p.signatures = sig, RunSignatures.init_once() rs = RunSignatures({}) s1 = rs.signatures[0] # Not a match. f = mock.MagicMock(return_value=False) s1.matched = False rs.call_signature(s1, f, 1, 2, a=3, b=4) assert s1.matched is False f.assert_called_once_with(1, 2, a=3, b=4) # It is a match. f = mock.MagicMock(return_value=True) rs.call_signature(s1, f, "foo", "bar") assert s1.matched is True f.assert_called_once_with("foo", "bar") # Now it is a match, no longer call the handler. f = mock.MagicMock() rs.call_signature(s1, f, "foo", "bar") f.assert_not_called()
def process(target, copy_path, task): results = RunProcessing(task=task).run() RunSignatures(results=results).run() RunReporting(task=task, results=results).run() if config("cuckoo:cuckoo:delete_original"): try: if target and os.path.exists(target): os.remove(target) except OSError as e: log.error("Unable to delete original file at path \"%s\": %s", target, e) if config("cuckoo:cuckoo:delete_bin_copy"): try: if copy_path and os.path.exists(copy_path): os.remove(copy_path) except OSError as e: log.error( "Unable to delete the copy of the original file at " "path \"%s\": %s", copy_path, e)
def test_signature_version(): rs = RunSignatures({}) class sig_normal(object): name = "sig_normal" minimum = "2.0.0" maximum = None rs.version = "2.0.0" assert rs.check_signature_version(sig_normal) rs.version = "2.2.0" assert rs.check_signature_version(sig_normal) class sig_run(object): name = "sig_run" minimum = "2.0.0" maximum = None def run(self): pass assert not rs.check_signature_version(sig_run) class sig_outdated(object): name = "sig_outdated" minimum = "2.0.3" maximum = None rs.version = "2.0.0" assert not rs.check_signature_version(sig_outdated) class sig_obsolete(object): name = "sig_obsolete" minimum = "2.0.0" maximum = "2.0.9" rs.version = "2.1.0" assert not rs.check_signature_version(sig_obsolete)
def test_on_extract(): set_cwd(tempfile.mkdtemp()) cuckoo_create() init_modules() Database().connect() mkdir(cwd(analysis=2)) cmd = Scripting().parse_command("cmd.exe /c ping 1.2.3.4") ex = ExtractManager.for_task(2) ex.push_script({ "pid": 1, "first_seen": 2, }, cmd) results = RunProcessing(task=Dictionary({ "id": 2, "category": "file", "target": __file__, })).run() assert results["extracted"] == [{ "category": "script", "pid": 1, "first_seen": 2, "program": "cmd", "raw": cwd("extracted", "0.bat", analysis=2), "yara": [], "info": {}, }] class sig1(object): name = "sig1" @property def matched(self): return False @matched.setter def matched(self, value): pass def init(self): pass def on_signature(self): pass def on_complete(self): pass def on_yara(self): pass on_extract = mock.MagicMock() rs = RunSignatures(results) rs.signatures = sig1(), rs.run() sig1.on_extract.assert_called_once() em = sig1.on_extract.call_args_list[0][0][0] assert em.category == "script"
def test_on_yara(): set_cwd(os.path.realpath(tempfile.mkdtemp())) cuckoo_create() init_modules() shutil.copy(cwd("yara", "binaries", "vmdetect.yar"), cwd("yara", "memory", "vmdetect.yar")) init_yara() mkdir(cwd(analysis=1)) open(cwd("binary", analysis=1), "wb").write("\x0f\x3f\x07\x0b") mkdir(cwd("files", analysis=1)) open(cwd("files", "1.txt", analysis=1), "wb").write("\x56\x4d\x58\x68") mkdir(cwd("memory", analysis=1)) open(cwd("memory", "1-0.dmp", analysis=1), "wb").write( struct.pack("QIIII", 0x400000, 0x1000, 0, 0, 0) + "\x45\xc7\x00\x01") Database().connect() ExtractManager._instances = {} results = RunProcessing(task=Dictionary({ "id": 1, "category": "file", "target": __file__, })).run() assert results["target"]["file"]["yara"][0]["offsets"] == { "virtualpc": [(0, 0)], } assert results["procmemory"][0]["regions"] == [{ "addr": "0x00400000", "end": "0x00401000", "offset": 24, "protect": None, "size": 4096, "state": 0, "type": 0, }] assert results["procmemory"][0]["yara"][0]["offsets"] == { "vmcheckdll": [(24, 0)], } assert results["dropped"][0]["yara"][0]["offsets"] == { "vmware": [(0, 0)], "vmware1": [(0, 0)], } class sig1(Signature): name = "sig1" @property def matched(self): return False @matched.setter def matched(self, value): pass def init(self): pass def on_signature(self, sig): pass def on_complete(self): pass def on_extract(self, match): pass on_yara = mock.MagicMock() rs = RunSignatures(results) rs.signatures = sig1(rs), rs.run() assert sig1.on_yara.call_count == 3 sig1.on_yara.assert_any_call("sample", cwd("binary", analysis=1), mock.ANY) sig1.on_yara.assert_any_call("dropped", cwd("files", "1.txt", analysis=1), mock.ANY) sig1.on_yara.assert_any_call("procmem", cwd("memory", "1-0.dmp", analysis=1), mock.ANY) ym = sig1.on_yara.call_args_list[0][0][2] assert ym.offsets == { "virtualpc": [(0, 0)], } assert ym.string("virtualpc", 0) == "\x0f\x3f\x07\x0b"
def test_on_yara(): set_cwd(tempfile.mkdtemp()) cuckoo_create() init_modules() shutil.copy( cwd("yara", "binaries", "vmdetect.yar"), cwd("yara", "memory", "vmdetect.yar") ) init_yara(True) mkdir(cwd(analysis=1)) open(cwd("binary", analysis=1), "wb").write("\x0f\x3f\x07\x0b") mkdir(cwd("files", analysis=1)) open(cwd("files", "1.txt", analysis=1), "wb").write("\x56\x4d\x58\x68") mkdir(cwd("memory", analysis=1)) open(cwd("memory", "1-0.dmp", analysis=1), "wb").write( struct.pack("QIIII", 0x400000, 0x1000, 0, 0, 0) + "\x45\xc7\x00\x01" ) Database().connect() results = RunProcessing(task=Dictionary({ "id": 1, "category": "file", "target": __file__, })).run() assert results["target"]["file"]["yara"][0]["offsets"] == { "virtualpc": [(0, 0)], } assert results["procmemory"][0]["yara"][0]["offsets"] == { "vmcheckdll": [(24, 0)], } assert results["dropped"][0]["yara"][0]["offsets"] == { "vmware": [(0, 0)], "vmware1": [(0, 0)], } class sig1(object): name = "sig1" @property def matched(self): return False @matched.setter def matched(self, value): pass def init(self): pass def on_signature(self): pass def on_complete(self): pass on_yara = mock.MagicMock() rs = RunSignatures(results) rs.signatures = sig1(), rs.run() assert sig1.on_yara.call_count == 3 sig1.on_yara.assert_any_call( "sample", cwd("binary", analysis=1), mock.ANY ) sig1.on_yara.assert_any_call( "dropped", cwd("files", "1.txt", analysis=1), mock.ANY ) sig1.on_yara.assert_any_call( "procmem", cwd("memory", "1-0.dmp", analysis=1), mock.ANY ) assert sig1.on_yara.call_args_list[0][0][2]["offsets"] == { "virtualpc": [(0, 0)], }