def _find_primary_adapter(self):
     adapters = self._device['network_tool'].adapters_in_priority_order()
     primary_adapter = [
         adapter for adapter in adapters if adapter.pingable()
     ][0]
     L.info("Primary network adapter is {}".format(primary_adapter.name()))
     return primary_adapter
    def remote_mkdir(self, path, mode=None):
        args = [path]
        if mode:
            args += ["--mode={}".format(str(mode))]

        L.debug("Making remote directory: {}".format(path))
        self.execute_scriptlet('remote_mkdir.py', args)
Exemplo n.º 3
0
    def detect(self):
        L.debug("Trying to determine if we're using the l2tp protocol")
        vpn_info = VPNInfo()

        # Assume that no-process definitively means no openvpn
        if not self._l2tp_processes():
            L.debug('Not using l2tp')
            return None

        L.info("Detected l2tp as the current VPN protocol")

        vpn_info.vpn_processes = self._l2tp_processes()
        vpn_info.protocol = 'l2tp'
        # TODO: Do tunnel_interface
        vpn_info.vpn_server_ip = self._get_vpn_server_ip()
        vpn_info.dns_server_ips = []
        vpn_info.tunnel_interface = None

        if vpn_info.vpn_server_ip == ipaddress.ip_address('127.0.0.1'):
            L.warning(
                "Got vpn server IP 127.0.0.1. The app is likely using a proxy. Removing this IP"
            )
            vpn_info.vpn_server_ip = None

        # Prevent anyone who's holding on to this class from using stale data.
        self._reset()
        L.debug(
            "Detected the following info about openvpn: {}".format(vpn_info))
        return vpn_info
Exemplo n.º 4
0
    def ngrok_main(self, port):
        cmd = [
            'ngrok', 'http',
            str(port), '-log', 'stdout', '--log-level', 'debug'
        ]
        self._ngrok_proc = subprocess.Popen(cmd,
                                            stdout=subprocess.PIPE,
                                            stderr=None)
        theq = queue.Queue()
        thread = threading.Thread(target=WebServer.enqueue_ngrok_output,
                                  args=(self._ngrok_proc.stdout, theq))
        thread.daemon = True  # thread dies with the program
        thread.start()

        prog = re.compile(r".*URL:([^\s]+)\s.*")
        while 1:
            try:
                line = theq.get(timeout=1)
                match = prog.match(str(line))
                if not match:
                    continue

                url = match.group(1)
                L.debug("ngrok secure url is {}".format(url))
                return match.group(1)
            except queue.Empty:
                # Add a timeout for getting URL
                pass
Exemplo n.º 5
0
    def _config_file(self):
        if self._config_file_cache:
            return self._config_file_cache

        config_file = self._command_line_arg('--config')
        if not config_file:
            return None

        if os.path.exists(config_file):
            self._config_file_cache = config_file
            L.debug("Got config file {}".format(self._config_file_cache))
            return self._config_file_cache

        # Couldn't find the config file so maybe it's a relative path to --cd
        working_directory = self._command_line_arg('--cd')
        if working_directory:
            config_file = os.path.join(working_directory,
                                       self._command_line_arg('--config'))
            if os.path.exists(config_file):
                self._config_file_cache = config_file
                L.debug("Got config file {}".format(self._config_file_cache))
                return self._config_file_cache

        raise XVEx(
            "Found argument --config={} but the config files doesn't exists".
            format(config_file))
Exemplo n.º 6
0
    def _try_vpn_server_ip_from_config_file(self):
        L.debug("Trying to determine the VPN server IP from the config file")
        if not self._config_file():
            L.debug(
                "Couldn't get config file so can't determine the VPN server IP"
            )
            return None

        # TODO: DRY
        with open(self._config_file()) as file_:
            for line in file_.readlines():
                match = OpenVPNDetector.PROG_REMOTE.match(line)
                if match:
                    ip = match.group(1)
                    L.debug(
                        "Got VPN server IP {} from the --remote".format(ip))
                    return ip
                match = OpenVPNDetector.PROG_ROUTE.match(line)
                if match:
                    ip = match.group(1)
                    L.debug("Got VPN server IP {} from the --route".format(ip))
                    return ip
        L.debug("Couldn't determine VPN server IP from config file {}".format(
            self._config_file()))
        return None
Exemplo n.º 7
0
 def do_dns_requests(self):
     while not self.thread_should_exit():
         try:
             hostname = random.choice(TestDNSVanillaAggressivePacketCapture.HOSTNAMES)
             self.localhost['dns_tool'].lookup(hostname)
         except XVEx as ex:
             L.debug("DNS lookup failed. Not considering this an error: {}".format(ex))
 def start_checking_dns(self):
     self._stop_dns = False
     L.info("Starting {} background DNS threads".format(
         self._background_threads))
     for _ in range(0, self._background_threads):
         self._thread_results.append(
             self._thread_pool.apply_async(self.dns_check_main))
    def dns_server_ips(self):
        info = self._vpn_info()
        if info is not None and info.dns_server_ips:
            return info.dns_server_ips

        L.warning("Couldn't find VPN DNS server IPs. Will fallback to default method")
        return super().dns_server_ips()
    def vpn_processes(self):
        info = self._vpn_info()
        if info is not None and info.vpn_processes:
            return info.vpn_processes

        L.warning("Couldn't find VPN processes. Will fallback to default method")
        return super().vpn_processes()
    def protocol(self):
        info = self._vpn_info()
        if info is not None and info.protocol:
            return info.protocol

        L.warning("Couldn't determine the VPN protocol. Will fallback to default method")
        return super().protocol()
    def remote_makedirs(self, path, mode=None):
        args = [path]
        if mode:
            args += ["--mode={}".format(str(mode))]

        L.debug("Making remote directories: {}".format(path))
        return self.execute_scriptlet('remote_makedirs.py', args)
Exemplo n.º 13
0
    def start(self):
        L.debug("Starting packet capture on interface {}".format(
            self._interface))
        if self._popen:
            raise XVEx("Packet capture already started!")

        binary = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                              "..", "bin", "xv_packet_capture.exe")

        os.path.join(self._device.temp_directory(),
                     WindowsGoPacketCapturer._random_pid_file())

        binary = windows_real_path(binary)
        output_directory = windows_real_path(self._device.temp_directory())
        cmd = [
            'cmd.exe', '/c', binary, '-i',
            str(self._interface), '-o', output_directory, '-f',
            self._capture_file, '-m', 'windump', '--preserve', '--debug'
        ]

        stdout = os.path.join(self._device.temp_directory(),
                              "{}.stdout".format(self._capture_file))
        stderr = os.path.join(self._device.temp_directory(),
                              "{}.stderr".format(self._capture_file))

        # TODO: Check for errors once opened?
        L.debug("Starting packet capture: {}".format(cmd))
        makedirs_safe(self._device.temp_directory())
        with open(stdout, "w") as out, open(stderr, "w") as err:
            self._popen = subprocess.Popen(cmd, stdout=out, stderr=err)

        self._pid = self._find_windows_pid()
Exemplo n.º 14
0
 def release_devices(self):
     # TODO: Merge this into device teardown. Probably do it with a component, e.g. vm_manager.
     # However, we could do it via the connector and make it part of that objects responsibility.
     # The advantage with a component is that it's configurable, so we can configure things like
     # whether to update to a snapshot or whether to pause.
     for vmx in self._vms_in_use:
         L.debug("Pausing VM: {}".format(vmx))
Exemplo n.º 15
0
 def block_application(full_path, direction="out"):
     full_path = windows_real_path(full_path)
     L.info(
         "Creating firewall rule to block application {}".format(full_path))
     return WindowsAdvFirewall.create_rule("block",
                                           program=full_path,
                                           direction=direction)
Exemplo n.º 16
0
 def _create_chain(self, chain):
     L.debug("Creating iptables chain {}".format(chain))
     if self._chain_exists(chain):
         L.debug("iptables chain {} exists".format(chain))
         return
     self._connector_helper.check_command(["iptables", "-w", "-N", chain],
                                          root=True)
Exemplo n.º 17
0
    def discover_device(self, discovery_keys):
        L.debug('Looking for device with keys {}'.format(discovery_keys))

        device = self._inventory_item_for_discovery_keys(discovery_keys)
        if device is None:
            return None

        if 'output_root' not in device:
            raise XVEx("Device config didn't specify 'output_root': {}".format(device))

        device['output_directory'] = os.path.join(
            device['output_root'], self._context['run_directory'])

        # TODO: Look into refactoring this.
        if 'dummy' in device and device['dummy']:
            connector = DummyConnector()
        elif 'adb_id' in device and device['adb_id']:
            connector = ADBConnector(device['adb_id'])
        else:
            connector = SimpleSSHConnector(
                ips=device['ips'],
                username=device['username'],
                account_password=device.get('account_password', None),
                ssh_key=device.get('ssh_key', None),
                ssh_password=device.get('ssh_password', None)
            )

        return create_device(device['os_name'], device, connector)
Exemplo n.º 18
0
 def delete_rule(name):
     L.info("Deleting firewall rule {}".format(name))
     cmd = [
         'netsh', 'advfirewall', 'firewall', 'delete', 'rule',
         "name={}".format(name)
     ]
     check_subprocess(cmd)
 def setup(self):
     super().setup()
     # TODO: Hardcoded 1 and 1000. Does it matter?
     self._adapter1.set_interface_metric(1)
     L.describe("Set interface metric for adapter {} to {}".format(self._adapter1.name(), 1))
     self._adapter2.set_interface_metric(1000)
     L.describe("Set interface metric for adapter {} to {}".format(self._adapter2.name(), 1000))
    def pingable(self):
        if not self.enabled():
            return False
        ips = self.ip_addresses()
        if not ips:
            return False
        cmd = ['ping', '-n', '1', '-w', '2', '-S', ips[0].exploded, '8.8.8.8']
        output = ""
        try:
            output = check_subprocess(cmd)[0]
            if 'Received = 1' in output:
                return True
            else:
                # Consider this a real error and propagate. It's likely a code issue.
                raise XVEx(
                    "Don't know how to parse ping output: {}".format(output))
        except XVProcessException as ex:
            if 'Lost = 1' in ex.stderr:
                L.debug("Couldn't ping on adapter {}".format(self))
                return False
            L.warning("Ping failed unexpectedly with error '{}'. "
                      "Assuming adapter {} un-pingable.".format(
                          ex.stderr, self))

            return False
 def test_with_packet_capture(self):
     L.describe("Create disruption...")
     self.disrupter.create_disruption()
     L.describe("Generate traffic")
     self.webdriver = self.localhost['webdriver'].driver(
         self.parameters['browser'])
     self.webdriver.get("https://www.expressvpn.com/dns-leak-test")
Exemplo n.º 22
0
 def _get_full_tap_adapter_name(self, tap_adapter_name):
     if tap_adapter_name is None:
         return None
     # The logic here is necessary because Windows can give the TAP adapters suffixes like #2.
     # So the name might not match exactly what we expect. I think this happens when there's a
     # name collision. If we have two adapters with the same name then let's error as it's
     # probably going to cause a problem.
     L.debug("Looking for TAP adapter with name '{}'".format(tap_adapter_name))
     adapters = self._device['network_tool'].adapters_in_priority_order()
     candidates = []
     for adapter in adapters:
         adapter_name = adapter.name()
         if tap_adapter_name == adapter_name:
             # If we find an exact match then just return it
             return adapter_name
         if adapter_name.startswith(tap_adapter_name):
             candidates.append(adapter_name)
     if len(candidates) == 1:
         L.debug("Found TAP adapter with name '{}'".format(candidates[0]))
         return candidates[0]
     if len(candidates) == 0:
         raise XVEx("Found no candidate adapters matching TAP adapter '{}' for '{}':\n{}".format(
             tap_adapter_name, self._config['name'], candidates))
     raise XVEx("Found several candidate adapters matching TAP adapter '{}' for '{}':\n{}".format(
         tap_adapter_name, self._config['name'], candidates))
Exemplo n.º 23
0
    def set_lan_ip(self, ip):
        L.info("Setting LAN IP for router to: {}".format(ip))

        self._connector_helper.check_command(
            ['uci', 'set', "network.lan.ipaddr='{}'".format(ip.exploded)])
        self._connector_helper.check_command(['uci', 'commit', 'network'])
        self._connector_helper.check_command(['/etc/init.d/network', 'restart', '&'])
Exemplo n.º 24
0
    def stop(self):
        L.debug("Stopping packet capture on interface {} and getting packets".
                format(self._interface))

        if not self._popen:
            raise XVEx("Packet capture not started!")

        old_handler = None

        def interrupt_func(_, __):
            L.debug(
                "Ignoring interrupt in main process when killing packet capture"
            )
            signal.signal(signal.SIGINT, old_handler)

        old_handler = signal.signal(signal.SIGINT, interrupt_func)

        # Requires https://github.com/alirdn/windows-kill
        subprocess.Popen(['windows-kill', '-SIGINT',
                          str(self._pid)],
                         stdout=subprocess.DEVNULL,
                         stderr=subprocess.DEVNULL)

        self._pid = None

        # self._popen.send_signal(2)
        self._popen.wait()
        self._popen = None
        return self._get_packets()
 def open(self):
     if self._app_path:
         L.info("Opening VPN application {} (it might take a moment to appear)".format(
             self._config["name"]))
         self._device.open_app(self._app_path)
     else:
         super().open()
    def report_info(self):
        info = super().report_info()
        try:
            info += self._connector_helper.check_command(['systeminfo'])[0]
        except XVProcessException as ex:
            L.warning("Couldn't get OS info from systeminfo:\n{}".format(ex))

        return info
Exemplo n.º 27
0
 def _create_rules_file(rules):
     filehandle, path = tempfile.mkstemp(suffix='_pf_rules.conf',
                                         prefix='xv_leak_test_rules_')
     with os.fdopen(filehandle, 'w') as _file:
         for rule in rules:
             _file.write("{}\n".format(rule))
     L.debug("Wrote pf file {} with rules:\n{}".format(path, rules))
     return path
Exemplo n.º 28
0
    def _create_rule(self, rule_args):
        L.debug("Creating iptables rule {}".format(rule_args))
        if self._rule_exists(rule_args):
            L.debug("iptables rule {} already exists".format(rule_args))

        self._connector_helper.check_command(["iptables", "-w", "-A"] +
                                             rule_args,
                                             root=True)
    def setup(self):
        super().setup()

        L.describe('Ensure no VPN apps are connected or open')
        self.target_device['cleanup'].cleanup()

        L.describe('Configure VPN application')
        self.target_device['vpn_application'].configure()
Exemplo n.º 30
0
    def disrupt(self):
        L.describe('Find the VPN processes and kill them (not the main application)')
        pids = self._device['vpn_application'].vpn_processes()
        self.assertNotEmpty(pids, 'Found no VPN processes. This should not happen')

        for pid in pids:
            self._device.kill_process(pid)
            L.info("Killed VPN process (PID {})".format(pid))