Exemple #1
0
 def test_submit_abort(self, p, capsys):
     p.side_effect = KeyboardInterrupt
     main.main((
         "--cwd", cwd(), "submit", Files.create(cwd(), "a.txt", "hello")
     ), standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting submission of" in out
Exemple #2
0
 def test_empty_reprocess(self):
     db.connect()
     mkdir(cwd(analysis=1))
     process_task_range("1")
     assert os.path.exists(cwd("reports", "report.json", analysis=1))
     obj = json.load(open(cwd("reports", "report.json", analysis=1), "rb"))
     assert "contact back" in obj["debug"]["errors"][0]
Exemple #3
0
    def test_process_task_range_range(self, p):
        mkdir(cwd(analysis=3))
        for x in xrange(10, 101):
            mkdir(cwd(analysis=x))
        process_task_range("3,5,10-100")
        assert p.call_count == 92  # 101-10+1
        p.assert_any_call({
            "id": 3,
            "category": "file",
            "target": "",
            "options": {},
            "package": None,
            "custom": None,
        })

        # We did not create an analysis directory for analysis=5.
        with pytest.raises(AssertionError):
            p.assert_any_call({
                "id": 5,
                "category": "file",
                "target": "",
                "options": {},
                "package": None,
                "custom": None,
            })

        for x in xrange(10, 101):
            p.assert_any_call({
                "id": x,
                "category": "file",
                "target": "",
                "options": {},
                "package": None,
                "custom": None,
            })
Exemple #4
0
def init_binaries():
    """Inform the user about the need to periodically look for new analyzer
    binaries. These include the Windows monitor etc."""
    def throw():
        raise CuckooStartupError(
            "The binaries used for Windows analysis are updated regularly, "
            "independently from the release line. It appears that you're "
            "not up-to-date. This may happen when you've just installed the "
            "latest development version of Cuckoo or when you've updated "
            "to the latest Cuckoo. In order to get up-to-date, please run "
            "the following command: `cuckoo community`."
        )

    dirpath = cwd("monitor", "latest")

    # If "latest" is a symbolic link, check that it exists.
    if os.path.islink(dirpath):
        if not os.path.exists(dirpath):
            throw()
    # If "latest" is a file, check that it contains a legitimate hash.
    elif os.path.isfile(dirpath):
        monitor = os.path.basename(open(dirpath, "rb").read().strip())
        if not monitor or not os.path.isdir(cwd("monitor", monitor)):
            throw()
    else:
        throw()
Exemple #5
0
def sqldump(dburi, dirpath):
    args, env = dumpcmd(dburi, dirpath)

    envargs = " ".join("%s=%s" % (k, v) for k, v in env.items())
    cmdline = " ".join('"%s"' % arg if " " in arg else arg for arg in args)
    cmd = "%s %s" % (envargs, cmdline) if envargs else cmdline

    print "We can make a SQL database backup as follows:"
    print "input cmd  =>", cmd
    print "output SQL =>", cwd("backup.sql")

    if not click.confirm("Would you like to make a backup", default=True):
        return

    try:
        subprocess.check_call(
            args, stdout=open(cwd("backup.sql"), "wb"),
            env=dict(os.environ.items() + env.items())
        )
    except (subprocess.CalledProcessError, OSError) as e:
        raise CuckooOperationalError(
            "Error creating SQL database dump as the command returned an "
            "error code: %s. Please make sure that the required tooling "
            "for making a database backup is installed and review the "
            "database URI to make sure it's correct: %s!" % (e, dburi)
        )
Exemple #6
0
 def test_monitor_latest_symlink(self):
     set_cwd(tempfile.mktemp())
     cuckoo_create()
     monitor = open(cwd("monitor", "latest"), "rb").read().strip()
     os.unlink(cwd("monitor", "latest"))
     os.symlink(cwd("monitor", monitor), cwd("monitor", "latest"))
     migrate_cwd()
Exemple #7
0
    def apply_template(cls):
        # check elasticsearch version
        es_version = elastic.info().get("version", {}).get('number', 'Invalid ES info schema')
        # get the version 5 template
        if es_version.startswith('5') or es_version.startswith('6'):
            template_path = cwd("elasticsearch", "template-es5.json")
            if not os.path.exists(template_path):
                return False
        elif es_version.startswith('2'):
            template_path = cwd("elasticsearch", "template-es2.json")
            if not os.path.exists(template_path):
                return False
        else:
            raise CuckooReportError('Invalid Elasticsearch version %s unable to start the Elasticsearch'
                                    'reporting module' % es_version)

        try:
            template = json.loads(open(template_path, "rb").read())
        except ValueError:
            raise CuckooReportError(
                "Unable to read valid JSON from the ElasticSearch "
                "template JSON file located at: %s" % template_path
            )

        # Create an index wildcard based off of the index name specified
        # in the config file, this overwrites the settings in
        # template.json.
        template["template"] = elastic.index + "-*"

        # if the template does not already exist then create it
        if not elastic.client.indices.exists_template(cls.template_name):
            elastic.client.indices.put_template(
                name=cls.template_name, body=json.dumps(template)
            )
        return True
Exemple #8
0
def test_process_json_logging():
    set_cwd(tempfile.mkdtemp())
    cuckoo_create()
    init_yara()
    init_logfile("process-p0.json")

    def process_tasks(instance, maxcount):
        logger("foo bar", action="hello.world", status="success")

    with mock.patch("cuckoo.main.Database"):
        with mock.patch("cuckoo.main.process_tasks") as p1:
            with mock.patch("time.time") as p2:
                p1.side_effect = process_tasks
                p2.return_value = 1484232003
                main.main(
                    ("--cwd", cwd(), "process", "p0"), standalone_mode=False
                )

    assert json.load(open(cwd("log", "process-p0.json"), "rb")) == {
        "asctime": mock.ANY,
        "action": "hello.world",
        "level": "info",
        "message": "foo bar",
        "status": "success",
        "task_id": None,
        "time": 1484232003,
    }
Exemple #9
0
    def test_import_noconfirm(self, p):
        set_cwd(tempfile.mkdtemp())
        p.side_effect = True, False

        dirpath = init_legacy_analyses()
        os.makedirs(os.path.join(dirpath, "lib", "cuckoo", "common"))
        open(os.path.join(
            dirpath, "lib", "cuckoo", "common", "constants.py"
        ), "wb").write(constants_11_py)

        shutil.copytree(
            "tests/files/conf/110_plain", os.path.join(dirpath, "conf")
        )

        filepath = os.path.join(dirpath, "conf", "cuckoo.conf")
        buf = open(filepath, "rb").read()
        open(filepath, "wb").write(buf.replace(
            "connection =", "connection = %s" % self.URI
        ))

        main.main(
            ("--cwd", cwd(), "import", dirpath), standalone_mode=False
        )

        db = Database()
        db.connect()
        assert db.engine.name == self.ENGINE
        assert open(cwd("logs", "a.txt", analysis=1), "rb").read() == "a"
        assert config("cuckoo:database:connection") == self.URI
        assert db.count_tasks() == 2
Exemple #10
0
def test_migration_201_202():
    set_cwd(tempfile.mkdtemp())
    Folders.create(cwd(), "conf")
    Files.create(cwd("conf"), "virtualbox.conf", """
[virtualbox]
machines = cuckoo1, cuckoo2
[cuckoo1]
platform = windows
[cuckoo2]
platform = windows
""")
    # Except for virtualbox.
    machineries = (
        "avd", "esx", "kvm", "physical", "qemu",
        "vmware", "vsphere", "xenserver",
    )
    for machinery in machineries:
        Files.create(
            cwd("conf"), "%s.conf" % machinery,
            "[%s]\nmachines =" % machinery
        )
    cfg = Config.from_confdir(cwd("conf"), loose=True)
    cfg = migrate(cfg, "2.0.1", "2.0.2")
    assert cfg["virtualbox"]["cuckoo1"]["osprofile"] is None
    assert cfg["virtualbox"]["cuckoo2"]["osprofile"] is None
Exemple #11
0
def write_supervisor_conf(username):
    """Writes supervisord.conf configuration file if it does not exist yet."""
    # TODO Handle updates?
    if os.path.exists(cwd("supervisord.conf")):
        return

    if os.environ.get("VIRTUAL_ENV"):
        virtualenv = os.path.join(os.environ["VIRTUAL_ENV"], "bin")
        python_path = os.path.join(virtualenv, "python")
        cuckoo_path = os.path.join(virtualenv, "cuckoo")
    else:
        python_path = "python"
        cuckoo_path = "cuckoo"

    template = jinja2.Environment().from_string(
        open(cwd("cwd", "supervisord.jinja2", private=True), "rb").read()
    )

    with open(cwd("supervisord.conf"), "wb") as f:
        f.write(template.render({
            "cwd": cwd,
            "username": username,
            "cuckoo_path": cuckoo_path,
            "python_path": python_path,
        }).rstrip().encode("utf8") + "\n")
Exemple #12
0
def test_migration_203_204():
    set_cwd(tempfile.mkdtemp())
    Folders.create(cwd(), "conf")
    Files.create(cwd("conf"), "processing.conf", """
[dumptls]
enabled = on
""")
    Files.create(cwd("conf"), "qemu.conf", """
[qemu]
machines = ubuntu32, ubuntu64
[ubuntu32]
arch = x86
[ubuntu64]
arch = x64
    """)
    cfg = Config.from_confdir(cwd("conf"), loose=True)
    cfg = migrate(cfg, "2.0.3", "2.0.4")
    assert cfg["processing"]["extracted"]["enabled"] is True
    # Except for qemu.
    machineries = (
        "avd", "esx", "kvm", "physical", "virtualbox",
        "vmware", "vsphere", "xenserver",
    )
    for machinery in machineries:
        Files.create(
            cwd("conf"), "%s.conf" % machinery, "[%s]\nmachines =" % machinery
        )
    assert cfg["qemu"]["ubuntu32"]["enable_kvm"] is False
    assert cfg["qemu"]["ubuntu32"]["snapshot"] is None
Exemple #13
0
 def test_cuckoo_init_main(self):
     """Tests that 'cuckoo' works with a new CWD."""
     main.main(
         ("--cwd", cwd(), "--nolog"),
         standalone_mode=False
     )
     assert os.path.exists(os.path.join(cwd(), "mitm.py"))
Exemple #14
0
    def test_cuckoo_init_no_resultserver(self):
        """Tests that 'cuckoo init' doesn't launch the ResultServer."""
        with pytest.raises(SystemExit):
            main.main(
                ("--cwd", cwd(), "--nolog", "init"),
                standalone_mode=False
            )

        # We copy the monitor binary directory over from user-CWD (which is
        # also present in the Travis CI environment, etc) as otherwise the
        # following call will raise an exception about not having found the
        # monitoring binaries.
        shutil.rmtree(os.path.join(cwd(), "monitor"))
        shutil.copytree(
            os.path.expanduser("~/.cuckoo/monitor"),
            os.path.join(cwd(), "monitor")
        )

        # Raises CuckooCriticalError if ResultServer can't bind (which no
        # longer happens now, naturally).
        main.main(
            ("--cwd", cwd(), "--nolog", "init"),
            standalone_mode=False
        )

        assert ResultServer not in Singleton._instances
Exemple #15
0
    def test_path(self, p):
        set_cwd(tempfile.mkdtemp(prefix="."))
        cuckoo_create()
        mkdir(cwd(analysis=1))

        def create():
            mkdir(cwd("suricata", "files", analysis=1))

            f = open(cwd("suricata", "files", "file.1", analysis=1), "wb")
            f.write("a")
            f = open(cwd("suricata", "eve.json", analysis=1), "wb")
            f.write("")
            f = open(cwd("suricata", "files-json.log", analysis=1), "wb")
            f.write(json.dumps({
                "id": 1,
                "size": 1,
                "filename": "a.txt",
            }))

        open(cwd("dump.pcap", analysis=1), "wb").write("pcap")

        s = Suricata()
        s.set_path(cwd(analysis=1))
        s.set_options({})
        s.process_pcap_binary = create
        s.run()
Exemple #16
0
    def test_import_confirm(self, p):
        set_cwd(tempfile.mkdtemp())
        p.return_value = True

        dirpath = init_legacy_analyses()
        os.makedirs(os.path.join(dirpath, "lib", "cuckoo", "common"))
        open(os.path.join(
            dirpath, "lib", "cuckoo", "common", "constants.py"
        ), "wb").write(constants_11_py)

        shutil.copytree(
            "tests/files/conf/110_plain", os.path.join(dirpath, "conf")
        )

        filepath = os.path.join(dirpath, "conf", "cuckoo.conf")
        buf = open(filepath, "rb").read()
        open(filepath, "wb").write(buf.replace(
            "connection =", "connection = %s" % self.URI
        ))

        try:
            main.main(
                ("--cwd", cwd(), "import", dirpath), standalone_mode=False
            )
        except CuckooOperationalError as e:
            assert "SQL database dump as the command" in e.message
            assert not is_linux()
            return

        db = Database()
        db.connect()
        assert db.engine.name == self.ENGINE
        assert open(cwd("logs", "a.txt", analysis=1), "rb").read() == "a"
        assert config("cuckoo:database:connection") == self.URI
        assert db.count_tasks() == 2
Exemple #17
0
def test_basics():
    set_cwd(tempfile.mkdtemp())
    cuckoo_create()
    mkdir(cwd(analysis=1))
    init_yara()

    em = ExtractManager(1)
    em.write_extracted("foo", "bar")
    filepath = cwd("extracted", "0.foo", analysis=1)
    assert open(filepath, "rb").read() == "bar"

    scr = Scripting()
    cmd = scr.parse_command(
        "powershell -e %s" % "foobar".encode("utf-16le").encode("base64")
    )

    em.push_script({
        "pid": 1,
        "first_seen": 2,
    }, cmd)
    filepath = cwd("extracted", "0.ps1", analysis=1)
    assert open(filepath, "rb").read() == "foobar"

    em.push_command_line(
        "powershell -e %s" % "world!".encode("utf-16le").encode("base64")
    )
    filepath = cwd("extracted", "1.ps1", analysis=1)
    assert open(filepath, "rb").read() == "world!"
Exemple #18
0
 def test_empty_move(self):
     oldfilepath = Files.temp_put("hello")
     movesql("sqlite:///%s" % oldfilepath, "move", temppath())
     assert not os.path.exists(oldfilepath)
     assert os.path.exists(cwd("cuckoo.db"))
     assert not os.path.islink(cwd("cuckoo.db"))
     assert open(cwd("cuckoo.db"), "rb").read() == "hello"
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
Exemple #20
0
    def migrate(cls):
        tasks = cls.d.engine.execute(
            "SELECT status FROM tasks ORDER BY id"
        ).fetchall()
        assert tasks[0][0] == "success"
        assert tasks[1][0] == "processing"
        assert tasks[2][0] == "pending"

        main.main(
            ("--cwd", cwd(), "migrate", "--revision", "263a45963c72"),
            standalone_mode=False
        )

        tasks = cls.d.engine.execute(
            "SELECT status FROM tasks ORDER BY id"
        ).fetchall()
        assert tasks[0][0] == "completed"
        assert tasks[1][0] == "running"
        assert tasks[2][0] == "pending"

        main.main(
            ("--cwd", cwd(), "migrate"),
            standalone_mode=False
        )

        tasks = cls.d.engine.execute(
            "SELECT status, owner FROM tasks ORDER BY id"
        ).fetchall()
        assert tasks[0][0] == "completed"
        assert tasks[0][1] is None
        assert tasks[1][0] == "running"
        assert tasks[2][0] == "pending"
Exemple #21
0
def tasks_report(task_id, report_format="json"):
    formats = {
        "json": "report.json",
        "html": "report.html",
        "maec": "report.MAEC-5.0.json",
    }

    bz_formats = {
        "all": {"type": "-", "files": ["memory.dmp"]},
        "dropped": {"type": "+", "files": ["files"]},
        "package_files": {"type": "+", "files": ["package_files"]},
    }

    tar_formats = {
        "bz2": "w:bz2",
        "gz": "w:gz",
        "tar": "w",
    }

    if report_format.lower() in formats:
        report_path = cwd(
            "storage", "analyses", "%d" % task_id, "reports",
            formats[report_format.lower()]
        )
    elif report_format.lower() in bz_formats:
        bzf = bz_formats[report_format.lower()]
        srcdir = cwd("storage", "analyses", "%d" % task_id)
        s = io.BytesIO()

        # By default go for bz2 encoded tar files (for legacy reasons).
        tarmode = tar_formats.get(request.args.get("tar"), "w:bz2")

        tar = tarfile.open(fileobj=s, mode=tarmode, dereference=True)
        for filedir in os.listdir(srcdir):
            filepath = os.path.join(srcdir, filedir)
            if not os.path.exists(filepath):
                continue
            if bzf["type"] == "-" and filedir not in bzf["files"]:
                tar.add(filepath, arcname=filedir)
            if bzf["type"] == "+" and filedir in bzf["files"]:
                tar.add(filepath, arcname=filedir)
        tar.close()

        response = make_response(s.getvalue())
        response.headers["Content-Type"] = \
            "application/x-tar; charset=UTF-8"
        return response
    else:
        return json_error(400, "Invalid report format")

    if not os.path.exists(report_path):
        return json_error(404, "Report not found")

    if report_format == "json" or report_format == "maec":
        response = make_response(open(report_path, "rb").read())
        response.headers["Content-Type"] = "application/json"
        return response
    else:
        return open(report_path, "rb").read()
Exemple #22
0
def init_yara():
    """Initialize & load/compile Yara rules."""
    categories = (
        "binaries", "urls", "memory", "scripts", "shellcode", "dumpmem",
    )
    log.debug("Initializing Yara...")
    for category in categories:
        dirpath = cwd("yara", category)
        if not os.path.exists(dirpath):
            log.warning("Missing Yara directory: %s?", dirpath)

        rules, indexed = {}, []
        for dirpath, dirnames, filenames in os.walk(dirpath, followlinks=True):
            for filename in filenames:
                if not filename.endswith((".yar", ".yara")):
                    continue

                filepath = os.path.join(dirpath, filename)

                try:
                    # TODO Once Yara obtains proper Unicode filepath support we
                    # can remove this check. See also this Github issue:
                    # https://github.com/VirusTotal/yara-python/issues/48
                    assert len(str(filepath)) == len(filepath)
                except (UnicodeEncodeError, AssertionError):
                    log.warning(
                        "Can't load Yara rules at %r as Unicode filepaths are "
                        "currently not supported in combination with Yara!",
                        filepath
                    )
                    continue

                rules["rule_%s_%d" % (category, len(rules))] = filepath
                indexed.append(filename)

        try:
            File.yara_rules[category] = yara.compile(filepaths=rules)
        except yara.Error as e:
            raise CuckooStartupError(
                "There was a syntax error in one or more Yara rules: %s" % e
            )

        # The memory.py processing module requires a yara file with all of its
        # rules embedded in it, so create this file to remain compatible.
        if category == "memory":
            f = open(cwd("stuff", "index_memory.yar"), "wb")
            for filename in indexed:
                f.write('include "%s"\n' % cwd("yara", "memory", filename))

        indexed = sorted(indexed)
        for entry in indexed:
            if (category, entry) == indexed[-1]:
                log.debug("\t `-- %s %s", category, entry)
            else:
                log.debug("\t |-- %s %s", category, entry)

    # Store the compiled Yara rules for the "dumpmem" category in
    # $CWD/stuff/ so that we may pass it along to zer0m0n during analysis.
    File.yara_rules["dumpmem"].save(cwd("stuff", "dumpmem.yarac"))
Exemple #23
0
def fetch_community(branch="master", force=False, filepath=None):
    if filepath:
        buf = open(filepath, "rb").read()
    else:
        log.info("Downloading.. %s", URL % branch)
        r = requests.get(URL % branch)
        if r.status_code != 200:
            raise CuckooOperationalError(
                "Error fetching the Cuckoo Community binaries "
                "(status_code: %d)!" % r.status_code
            )

        buf = r.content

    t = tarfile.TarFile.open(fileobj=io.BytesIO(buf), mode="r:gz")

    folders = {
        "modules/signatures": "signatures",
        "data/monitor": "monitor",
        "data/yara": "yara",
        "agent": "agent",
        "analyzer": "analyzer",
    }

    members = t.getmembers()

    directory = members[0].name.split("/")[0]
    for tarfolder, outfolder in folders.items():
        mkdir(cwd(outfolder))

        # E.g., "community-master/modules/signatures".
        name_start = "%s/%s" % (directory, tarfolder)
        for member in members:
            if not member.name.startswith(name_start) or \
                    name_start == member.name:
                continue

            filepath = cwd(outfolder, member.name[len(name_start)+1:])
            if member.isdir():
                mkdir(filepath)
                continue

            # TODO Ask for confirmation as we used to do.
            if os.path.exists(filepath) and not force:
                log.debug(
                    "Not overwriting file which already exists: %s",
                    member.name[len(name_start)+1:]
                )
                continue

            if member.issym():
                t.makelink(member, filepath)
                continue

            if not os.path.exists(os.path.dirname(filepath)):
                os.makedirs(os.path.dirname(filepath))

            log.debug("Extracted %s..", member.name[len(name_start)+1:])
            open(filepath, "wb").write(t.extractfile(member).read())
Exemple #24
0
    def test_main(self, p, q):
        p.side_effect = SystemExit(0)

        # Ensure that the "latest" binary value makes sense so that the
        # "run community command" exception is not thrown.
        mkdir(cwd("monitor", open(cwd("monitor", "latest")).read().strip()))
        main.main(("--cwd", cwd(), "-d", "--nolog"), standalone_mode=False)
        q.assert_called_once()
Exemple #25
0
    def test_symlink(self):
        if not is_linux():
            return

        # Include all Yara rules from binaries/ into memory/ as well.
        os.symlink(cwd("yara", "binaries"), cwd("yara", "memory", "bins"))
        init_yara()
        assert len(list(File.yara_rules["memory"])) == 5
Exemple #26
0
 def test_cuckoo_init_main_nosigs(self, p):
     """Ensure load_signatures() isn't called for 'cuckoo' with new CWD."""
     main.main(
         ("--cwd", cwd(), "--nolog"),
         standalone_mode=False
     )
     assert os.path.exists(os.path.join(cwd(), "mitm.py"))
     p.assert_not_called()
Exemple #27
0
def test_remove_pidfile():
    set_cwd(tempfile.mkdtemp())
    cuckoo_create()

    Pidfile("test2").create()
    assert os.path.exists(cwd("pidfiles", "test2.pid"))

    Pidfile("test2").remove()
    assert not os.path.exists(cwd("pidfiles", "test2.pid"))
Exemple #28
0
def migrate_database(revision="head"):
    args = [
        "alembic", "-x", "cwd=%s" % cwd(), "upgrade", revision,
    ]
    try:
        subprocess.check_call(args, cwd=cwd("db_migration", private=True))
    except subprocess.CalledProcessError:
        return False
    return True
Exemple #29
0
def create_app():
    app = flask.Flask(
        "Distributed Cuckoo",
        template_folder=cwd("..", "distributed", "templates", private=True),
        static_folder=cwd("..", "distributed", "static", private=True),
    )

    init_settings()
    app.config.from_object(settings)

    for blueprint, routes in blueprints:
        for route in routes:
            app.register_blueprint(blueprint, url_prefix=route)

    db.init_app(app)
    db.create_all(app=app)

    # Check whether an alembic version is present and whether
    # we're up-to-date.
    with app.app_context():
        row = AlembicVersion.query.first()
        if not row:
            db.session.add(AlembicVersion(AlembicVersion.VERSION))
            db.session.commit()
        elif row.version_num != AlembicVersion.VERSION:
            sys.exit("Your database is not up-to-date, please upgrade it "
                     "(run `cuckoo distributed migrate`).")

    # Further check the configuration.
    if not settings.SQLALCHEMY_DATABASE_URI:
        sys.exit("Please configure a database connection.")

    if not settings.report_formats:
        sys.exit("Please configure one or more reporting formats.")

    if not settings.samples_directory or \
            not os.path.isdir(settings.samples_directory):
        sys.exit("Please configure a samples directory path.")

    if not settings.reports_directory or \
            not os.path.isdir(settings.reports_directory):
        sys.exit("Please configure a reports directory path.")

    @app.after_request
    def custom_headers(response):
        """Set some custom headers across all HTTP responses."""
        response.headers["Server"] = "Distributed Machete Server"
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["X-XSS-Protection"] = "1; mode=block"
        response.headers["Pragma"] = "no-cache"
        response.headers["Cache-Control"] = "no-cache"
        response.headers["Expires"] = "0"
        return response

    return app
Exemple #30
0
def analyzer_zipfile(platform, monitor):
    """Creates the Zip file that is sent to the Guest."""
    t = time.time()

    zip_data = io.BytesIO()
    zip_file = zipfile.ZipFile(zip_data, "w", zipfile.ZIP_STORED)

    # Select the proper analyzer's folder according to the operating
    # system associated with the current machine.
    root = cwd("analyzer", platform)
    root_len = len(os.path.abspath(root))

    if not os.path.exists(root):
        log.error("No valid analyzer found at path: %s", root)
        raise CuckooGuestError(
            "No valid analyzer found for %s platform!" % platform
        )

    # Walk through everything inside the analyzer's folder and write
    # them to the zip archive.
    for root, dirs, files in os.walk(root):
        archive_root = os.path.abspath(root)[root_len:]
        for name in files:
            path = os.path.join(root, name)
            archive_name = os.path.join(archive_root, name)
            zip_file.write(path, archive_name)

    # Include the chosen monitoring component and any additional files.
    if platform == "windows":
        dirpath = cwd("monitor", monitor)

        # Generally speaking we should no longer be getting symbolic links for
        # "latest" anymore, so in the case of a file; follow it.
        if os.path.isfile(dirpath):
            monitor = os.path.basename(open(dirpath, "rb").read().strip())
            dirpath = cwd("monitor", monitor)

        for name in os.listdir(dirpath):
            zip_file.write(
                os.path.join(dirpath, name), os.path.join("bin", name)
            )

        # Dump compiled "dumpmem" Yara rules for zer0m0n usage.
        zip_file.write(cwd("stuff", "dumpmem.yarac"), "bin/rules.yarac")

    zip_file.close()
    data = zip_data.getvalue()

    if time.time() - t > 10:
        log.warning(
            "It took more than 10 seconds to build the Analyzer Zip for the "
            "Guest. This might be a serious performance penalty. Is your "
            "analyzer/windows/ directory bloated with unnecessary files?"
        )

    return data
Exemple #31
0
 def test_dnsserve_abort(self, p, capsys):
     p.side_effect = KeyboardInterrupt
     main.main(("--cwd", cwd(), "dnsserve"), standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting Cuckoo DNS Serve" in out
Exemple #32
0
def cuckoo_clean():
    """Clean up cuckoo setup.
    It deletes logs, all stored data from file system and configured
    databases (SQL and MongoDB).
    """
    # Init logging (without writing to file).
    init_console_logging()

    try:
        # Initialize the database connection.
        db = Database()
        db.connect(schema_check=False)

        # Drop all tables.
        db.drop()
    except (CuckooDependencyError, CuckooDatabaseError) as e:
        # If something is screwed due to incorrect database migrations or bad
        # database SqlAlchemy would be unable to connect and operate.
        log.warning(
            "Error connecting to database: it is suggested to check "
            "the connectivity, apply all migrations if needed or purge "
            "it manually. Error description: %s", e)

    # Check if MongoDB reporting is enabled and drop the database if it is.
    if mongo.init():
        try:
            mongo.connect()
            mongo.drop()
            mongo.close()
        except Exception as e:
            log.warning("Unable to drop MongoDB database: %s", e)

    # Check if ElasticSearch reporting is enabled and drop its data if it is.
    if elastic.init():
        elastic.connect()

        # TODO This should be moved to the elastic abstract.
        # TODO We should also drop historic data, i.e., from pervious days,
        # months, and years.
        date_index = datetime.datetime.utcnow().strftime({
            "yearly": "%Y",
            "monthly": "%Y-%m",
            "daily": "%Y-%m-%d",
        }[elastic.index_time_pattern])
        dated_index = "%s-%s" % (elastic.index, date_index)

        elastic.client.indices.delete(index=dated_index, ignore=[400, 404])

        template_name = "%s_template" % dated_index
        if elastic.client.indices.exists_template(template_name):
            elastic.client.indices.delete_template(template_name)

    # Paths to clean.
    paths = [
        cwd("cuckoo.db"),
        cwd("log"),
        cwd("storage", "analyses"),
        cwd("storage", "baseline"),
        cwd("storage", "binaries"),
    ]

    # Delete the various files and directories. In case of directories, keep
    # the parent directories, so to keep the state of the CWD in tact.
    for path in paths:
        if os.path.isdir(path):
            try:
                shutil.rmtree(path)
                os.mkdir(path)
            except (IOError, OSError) as e:
                log.warning("Error removing directory %s: %s", path, e)
        elif os.path.isfile(path):
            try:
                os.unlink(path)
            except (IOError, OSError) as e:
                log.warning("Error removing file %s: %s", path, e)
Exemple #33
0
 def test_clean(self):
     with mock.patch("cuckoo.main.cuckoo_clean") as p:
         p.return_value = None
         main.main(("--cwd", cwd(), "clean"), standalone_mode=False)
         p.assert_called_once_with()
Exemple #34
0
 def test_process_once(self, p, q):
     main.main(("--cwd", cwd(), "process", "-r", "1234"),
               standalone_mode=False)
     p.assert_called_once_with("1234")
     q.assert_called_once()
Exemple #35
0
 def _build_whitelist(self):
     result = []
     whitelist_path = cwd("whitelist", "domain.txt", private=True)
     for line in open(whitelist_path, "rb"):
         result.append(line.strip())
     return result
Exemple #36
0
def test_clean_keepdirs(p):
    set_cwd(tempfile.mkdtemp())
    cuckoo_create()

    with open(cwd("log", "cuckoo.log"), "wb") as f:
        f.write("this is a log file")

    os.mkdir(cwd(analysis=1))
    with open(cwd("analysis.log", analysis=1), "wb") as f:
        f.write("this is also a log file")

    with open(cwd("storage", "binaries", "a"*40), "wb") as f:
        f.write("this is a binary file")

    assert os.path.isdir(cwd("log"))
    assert os.path.exists(cwd("log", "cuckoo.log"))
    assert os.path.exists(cwd("storage", "analyses"))
    assert os.path.exists(cwd("storage", "analyses", "1"))
    assert os.path.exists(cwd("storage", "analyses", "1", "analysis.log"))
    assert os.path.exists(cwd("storage", "baseline"))
    assert os.path.exists(cwd("storage", "binaries"))
    assert os.path.exists(cwd("storage", "binaries", "a"*40))

    cuckoo_clean()

    assert os.path.isdir(cwd("log"))
    assert not os.path.exists(cwd("log", "cuckoo.log"))
    assert os.path.exists(cwd("storage", "analyses"))
    assert not os.path.exists(cwd("storage", "analyses", "1"))
    assert not os.path.exists(cwd("storage", "analyses", "1", "analysis.log"))
    assert os.path.exists(cwd("storage", "baseline"))
    assert os.path.exists(cwd("storage", "binaries"))
    assert not os.path.exists(cwd("storage", "binaries", "a"*40))
Exemple #37
0
def init_yara():
    """Initialize & load/compile Yara rules."""
    categories = (
        "binaries",
        "urls",
        "memory",
        "scripts",
        "shellcode",
        "office",
    )
    log.debug("Initializing Yara...")
    for category in categories:
        dirpath = cwd("yara", category)
        if not os.path.exists(dirpath):
            log.warning("Missing Yara directory: %s?", dirpath)

        rules, indexed = {}, []
        for dirpath, dirnames, filenames in os.walk(dirpath, followlinks=True):
            for filename in filenames:
                if not filename.endswith((".yar", ".yara")):
                    continue

                filepath = os.path.join(dirpath, filename)

                try:
                    # TODO Once Yara obtains proper Unicode filepath support we
                    # can remove this check. See also this Github issue:
                    # https://github.com/VirusTotal/yara-python/issues/48
                    assert len(str(filepath)) == len(filepath)
                except (UnicodeEncodeError, AssertionError):
                    log.warning(
                        "Can't load Yara rules at %r as Unicode filepaths are "
                        "currently not supported in combination with Yara!",
                        filepath)
                    continue

                rules["rule_%s_%d" % (category, len(rules))] = filepath
                indexed.append(filename)

        # Need to define each external variable that will be used in the
        # future. Otherwise Yara will complain.
        externals = {
            "filename": "",
        }

        try:
            File.yara_rules[category] = yara.compile(filepaths=rules,
                                                     externals=externals)
        except yara.Error as e:
            raise CuckooStartupError(
                "There was a syntax error in one or more Yara rules: %s" % e)

        # The memory.py processing module requires a yara file with all of its
        # rules embedded in it, so create this file to remain compatible.
        if category == "memory":
            f = open(cwd("stuff", "index_memory.yar"), "wb")
            for filename in indexed:
                f.write('include "%s"\n' % cwd("yara", "memory", filename))

        indexed = sorted(indexed)
        for entry in indexed:
            if (category, entry) == indexed[-1]:
                log.debug("\t `-- %s %s", category, entry)
            else:
                log.debug("\t |-- %s %s", category, entry)

    # Store the compiled Yara rules for the "memory" category in $CWD/stuff/
    # so that we may easily pass it along to zer0m0n during an analysis.
    File.yara_rules["memory"].save(cwd("stuff", "dumpmem.yarac"))
Exemple #38
0
 def test_rooter_abort(self, p, capsys):
     p.side_effect = KeyboardInterrupt
     main.main(("--cwd", cwd(), "rooter"), standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting the Cuckoo Rooter" in out
Exemple #39
0
 def test_api(self):
     with mock.patch("cuckoo.main.cuckoo_api") as p:
         p.return_value = None
         main.main(("--cwd", cwd(), "api"), standalone_mode=False)
         p.assert_called_once_with("localhost", 8090, False)
Exemple #40
0
 def test_community_abort(self, p, capsys):
     p.side_effect = KeyboardInterrupt
     main.main(("--cwd", cwd(), "community"), standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting fetching of" in out
Exemple #41
0
 def test_cuckoo_conf(self):
     Folders.create(cwd(), "conf")
     write_cuckoo_conf()
Exemple #42
0
 def __init__(self, task, results):
     """@param analysis_path: analysis folder path."""
     self.task = task
     self.results = results
     self.analysis_path = cwd("storage", "analyses", "%s" % task["id"])
Exemple #43
0
 def test_dnsserve(self):
     with mock.patch("cuckoo.main.cuckoo_dnsserve") as p:
         p.return_value = None
         main.main(("--cwd", cwd(), "dnsserve"), standalone_mode=False)
         p.assert_called_once_with("0.0.0.0", 53, None, None)
Exemple #44
0
 def test_process_init_modules(self, p, q):
     main.main(
         ("--cwd", cwd(), "process", "-r", "1"),
         standalone_mode=False
     )
     p.assert_called_once()
Exemple #45
0
                workers[tn] = None

        time.sleep(5)


if os.environ.get("CUCKOO_APP") == "worker":
    decide_cwd(exists=True)

    if not HAVE_GEVENT:
        sys.exit("Please install Distributed Cuckoo dependencies (through "
                 "`pip install cuckoo[distributed]`)")

    formatter = logging.Formatter(
        "%(asctime)s [%(name)s] %(levelname)s: %(message)s")

    fh = logging.handlers.WatchedFileHandler(cwd("log", "distributed.log"))
    fh.setFormatter(formatter)
    logging.getLogger().addHandler(fh)

    # Create the Flask object and push its context so that we can reuse the
    # database connection throughout our script.
    app = create_app()

    workers = {
        ("dist.scheduler", True):
        gevent.spawn(with_app, "dist.scheduler", scheduler),
        ("dist.status", True):
        gevent.spawn(with_app, "dist.status", scheduler),
    }

    with_app("dist.spawner", spawner)
Exemple #46
0
 def test_process_task_range_multi_db(self, p):
     mkdir(cwd(analysis=1234))
     mkdir(cwd(analysis=2345))
     p.return_value.view_task.return_value = {}
     process_task_range("2345")
     p.return_value.view_task.assert_called_once_with(2345)
Exemple #47
0
 def test_process_many(self, p, q):
     main.main(("--cwd", cwd(), "process", "instance"),
               standalone_mode=False)
     p.assert_called_once_with("instance", 0)
     q.assert_called_once()
Exemple #48
0
    def import_(self, f, submit_id):
        """Import an analysis identified by the file(-like) object f."""
        try:
            z = zipfile.ZipFile(f)
        except zipfile.BadZipfile:
            raise CuckooOperationalError(
                "Imported analysis is not a proper .zip file.")

        # Ensure there are no files with illegal or potentially insecure names.
        # TODO Keep in mind that if we start to support other archive formats
        # (e.g., .tar) that those may also support symbolic links. In that case
        # we should probably start using sflock here.
        for filename in z.namelist():
            if filename.startswith("/") or ".." in filename or ":" in filename:
                raise CuckooOperationalError(
                    "The .zip file contains a file with a potentially "
                    "incorrect filename: %s" % filename)

        if "task.json" not in z.namelist():
            raise CuckooOperationalError(
                "The task.json file is required in order to be able to import "
                "an analysis! This file contains metadata about the analysis.")

        required_fields = {
            "options": dict,
            "route": basestring,
            "package": basestring,
            "target": basestring,
            "category": basestring,
            "memory": bool,
            "timeout": (int, long),
            "priority": (int, long),
            "custom": basestring,
            "tags": (tuple, list),
        }

        try:
            info = json.loads(z.read("task.json"))
            for key, type_ in required_fields.items():
                if key not in info:
                    raise ValueError("missing %s" % key)
                if info[key] is not None and not isinstance(info[key], type_):
                    raise ValueError("%s => %s" % (key, info[key]))
        except ValueError as e:
            raise CuckooOperationalError(
                "The provided task.json file, required for properly importing "
                "the analysis, is incorrect or incomplete (%s)." % e)

        if info["category"] == "url":
            task_id = db.add_url(url=info["target"],
                                 package=info["package"],
                                 timeout=info["timeout"],
                                 options=info["options"],
                                 priority=info["priority"],
                                 custom=info["custom"],
                                 memory=info["memory"],
                                 tags=info["tags"],
                                 submit_id=submit_id)
        else:
            # Users may have the "delete_bin_copy" enabled and in such cases
            # the binary file won't be included in the .zip file.
            if "binary" in z.namelist():
                filepath = Files.temp_named_put(
                    z.read("binary"), os.path.basename(info["target"]))
            else:
                filepath = __file__

            # We'll be updating the target shortly.
            task_id = db.add_path(file_path=filepath,
                                  package=info["package"],
                                  timeout=info["timeout"],
                                  options=info["options"],
                                  priority=info["priority"],
                                  custom=info["custom"],
                                  memory=info["memory"],
                                  tags=info["tags"],
                                  submit_id=submit_id)

        if not task_id:
            raise CuckooOperationalError(
                "There was an error creating a task for the to-be imported "
                "analysis in our database.. Can't proceed.")

        # The constructors currently don't accept this argument.
        db.set_route(task_id, info["route"])

        mkdir(cwd(analysis=task_id))
        z.extractall(cwd(analysis=task_id))

        # If there's an analysis.json file, load it up to figure out additional
        # metdata regarding this analysis.
        if os.path.exists(cwd("analysis.json", analysis=task_id)):
            try:
                obj = json.load(
                    open(cwd("analysis.json", analysis=task_id), "rb"))
                if not isinstance(obj, dict):
                    raise ValueError
                if "errors" in obj and not isinstance(obj["errors"], list):
                    raise ValueError
                if "action" in obj and not isinstance(obj["action"], list):
                    raise ValueError
            except ValueError:
                log.warning(
                    "An analysis.json file was provided, but wasn't a valid "
                    "JSON object/structure that we can to enhance the "
                    "analysis information.")
            else:
                for error in set(obj.get("errors", [])):
                    if isinstance(error, basestring):
                        db.add_error(error, task_id)
                for action in set(obj.get("action", [])):
                    if isinstance(action, basestring):
                        db.add_error("", task_id, action)

        # We set this analysis as completed so that it will be processed
        # automatically (assuming 'cuckoo process' is running).
        db.set_status(task_id, TASK_COMPLETED)
        return task_id
Exemple #49
0
def test_init(p):
    set_cwd(tempfile.mkdtemp())
    with pytest.raises(SystemExit):
        main.main(("--cwd", cwd(), "--nolog", "init"), standalone_mode=False)
    p.assert_not_called()
Exemple #50
0
    def start(self, label, task):
        """Start a virtual machine.
        @param label: virtual machine name.
        @param task: task object.
        """
        log.debug("Starting vm %s", label)

        vmname = self.db.view_machine_by_label(label).name
        vm_state_timeout = config("cuckoo:timeouts:vm_state")

        try:
            args = [
                "sudo", self.options.avd.emulator_path,
                "@%s" % label,
                "-no-snapshot-save",
                "-net-tap", "tap_%s" % vmname,
                "-net-tap-script-up", cwd("stuff", "setup-hostnet-avd.sh")
            ]

            # Aggregate machine-specific options.
            for machine in self.machines():
                if machine.label == label:
                    # In headless mode we remove the audio, and window support.
                    if "headless" in machine.options:
                        args += ["-no-audio", "-no-window"]

                    # Retrieve the snapshot name for this machine to load it.
                    args += ["-snapshot", machine.snapshot]

                    break

            # Create a socket server to acquire the console port of the emulator.
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setblocking(0)
            s.bind(("127.0.0.1", 0))
            s.listen(0)
            args += ["-report-console", "tcp:%s" % s.getsockname()[1]]

            # Start the emulator process..
            emu_conn = None
            proc = subprocess.Popen(
                args, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )

            rlist = [s]
            time_cnt = 0
            while True:
                if proc.poll() is not None:
                    out, err = proc.communicate()
                    # Grab emulator error message from stderr & stdout
                    exc_info = ""
                    for line in out.splitlines():
                        if "emulator: ERROR: " in line:
                            exc_info += "%s\n" % line
                    exc_info += err

                    raise OSError(exc_info.rstrip())

                rds, _, _ = select.select(rlist, [], [], 0)
                sock2read = rds.pop() if rds else None
                if sock2read == s:
                    emu_conn, _ = s.accept()
                    emu_conn.setblocking(0)
                    rlist[0] = emu_conn
                elif emu_conn and sock2read == emu_conn:
                    emu_port = emu_conn.recv(1024)
                    break

                if time_cnt < vm_state_timeout:
                    time.sleep(1)
                    time_cnt += 1
                else:
                    proc.terminate()
                    raise OSError("timed out")

            self._emulator_labels[label] = "emulator-%s" % emu_port
        except OSError as e:
            raise CuckooMachineError(
                "Emulator failed starting machine %s: %s" % (label, e)
            )
        except IOError as e:
            raise CuckooMachineError(e)
        finally:
            s.close()
            if emu_conn:
                emu_conn.close()

        self._wait_status_ready(label)
Exemple #51
0
 def test_process_abort(self, p, q, capsys):
     p.side_effect = KeyboardInterrupt
     main.main(("--cwd", cwd(), "process", "-r", "1234"),
               standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting (re-)processing of your analyses" in out
Exemple #52
0
def web(ctx, args, host, port, uwsgi, nginx):
    """Operate the Cuckoo Web Interface.

    Use "--help" to get this help message and "help" to find Django's
    manage.py potential subcommands.
    """
    username = ctx.parent.user or getuser()
    if uwsgi:
        print "[uwsgi]"
        print "plugins = python"
        if os.environ.get("VIRTUAL_ENV"):
            print "virtualenv =", os.environ["VIRTUAL_ENV"]
        print "module = cuckoo.web.web.wsgi"
        print "uid =", username
        print "gid =", username
        dirpath = os.path.join(cuckoo.__path__[0], "web", "static")
        print "static-map = /static=%s" % dirpath
        print "# If you're getting errors about the PYTHON_EGG_CACHE, then"
        print "# uncomment the following line and add some path that is"
        print "# writable from the defined user."
        print "# env = PYTHON_EGG_CACHE="
        print "env = CUCKOO_APP=web"
        print "env = CUCKOO_CWD=%s" % cwd()
        return

    if nginx:
        print "upstream _uwsgi_cuckoo_web {"
        print "    server unix:/run/uwsgi/app/cuckoo-web/socket;"
        print "}"
        print
        print "server {"
        print "    listen %s:%d;" % (host, port)
        print
        print "    # Cuckoo Web Interface"
        print "    location / {"
        print "        client_max_body_size 1G;"
        print "        proxy_redirect off;"
        print "        proxy_set_header X-Forwarded-Proto $scheme;"
        print "        uwsgi_pass  _uwsgi_cuckoo_web;"
        print "        include     uwsgi_params;"
        print "    }"
        print "}"
        return

    # Switch to cuckoo/web and add the current path to sys.path as the Web
    # Interface is using local imports here and there.
    # TODO Rename local imports to either cuckoo.web.* or relative imports.
    os.chdir(os.path.join(cuckoo.__path__[0], "web"))
    sys.path.insert(0, ".")

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cuckoo.web.web.settings")

    # The Django HTTP server also imports the WSGI module for some reason, so
    # ensure that WSGI is able to load.
    os.environ["CUCKOO_APP"] = "web"
    os.environ["CUCKOO_CWD"] = cwd()

    from django.core.management import execute_from_command_line

    init_console_logging(level=ctx.parent.level)
    Database().connect()

    if not args:
        execute_from_command_line(
            ("cuckoo", "runserver", "%s:%d" % (host, port)))
    else:
        execute_from_command_line(("cuckoo", ) + args)
Exemple #53
0
 def test_dist_instance(self):
     with mock.patch("cuckoo.main.cuckoo_distributed_instance") as p:
         p.return_value = None
         main.main(("--cwd", cwd(), "distributed", "instance", "name"),
                   standalone_mode=False)
         p.assert_called_once_with("name")
Exemple #54
0
    def __init__(self, file_name="cuckoo", cfg=None, strict=False,
                 loose=False, raw=False):
        """
        @param file_name: file name without extension.
        @param cfg: configuration file path.
        """
        env = {}
        for key, value in os.environ.items():
            if key.startswith("CUCKOO_"):
                env[key] = value

        env["CUCKOO_CWD"] = cwd()
        env["CUCKOO_APP"] = os.environ.get("CUCKOO_APP", "")
        config = ConfigParser.ConfigParser(env)

        self.env_keys = []
        for key in env.keys():
            self.env_keys.append(key.lower())

        self.sections = {}

        try:
            config.read(cfg or cwd("conf", "%s.conf" % file_name))
        except ConfigParser.ParsingError as e:
            raise CuckooConfigurationError(
                "There was an error reading in the $CWD/conf/%s.conf "
                "configuration file. Most likely there are leading "
                "whitespaces in front of one of the key=value lines defined. "
                "More information from the original exception: %s" %
                (file_name, e)
            )

        if file_name not in self.configuration and not loose:
            log.error("Unknown config file %s.conf", file_name)
            return

        for section in config.sections():
            types = self.get_section_types(file_name, section, strict, loose)
            if types is None:
                continue

            self.sections[section] = Dictionary()
            setattr(self, section, self.sections[section])

            try:
                items = config.items(section)
            except ConfigParser.InterpolationMissingOptionError as e:
                log.error("Missing environment variable(s): %s", e)
                raise CuckooConfigurationError(
                    "Missing environment variable: %s" % e
                )

            for name, raw_value in items:
                if name in self.env_keys:
                    continue

                if "\n" in raw_value:
                    wrong_key = "???"
                    try:
                        wrong_key = raw_value.split("\n", 1)[1].split()[0]
                    except:
                        pass

                    raise CuckooConfigurationError(
                        "There was an error reading in the $CWD/conf/%s.conf "
                        "configuration file. Namely, there are one or more "
                        "leading whitespaces before the definition of the "
                        "'%s' key/value pair in the '%s' section. Please "
                        "remove those leading whitespaces as Python's default "
                        "configuration parser is unable to handle those "
                        "properly." % (file_name, wrong_key, section)
                    )

                if not raw and name in types:
                    # TODO Is this the area where we should be checking the
                    # configuration values?
                    # if not types[name].check(raw_value):
                    #     print file_name, section, name, raw_value
                    #     raise

                    value = types[name].parse(raw_value)
                else:
                    if not loose:
                        log.error(
                            "Type of config parameter %s:%s:%s not found! "
                            "This may indicate that you've incorrectly filled "
                            "out the Cuckoo configuration, please double "
                            "check it.", file_name, section, name
                        )
                    value = raw_value

                self.sections[section][name] = value
Exemple #55
0
 def test_dist_server(self):
     with mock.patch("cuckoo.main.cuckoo_distributed") as p:
         p.return_value = None
         main.main(("--cwd", cwd(), "distributed", "server"),
                   standalone_mode=False)
         p.assert_called_once_with("localhost", 9003, False)
Exemple #56
0
 def test_clean_abort(self, p, capsys):
     p.side_effect = KeyboardInterrupt
     main.main(("--cwd", cwd(), "clean"), standalone_mode=False)
     out, _ = capsys.readouterr()
     assert "Aborting cleaning up of" in out
Exemple #57
0
 def test_process_task_range_single_db(self, p):
     mkdir(cwd(analysis=1234))
     p.return_value.view_task.return_value = {}
     process_task_range("1234")
     p.return_value.view_task.assert_called_once_with(1234)
Exemple #58
0
def migrate_cwd():
    log.warning(
        "This is the first time you're running Cuckoo after updating your "
        "local version of Cuckoo. We're going to update files in your CWD "
        "that require updating. Note that we'll first ensure that no custom "
        "patches have been applied by you before applying any modifications "
        "of our own.")

    # Remove now-obsolete index_*.yar files.
    for filename in os.listdir(cwd("yara")):
        if filename.startswith("index_") and filename.endswith(".yar"):
            os.remove(cwd("yara", filename))

    # Create the new $CWD/stuff/ directory.
    if not os.path.exists(cwd("stuff")):
        mkdir(cwd("stuff"))

    # Create the new $CWD/yara/dumpmem/ directory.
    if not os.path.exists(cwd("yara", "dumpmem")):
        mkdir(cwd("yara", "dumpmem"))

    hashes = {}
    for line in open(cwd("cwd", "hashes.txt", private=True), "rb"):
        if not line.strip() or line.startswith("#"):
            continue
        hash_, filename = line.split()
        hashes[filename] = hashes.get(filename, []) + [hash_]

    modified, outdated = [], []
    for filename, hashes in hashes.items():
        if not os.path.exists(cwd(filename)):
            outdated.append(filename)
            continue
        hash_ = hashlib.sha1(open(cwd(filename), "rb").read()).hexdigest()
        if hash_ not in hashes:
            modified.append(filename)
        if hash_ != hashes[-1]:
            outdated.append(filename)

    if modified:
        log.error(
            "One or more files in the CWD have been modified outside of "
            "regular Cuckoo usage. Due to these changes Cuckoo isn't able to "
            "automatically upgrade your setup.")

        for filename in sorted(modified):
            log.warning("Modified file: %s (=> %s)", filename, cwd(filename))

        log.error("Moving forward you have two options:")
        log.warning(
            "1) You make a backup of the affected files, remove their "
            "presence in the CWD (yes, actually 'rm -f' the file), and "
            "re-run Cuckoo to automatically restore the new version of the "
            "file. Afterwards you'll be able to re-apply any changes as you "
            "like.")
        log.warning(
            "2) You revert back to the version of Cuckoo you were on "
            "previously and accept that manual changes that have not been "
            "merged upstream require additional maintenance that you'll "
            "pick up at a later point in time.")

        sys.exit(1)

    for filename in outdated:
        log.debug("Upgraded %s", filename)
        if not os.path.exists(os.path.dirname(cwd(filename))):
            os.makedirs(os.path.dirname(cwd(filename)))
        shutil.copy(cwd("..", "data", filename, private=True), cwd(filename))

    log.info("Automated migration of your CWD was successful! Continuing "
             "execution of Cuckoo as expected.")
Exemple #59
0
 def __init__(self, task):
     """@param task: task dictionary of the analysis to process."""
     self.task = task
     self.machine = {}
     self.analysis_path = cwd(analysis=task["id"])
     self.baseline_path = cwd("storage", "baseline")
Exemple #60
0
 def test_submit(self):
     with mock.patch("cuckoo.main.submit_tasks") as p:
         p.return_value = []
         main.main((
             "--cwd", cwd(), "submit", Files.create(cwd(), "a.txt", "hello")
         ), standalone_mode=False)