示例#1
0
    def analyze_sample(self, sample_path, workdir, outdir, start_command,
                       timeout):
        analysis_info = dict()

        dns_server = self.config.config['drakrun'].get('dns_server', '8.8.8.8')
        drakmon_log_fp = os.path.join(outdir, "drakmon.log")

        with self.run_vm() as vm, \
             graceful_exit(start_dnsmasq(self.instance_id, dns_server)), \
             graceful_exit(start_tcpdump_collector(self.instance_id, outdir)), \
             open(drakmon_log_fp, "wb") as drakmon_log:

            analysis_info[
                'snapshot_version'] = vm.backend.get_vm0_snapshot_time()

            kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")

            self.log.info("Copying sample to VM...")
            injector = Injector(self.vm_name, self.runtime_info,
                                kernel_profile)
            result = injector.write_file(
                sample_path,
                f"%USERPROFILE%\\Desktop\\{os.path.basename(sample_path)}")
            injected_fn = json.loads(result.stdout)['ProcessName']

            if "%f" not in start_command:
                self.log.warning("No file name in start command")
            cur_start_command = start_command.replace("%f", injected_fn)

            # don't include our internal maintanance commands
            analysis_info['start_command'] = cur_start_command
            self.log.info("Using command: %s", cur_start_command)

            if self.net_enable:
                self.log.info("Setting up network...")
                injector.create_process("cmd /C ipconfig /renew >nul",
                                        wait=True,
                                        timeout=120)

            drakvuf_cmd = self.build_drakvuf_cmdline(
                timeout=timeout,
                cwd=subprocess.list2cmdline([ntpath.dirname(injected_fn)]),
                full_cmd=cur_start_command,
                dump_dir=os.path.join(outdir, "dumps"),
                ipt_dir=os.path.join(outdir, "ipt"),
                workdir=workdir,
            )

            try:
                subprocess.run(drakvuf_cmd,
                               stdout=drakmon_log,
                               check=True,
                               timeout=timeout + 60)
            except subprocess.TimeoutExpired:
                self.log.exception("DRAKVUF timeout expired")

        return analysis_info
def dnsmasq_start_test():
    if not tool_exists("dnsmasq"):
        pytest.skip("dnsmasq does not exist")

    # stale dnsmasq will create issues with the stopping test
    dnsmasq_pids = Path("/var/run/").glob("dnsmasq-vm*.pid")
    for pid in dnsmasq_pids:
        subprocess.run(["pkill", "-F", str(pid)])

    start_dnsmasq(10, "8.8.8.8", True)

    # wait up to 10 seconds for the pidfile to appear
    for _ in range(10):
        cmd = subprocess.run(["pgrep", "-F", "/var/run/dnsmasq-vm10.pid"])
        # we can break, pidfile appeared
        if cmd.returncode == 0:
            break
        time.sleep(1.0)

    assert cmd.returncode == 0

    # starting already stopped dnsmasq
    # what should be the expected behavior?
    start_dnsmasq(10, "8.8.8.8", True)
示例#3
0
def main():
    parser = argparse.ArgumentParser(
        description='DRAKVUF Sandbox interactive shell')
    parser.add_argument('vm_id', type=int, help='VM id you want to control')
    parser.add_argument('--dns', default='8.8.8.8')

    args = parser.parse_args()

    with graceful_exit(start_dnsmasq(args.vm_id, args.dns)), \
         DrakmonShell(args.vm_id, args.dns) as shell:
        helpers = {
            'copy': shell.copy,
            'drakvuf': shell.drakvuf,
            'vm': shell.vm
        }
        embed(banner='', user_ns=helpers, colors='neutral')
示例#4
0
def main():
    parser = argparse.ArgumentParser(
        description='DRAKVUF Sandbox interactive shell')
    parser.add_argument('vm_id', type=int, help='VM id you want to control')
    parser.add_argument('--dns', default='8.8.8.8')

    args = parser.parse_args()

    logging.basicConfig(level=logging.DEBUG,
                        format='[%(asctime)s][%(levelname)s] %(message)s',
                        handlers=[logging.StreamHandler()])

    with graceful_exit(start_dnsmasq(args.vm_id, args.dns)), \
            DrakmonShell(args.vm_id, args.dns) as shell:
        helpers = {
            'copy': shell.copy,
            'drakvuf': shell.drakvuf,
            'vm': shell.vm
        }
        embed(banner='', user_ns=helpers, colors='neutral')
示例#5
0
def test_dnsmasq_start():
    start_dnsmasq(1, '8.8.8.8', True)
    try:
        subprocess.check_output('pgrep dnsmasq', shell=True)
    except subprocess.CalledProcessError as e:
        assert e.returncode == 0
示例#6
0
    def process(self):
        sample = self.current_task.get_resource("sample")
        self.log.info("hostname: {}".format(socket.gethostname()))
        sha256sum = hashlib.sha256(sample.content).hexdigest()
        magic_output = magic.from_buffer(sample.content)
        self.log.info("running sample sha256: {}".format(sha256sum))

        timeout = self.current_task.payload.get(
            'timeout') or self.default_timeout
        hard_time_limit = 60 * 20
        if timeout > hard_time_limit:
            self.log.error(
                "Tried to run the analysis for more than hard limit of %d seconds",
                hard_time_limit)
            return

        self.log.info(f"analysis UID: {self.analysis_uid}")
        self.rs.set(f"drakvnc:{self.analysis_uid}", self.instance_id,
                    ex=3600)  # 1h

        workdir = f"/tmp/drakrun/{self.vm_name}"

        extension = self.current_task.headers.get("extension", "exe").lower()
        if '(DLL)' in magic_output:
            extension = 'dll'
        self.log.info("Running file as %s", extension)

        file_name = self.current_task.payload.get("file_name",
                                                  "malwar") + f".{extension}"
        # Alphanumeric, dot, underscore, dash
        if not re.match(r"^[a-zA-Z0-9\._\-]+$", file_name):
            self.log.error("Filename contains invalid characters")
            return
        self.log.info("Using file name %s", file_name)

        # Save sample to disk here as some branches of _get_start_command require file path.
        try:
            shutil.rmtree(workdir)
        except Exception as e:
            print(e)
        os.makedirs(workdir, exist_ok=True)
        with open(os.path.join(workdir, file_name), 'wb') as f:
            f.write(sample.content)

        start_command = self.current_task.payload.get(
            "start_command",
            self._get_start_command(extension, sample,
                                    os.path.join(workdir, file_name)))
        if not start_command:
            self.log.error(
                "Unable to run malware sample, could not generate any suitable command to run it."
            )
            return

        outdir = os.path.join(workdir, 'output')
        os.mkdir(outdir)
        os.mkdir(os.path.join(outdir, 'dumps'))
        os.mkdir(os.path.join(outdir, 'ipt'))

        metadata = {
            "sample_sha256": sha256sum,
            "magic_output": magic_output,
            "time_started": int(time.time())
        }

        with open(os.path.join(outdir, 'sample_sha256.txt'), 'w') as f:
            f.write(hashlib.sha256(sample.content).hexdigest())

        watcher_tcpdump = None
        watcher_dnsmasq = None

        for _ in range(3):
            try:
                self.log.info("Running VM {}".format(self.instance_id))
                watcher_dnsmasq = start_dnsmasq(
                    self.instance_id,
                    self.config.config['drakrun'].get('dns_server', '8.8.8.8'))

                snapshot_version = self.run_vm()
                metadata['snapshot_version'] = snapshot_version

                watcher_tcpdump = start_tcpdump_collector(
                    self.instance_id, outdir)

                self.log.info("running monitor {}".format(self.instance_id))

                hooks_list = os.path.join(ETC_DIR, "hooks.txt")
                kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")
                dump_dir = os.path.join(outdir, "dumps")
                ipt_dir = os.path.join(outdir, "ipt")
                drakmon_log_fp = os.path.join(outdir, "drakmon.log")

                self.log.info("Copying sample to VM...")
                injector = Injector(self.vm_name, self.runtime_info,
                                    kernel_profile)
                result = injector.write_file(
                    os.path.join(workdir, file_name),
                    f"%USERPROFILE%\\Desktop\\{file_name}")

                injected_fn = json.loads(result.stdout)['ProcessName']
                net_enable = int(self.config.config['drakrun'].get(
                    'net_enable', '0'))

                if "%f" not in start_command:
                    self.log.warning("No file name in start command")

                cwd = subprocess.list2cmdline([ntpath.dirname(injected_fn)])
                cur_start_command = start_command.replace("%f", injected_fn)

                # don't include our internal maintanance commands
                metadata['start_command'] = cur_start_command

                if net_enable:
                    self.log.info("Setting up network...")
                    injector.create_process("cmd /C ipconfig /renew >nul",
                                            wait=True,
                                            timeout=120)

                full_cmd = cur_start_command
                self.log.info("Using command: %s", full_cmd)

                task_quality = self.current_task.headers.get("quality", "high")

                drakvuf_cmd = ["drakvuf"] + self.generate_plugin_cmdline(task_quality) + \
                              ["-o", "json"
                               "-j", "5",
                               "-t", str(timeout),
                               "-i", str(self.runtime_info.inject_pid),
                               "-k", hex(self.runtime_info.vmi_offsets.kpgd),
                               "-d", self.vm_name,
                               "--dll-hooks-list", hooks_list,
                               "--memdump-dir", dump_dir,
                               "-r", kernel_profile,
                               "-e", full_cmd,
                               "-c", cwd]

                if self.config.config['drakrun'].getboolean('enable_ipt',
                                                            fallback=False):
                    drakvuf_cmd.extend(["--ipt-dir", ipt_dir])

                drakvuf_cmd.extend(self.get_profile_list())

                syscall_filter = self.config.config['drakrun'].get(
                    'syscall_filter', None)
                if syscall_filter:
                    drakvuf_cmd.extend(["-S", syscall_filter])

                with open(drakmon_log_fp, "wb") as drakmon_log:
                    drakvuf = subprocess.Popen(drakvuf_cmd, stdout=drakmon_log)

                    try:
                        exit_code = drakvuf.wait(timeout + 60)
                    except subprocess.TimeoutExpired as e:
                        logging.error(
                            "BUG: Monitor command doesn\'t terminate automatically after timeout expires."
                        )
                        logging.error("Trying to terminate DRAKVUF...")
                        drakvuf.terminate()
                        drakvuf.wait(10)
                        logging.error(
                            "BUG: Monitor command also doesn\'t terminate after sending SIGTERM."
                        )
                        drakvuf.kill()
                        drakvuf.wait()
                        logging.error("Monitor command was forcefully killed.")
                        raise e

                    if exit_code != 0:
                        raise subprocess.CalledProcessError(
                            exit_code, drakvuf_cmd)
                break
            except subprocess.CalledProcessError:
                self.log.info("Something went wrong with the VM {}".format(
                    self.instance_id),
                              exc_info=True)
            finally:
                try:
                    subprocess.run(["xl", "destroy", self.vm_name],
                                   cwd=workdir,
                                   check=True)
                except subprocess.CalledProcessError:
                    self.log.info("Failed to destroy VM {}".format(
                        self.instance_id),
                                  exc_info=True)

                if watcher_dnsmasq:
                    watcher_dnsmasq.terminate()
        else:
            self.log.info(
                "Failed to analyze sample after 3 retries, giving up.")
            return

        self.log.info("waiting for tcpdump to exit")

        if watcher_tcpdump:
            try:
                watcher_tcpdump.wait(timeout=60)
            except subprocess.TimeoutExpired:
                self.log.exception("tcpdump doesn't exit cleanly after 60s")

        self.crop_dumps(os.path.join(outdir, 'dumps'),
                        os.path.join(outdir, 'dumps.zip'))

        if self.config.config['drakrun'].getboolean('enable_ipt',
                                                    fallback=False):
            self.compress_ipt(os.path.join(outdir, 'ipt'),
                              os.path.join(outdir, 'ipt.zip'))

        self.log.info("uploading artifacts")

        metadata['time_finished'] = int(time.time())

        with open(os.path.join(outdir, 'metadata.json'), 'w') as f:
            f.write(json.dumps(metadata))

        payload = {"analysis_uid": self.analysis_uid}
        payload.update(metadata)

        headers = dict(self.headers)
        headers["quality"] = self.current_task.headers.get("quality", "high")

        t = Task(headers, payload=payload)

        for resource in self.upload_artifacts(self.analysis_uid, workdir):
            t.add_payload(resource.name, resource)

        t.add_payload('sample', sample)
        self.send_task(t)
示例#7
0
    def analyze_sample(self, sample_path, workdir, outdir, start_command,
                       timeout):
        analysis_info = dict()

        dns_server = self.config.config["drakrun"].get("dns_server", "8.8.8.8")
        drakmon_log_fp = os.path.join(outdir, "drakmon.log")

        with self.run_vm() as vm, graceful_exit(
                start_dnsmasq(self.instance_id, dns_server)), graceful_exit(
                    start_tcpdump_collector(self.instance_id, outdir)), open(
                        drakmon_log_fp, "wb") as drakmon_log:

            analysis_info[
                "snapshot_version"] = vm.backend.get_vm0_snapshot_time()

            kernel_profile = os.path.join(PROFILE_DIR, "kernel.json")

            self.log.info("Copying sample to VM...")
            injector = Injector(self.vm_name, self.runtime_info,
                                kernel_profile)
            result = injector.write_file(
                sample_path,
                f"%USERPROFILE%\\Desktop\\{os.path.basename(sample_path)}")

            try:
                injected_fn = json.loads(result.stdout)["ProcessName"]
            except ValueError as e:
                self.log.error(
                    "JSON decode error occurred when tried to parse injector's logs."
                )
                self.log.error(f"Raw log line: {result.stdout}")
                raise e

            # don't include our internal maintanance commands
            start_command = start_command.replace("%f", injected_fn)
            analysis_info["start_command"] = start_command
            self.log.info("Using command: %s", start_command)

            if self.net_enable:
                self.log.info("Setting up network...")
                injector.create_process("cmd /C ipconfig /renew >nul",
                                        wait=True,
                                        timeout=120)

            task_quality = self.current_task.headers.get("quality", "high")
            requested_plugins = self.current_task.payload.get(
                "plugins", self.active_plugins["_all_"])
            analysis_info["plugins"] = self.get_plugin_list(
                task_quality, requested_plugins)

            drakvuf_cmd = self.build_drakvuf_cmdline(
                timeout=timeout,
                cwd=subprocess.list2cmdline([ntpath.dirname(injected_fn)]),
                full_cmd=start_command,
                dump_dir=os.path.join(outdir, "dumps"),
                ipt_dir=os.path.join(outdir, "ipt"),
                workdir=workdir,
                enabled_plugins=analysis_info["plugins"],
            )

            try:
                subprocess.run(drakvuf_cmd,
                               stdout=drakmon_log,
                               check=True,
                               timeout=timeout + 60)
            except subprocess.CalledProcessError as e:
                # see DRAKVUF src/exitcodes.h for more details
                INJECTION_UNSUCCESSFUL = 4

                if e.returncode == INJECTION_UNSUCCESSFUL:
                    self.log_startup_failure(drakmon_log_fp)
                raise e
            except subprocess.TimeoutExpired as e:
                self.log.exception("DRAKVUF timeout expired")
                raise e

        return analysis_info
示例#8
0
def install(vcpus, memory, storage_backend, disk_size, iso_path, zfs_tank_name,
            lvm_volume_group, unattended_xml):
    if not check_root():
        return

    if storage_backend == "lvm" and lvm_volume_group is None:
        raise Exception("lvm storage backend requires --lvm-volume-group")
    if storage_backend == "zfs" and zfs_tank_name is None:
        raise Exception("zfs storage backend requires --zfs-tank-name")

    if not sanity_check():
        logging.error("Sanity check failed.")
        return

    stop_all_drakruns()

    logging.info("Performing installation...")

    if vcpus < 1:
        logging.error("Your VM must have at least 1 vCPU.")
        return

    if memory < 512:
        logging.error("Your VM must have at least 512 MB RAM.")
        return

    if memory < 1536:
        logging.warning(
            "Using less than 1.5 GB RAM per VM is not recommended for any supported system."
        )

    if unattended_xml:
        logging.info("Baking unattended.iso for automated installation")
        with tempfile.TemporaryDirectory() as tmpdir:
            tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml')

            with open(tmp_xml_path, 'wb') as fw:
                with open(unattended_xml, 'rb') as fr:
                    fw.write(fr.read())

            try:
                subprocess.check_output([
                    'genisoimage', '-o',
                    os.path.join(VOLUME_DIR, "unattended.iso"), '-J', '-r',
                    tmp_xml_path
                ],
                                        stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.exception("Failed to generate unattended.iso.")

    sha256_hash = hashlib.sha256()

    logging.info("Calculating hash of iso")
    iso_file_size = os.stat(iso_path).st_size
    block_size = 128 * 1024
    with tqdm(total=iso_file_size, unit_scale=True) as pbar:
        with open(iso_path, "rb") as f:
            for byte_block in iter(lambda: f.read(block_size), b""):
                pbar.update(block_size)
                sha256_hash.update(byte_block)

            iso_sha256 = sha256_hash.hexdigest()

    install_info = InstallInfo(vcpus=vcpus,
                               memory=memory,
                               storage_backend=storage_backend,
                               disk_size=disk_size,
                               iso_path=os.path.abspath(iso_path),
                               zfs_tank_name=zfs_tank_name,
                               lvm_volume_group=lvm_volume_group,
                               enable_unattended=unattended_xml is not None,
                               iso_sha256=iso_sha256)
    install_info.save()

    try:
        subprocess.check_output('xl uptime vm-0',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass
    else:
        logging.info('Detected that vm-0 is already running, stopping it.')
        subprocess.run('xl destroy vm-0', shell=True, check=True)

    generate_vm_conf(install_info, 0)

    backend = get_storage_backend(install_info)
    backend.initialize_vm0_volume(disk_size)

    try:
        subprocess.check_output("brctl show", shell=True)
    except subprocess.CalledProcessError:
        logging.exception(
            "Failed to execute brctl show. Make sure you have bridge-utils installed."
        )
        return

    net_enable = conf['drakrun'].getboolean('net_enable', fallback=False)
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')

    setup_vm_network(vm_id=0,
                     net_enable=net_enable,
                     out_interface=out_interface,
                     dns_server=dns_server)

    if net_enable:
        start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

    cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

    try:
        subprocess.run('xl create {}'.format(shlex.quote(cfg_path)),
                       shell=True,
                       check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to launch VM vm-0")
        return

    logging.info("-" * 80)
    logging.info("Initial VM setup is complete and the vm-0 was launched.")
    logging.info(
        "Please now VNC to the port 5900 on this machine to perform Windows installation."
    )
    logging.info(
        "After you have installed Windows and booted it to the desktop, please execute:"
    )
    logging.info("# draksetup postinstall")

    with open(cfg_path, "r") as f:
        data = f.read()
        m = re.search(r'vncpasswd[ ]*=(.*)', data)
        if m:
            passwd = m.group(1).strip()
            if passwd[0] == '"' and passwd[-1] == '"':
                passwd = passwd[1:-1]

            logging.info("Your configured VNC password is:")
            logging.info(passwd)

    logging.info(
        "Please note that on some machines, system installer may boot for up to 10 minutes"
    )
    logging.info(
        "and may look unresponsive during the process. Please be patient.")
    logging.info("-" * 80)
示例#9
0
def snapshot_import(name, bucket, full, zpool):
    local_install = InstallInfo.try_load()
    if local_install is not None:
        click.confirm("Detected local snapshot. It will be REMOVED. Continue?",
                      abort=True)

    mc = get_minio_client(conf)

    if not mc.bucket_exists(bucket):
        logging.error("Bucket %s doesn't exist", bucket)
        return

    ensure_dirs()

    try:
        if full:
            logging.warning(
                "Importing full snapshot. This may not work if hardware is different"
            )
            do_import_full(mc, name, bucket, zpool)
        else:
            do_import_minimal(mc, name, bucket, zpool)

            # This could probably use some refactoring
            # We're duplicating quite a lot of code from install function
            install_info = InstallInfo.load()
            generate_vm_conf(install_info, 0)
            backend = get_storage_backend(install_info)
            backend.rollback_vm_storage(0)

            net_enable = int(conf["drakrun"].get("net_enable", "0"))
            out_interface = conf["drakrun"].get("out_interface", "")
            dns_server = conf["drakrun"].get("dns_server", "")
            setup_vm_network(
                vm_id=0,
                net_enable=net_enable,
                out_interface=out_interface,
                dns_server=dns_server,
            )

            if net_enable:
                start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

            cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

            try:
                subprocess.run(["xl" "create", cfg_path], check=True)
            except subprocess.CalledProcessError:
                logging.exception("Failed to launch VM vm-0")
                return

            logging.info(
                "Minimal snapshots require postinstall to work correctly")
            logging.info(
                "Please VNC to the port 5900 to ensure the OS booted correctly"
            )
            logging.info(
                "After that, execute this command to finish the setup")
            logging.info("# draksetup postinstall")
    except NoSuchKey:
        logging.error("Import failed. Missing files in bucket.")
示例#10
0
def install(storage_backend, disk_size, iso_path, zfs_tank_name,
            unattended_xml):
    logging.info("Ensuring that drakrun@* services are stopped...")
    subprocess.check_output('systemctl stop \'drakrun@*\'',
                            shell=True,
                            stderr=subprocess.STDOUT)

    logging.info("Performing installation...")

    if unattended_xml:
        logging.info("Baking unattended.iso for automated installation")
        with tempfile.TemporaryDirectory() as tmpdir:
            tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml')

            with open(tmp_xml_path, 'wb') as fw:
                with open(unattended_xml, 'rb') as fr:
                    fw.write(fr.read())

            try:
                subprocess.check_output([
                    'genisoimage', '-o',
                    os.path.join(VOLUME_DIR, "unattended.iso"), '-J', '-r',
                    tmp_xml_path
                ],
                                        stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError:
                logging.exception("Failed to generate unattended.iso.")

    sha256_hash = hashlib.sha256()

    with open(iso_path, "rb") as f:
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)

        iso_sha256 = sha256_hash.hexdigest()

    install_info = InstallInfo(storage_backend=storage_backend,
                               disk_size=disk_size,
                               iso_path=os.path.abspath(iso_path),
                               zfs_tank_name=zfs_tank_name,
                               enable_unattended=unattended_xml is not None,
                               iso_sha256=iso_sha256)
    install_info.save()

    logging.info("Checking xen-detect...")
    proc = subprocess.run('xen-detect -N', shell=True)

    if proc.returncode != 1:
        logging.error(
            'It looks like the system is not running on Xen. Please reboot your machine into Xen hypervisor.'
        )
        return

    logging.info("Testing if xl tool is sane...")

    try:
        subprocess.check_output('xl info',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        logging.exception("Failed to test xl command.")
        return

    try:
        subprocess.check_output('xl uptime vm-0',
                                shell=True,
                                stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError:
        pass
    else:
        logging.info('Detected that vm-0 is already running, stopping it.')
        subprocess.run('xl destroy vm-0', shell=True, check=True)

    generate_vm_conf(install_info, 0)

    backend = get_storage_backend(install_info)
    backend.initialize_vm0_volume(disk_size)

    try:
        subprocess.check_output("brctl show", shell=True)
    except subprocess.CalledProcessError:
        logging.exception(
            "Failed to execute brctl show. Make sure you have bridge-utils installed."
        )
        return

    net_enable = int(conf['drakrun'].get('net_enable', '0'))
    out_interface = conf['drakrun'].get('out_interface', '')
    dns_server = conf['drakrun'].get('dns_server', '')

    setup_vm_network(vm_id=0,
                     net_enable=net_enable,
                     out_interface=out_interface,
                     dns_server=dns_server)

    if net_enable:
        start_dnsmasq(vm_id=0, dns_server=dns_server, background=True)

    cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg")

    try:
        subprocess.run('xl create {}'.format(shlex.quote(cfg_path)),
                       shell=True,
                       check=True)
    except subprocess.CalledProcessError:
        logging.exception("Failed to launch VM vm-0")
        return

    logging.info("-" * 80)
    logging.info("Initial VM setup is complete and the vm-0 was launched.")
    logging.info(
        "Please now VNC to the port 5900 on this machine to perform Windows installation."
    )
    logging.info(
        "After you have installed Windows and booted it to the desktop, please execute:"
    )
    logging.info("# draksetup postinstall")

    with open(cfg_path, "r") as f:
        data = f.read()
        m = re.search(r'vncpasswd[ ]*=(.*)', data)
        if m:
            passwd = m.group(1).strip()
            if passwd[0] == '"' and passwd[-1] == '"':
                passwd = passwd[1:-1]

            logging.info("Your configured VNC password is:")
            logging.info(passwd)

    logging.info(
        "Please note that on some machines, system installer may boot for up to 10 minutes"
    )
    logging.info(
        "and may look unresponsive during the process. Please be patient.")
    logging.info("-" * 80)