コード例 #1
0
    def default_interfaces(self):
        L.info(
            "No interfaces specified for packet capture. Checking config to determine which to use"
        )

        if 'capture_interface' not in self._config:
            raise XVEx(
                "Specify the 'capture_interface' in the 'packet_capturer' component config"
            )

        if self._config['capture_interface'] == 'span':
            interface = self._device.config()['span_interface']
            L.info("Using span interface for capture: {}".format(interface))
            return [interface]
        elif self._config['capture_interface'] == 'primary':
            interface = self._get_primary_interface()
            L.info("Using primary interface for capture: {}".format(interface))
            return [interface]
        elif self._config['capture_interface'] == 'non-tunnel':
            interfaces = self._get_non_tunnel_interfaces()
            L.info("Using non-tunnel interfaces for capture: {}".format(
                interfaces))
            return interfaces
        elif self._config['capture_interface'] == 'all':
            interfaces = self._get_all_interfaces()
            L.info("Using all interfaces for capture: {}".format(interfaces))
            return interfaces
        else:
            raise XVEx(
                "Don't know how to handle capture_interface '{}'".format(
                    self._config['capture_interface']))
コード例 #2
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))
コード例 #3
0
    def _webrtc_ips(self, url_base):
        url = self._url(url_base)
        L.describe("Open ICE detection webpage {}".format(url))
        self._webdriver.get(url)

        # TODO: This isn't working properly. Also, try to automate
        if self._ask_perms:
            message_and_await_enter(
                "Please grant the browser permissions to use audio/video")

        # Not in love with this but not sure we have a lot of choice. There's no way to know whether
        # the javascript code has done searching for IPs TODO: Verify that
        L.describe("Wait for {} seconds for IPs to report".format(
            WebRTCICEHelper.TIME_TO_WAIT_FOR_IPS))
        time.sleep(WebRTCICEHelper.TIME_TO_WAIT_FOR_IPS)

        if 'xv_leak_tools local ICE IP detection' not in self._webdriver.title:
            raise XVEx("Got unexpected webpage title: {}".format(
                self._webdriver.title))

        L.describe(
            "Check ICE detection webpage doesn't show our public IP addresses")
        inner_html = self._webdriver.find_element_by_id(
            'ice_ips').get_attribute('innerHTML')
        if not inner_html:
            raise XVEx('ice_ips div in webrtc page contained no inner_html')
        json_reponse = json.loads(inner_html)

        if not json_reponse['webrtc_supported']:
            L.warning(
                'WebRTC not supported for this browser. Reporting no ips.')
            return []

        return [ipaddress.ip_address(ip) for ip in json_reponse['ips']]
コード例 #4
0
    def _parse_output(output):
        server = None
        lines = output.splitlines()
        for line in lines:
            matches = Dig.PROG_DIG_SERVER.match(line)
            if not matches:
                continue
            server = matches.group(1)
            break

        if server is None:
            raise XVEx("Couldn't parse dig output: {}".format(output))

        ips = []
        for iline, line in enumerate(lines):
            if 'ANSWER SECTION' not in line:
                continue
            ips = Dig._parse_answers(lines[iline + 1:])
            break

        if len(ips) == 0:
            raise XVEx(
                "dig failed to return any IPs. Output was: {}".format(output))

        return ipaddress.ip_address(server), ips
コード例 #5
0
    def start(self):
        L.debug('Starting IPResponder thread')
        if self._server_address is None:
            raise XVEx(
                "Server address must be set before starting IPResponder")

        if self._send_forever_thread is not None:
            raise XVEx("IP Responder already started")

        self._check_server()
        self._stop_sending = threading.Event()
        self._send_forever_thread = threading.Thread(target=self._send_forever)
        self._send_forever_thread.start()
コード例 #6
0
 def set_dns_servers(self, *ips):
     if len(ips) > 2:
         raise XVEx("There is only space for two DNS servers per adapter")
     cmd_set = [
         'netsh', 'interface', 'ip', 'set', 'dns',
         self.net_connection_id()
     ]
     if not ips:
         cmd_set.append('dhcp')
         L.debug("Setting DNS on {} via DHCP".format(
             self.net_connection_id()))
         check_subprocess(cmd_set)
     else:
         # TODO: this breaks if we set a server that's already set. Not sure
         # if that's a problem.
         L.debug("Setting primary DNS on {} to {}".format(
             self.net_connection_id(), ips[0]))
         check_subprocess(cmd_set + ['static', ips[0], 'index=1'])
         if len(ips) == 2:
             cmd_add = [
                 'netsh', 'interface', 'ip', 'add', 'dns',
                 self.net_connection_id()
             ]
             L.debug("Setting secondary DNS on {} to {}".format(
                 self.net_connection_id(), ips[1]))
             check_subprocess(cmd_add + [ips[1], 'index=2'])
コード例 #7
0
    def pingable(self):
        if not self.enabled():
            L.verbose("{} not enabled".format(self.name()))
            return False
        ips = self.ip_addresses()
        if not ips:
            L.verbose("{} has no IP address".format(self.name()))
            return False
        cmd = ['ping', '-n', '1', '-w', '2', '-S', ips[0].exploded, '8.8.8.8']
        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
コード例 #8
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)
コード例 #9
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))
コード例 #10
0
    def __init__(self, anchor_name=None, no_disable=False):
        if anchor_name is None:
            anchor_name = PFCtl._random_anchor_name()

        self._anchor_name = anchor_name
        self._no_disable = no_disable
        self._token = None
        if self._no_disable:
            try:
                self._pfctl(['-e'])
            except XVProcessException:
                pass
        else:
            lines = self._pfctl(['-E'])[1].splitlines()
            for line in lines:
                match = PFCtl.PROG_TOKEN.match(line)
                if not match:
                    continue
                self._token = match.group(1)
                L.debug("Got pfctl reference {}".format(self._token))
            if not self._token:
                raise XVEx(
                    "Couldn't parse token from pfctl output:\n {}".format(
                        "\n".join(lines)))

        self._ensure_anchor_is_present()
コード例 #11
0
def _iter_package(package, restrict_platform=True, is_pkg=True):
    def ignore_package(package, ignore_substrings):
        # Don't import anything which doesn't belong on this OS
        for ignore in ignore_substrings:
            if ignore in package:
                return True
        return False

    if restrict_platform and ignore_package(package, other_oses()):
        return

    try:
        L.debug("Importing {}".format(package))
        mod = importlib.import_module(package)
        package_path = os.path.normpath(os.path.split(mod.__file__)[0])
        yield mod
    except ImportError as ex:
        raise XVEx(
            "Can't import package '{}' ({}). Make sure the package directory is in your "
            "sys.path".format(package, ex))

    if not is_pkg:
        return

    L.verbose("Walking package path {}".format(package_path))
    for _, module_name, is_pkg2 in pkgutil.walk_packages([package_path]):
        for mod in _iter_package("{}.{}".format(package, module_name),
                                 restrict_platform, is_pkg2):
            yield mod
コード例 #12
0
 def os_version(self):
     try:
         # Use systeminfo on Windows as platform.win32_ver doesn't work for cygwin. That way the
         # code is agnostic to the shell.
         output = self._connector_helper.check_command(['systeminfo'])[0]
         for line in output.splitlines():
             match = WindowsDevice.PROG_OS_VERSION.match(line)
             if match:
                 return match.group(1)
         raise XVEx(
             "Couldn't determine Windows Version from systeminfo output:\n{}"
             .format(output))
     except XVProcessException as ex:
         raise XVEx(
             "Couldn't determine Windows Version as systeminfo failed:\n{}".
             format(ex))
コード例 #13
0
 def discover_device(self, discovery_keys):
     for discoverer in self._discoverers:
         device = discoverer.discover_device(discovery_keys)
         if device is not None:
             return device
     raise XVEx(
         "Couldn't discover device using keys: {}".format(discovery_keys))
コード例 #14
0
 def _receive(self, timeout=1):
     L.debug("Listening on %s:%s" % self._query_sock.getsockname())
     ready = select.select([self._query_sock], [], {}, timeout)
     if not ready[0]:
         raise XVEx("Didn't get a response from IP responder server")
     data = self._query_sock.recvfrom(4096)[0]
     return set(ipaddress.ip_address(ip) for ip in data.decode().split())
コード例 #15
0
    def _ssh_connect(self):
        connect_errors = ""
        for ip in self._ips:
            self._create_route_to_ip(ip)
            connect_dict = {
                'hostname': ip,
                'username': self._username,
                'password': self._ssh_password,
                'key_filename': self._ssh_key,
                'timeout': 5,
            }

            L.debug("Connecting SSH with args {}".format(connect_dict))

            self._ssh_client = paramiko.SSHClient()
            self._ssh_client.set_missing_host_key_policy(
                paramiko.AutoAddPolicy())
            try:
                self._ssh_client.connect(**connect_dict)
                self._sftp_client = self._ssh_client.open_sftp()
                return
            except (paramiko.ssh_exception.NoValidConnectionsError,
                    socket.timeout) as ex:
                connect_errors += "Couldn't connect to host via SSH connection at {}: {}\n".format(
                    ip, ex)
        raise XVEx("Couldn't connect to host:\n{}".format(connect_errors))
コード例 #16
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()
コード例 #17
0
    def _openvpn_process(self):
        if self._openvpn_process_cache:
            return self._openvpn_process_cache

        openvpn_pids = self._device.pgrep('openvpn')
        if not openvpn_pids:
            return

        # This is a bit of a hack. Some providers are known to spawn two openvpn instances. We only
        # want the parent one which is the one where --config is specified. So if we find more than
        # one the we will let this slide if exactly one of them has --config in it.
        if len(openvpn_pids) != 1:
            good_pids = []
            for pid in openvpn_pids:
                for arg in self._device.command_line_for_pid(pid):
                    if "--config" in arg:
                        good_pids.append(pid)
            if len(good_pids) != 1:
                raise XVEx(
                    "Found {} openvpn processes, expected exactly 1".format(
                        len(openvpn_pids)))
            openvpn_pids = good_pids

        self._openvpn_process_cache = openvpn_pids[0]
        return self._openvpn_process_cache
コード例 #18
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()
コード例 #19
0
    def set_current(self):
        self._sync_preferences()

        if not SCNetworkSetSetCurrent(
                self._cf_set()) or not self._save_preferences():
            raise XVEx(
                "Couldn't set location '{}' to current location".format(self))
コード例 #20
0
 def vpn_server_ip(self):
     ips = self._get_ips('Please input the VPN server IP.', max_ips=1)
     if len(ips) > 1:
         raise XVEx('User provided more than one VPN server IP')
     if ips:
         return ips[0]
     return None
コード例 #21
0
 def _create_rules(**kwargs):
     rules = {}
     for key, value in kwargs.items():
         if key not in TrafficFilter.VALID_RULES:
             raise XVEx("Invalid filter rule: {}".format(key))
         L.debug("Adding rule {}: {}".format(key, value))
         rules[key] = value
     return rules
コード例 #22
0
 def _find_windows_pid(self):
     pids = self._device.pgrep("xv_packet_capture.exe")
     for pid in pids:
         cmd_line = self._device.command_line_for_pid(pid)
         for arg in cmd_line:
             if self._capture_file in arg:
                 return pid
     raise XVEx("Couldn't find PID")
コード例 #23
0
 def refresh(self):
     for row in wmic_rows():
         if row['GUID'] == self.guid():
             self.data = row
             return
     raise XVEx(
         "Couldn't find adapter with GUID {} when refreshing data".format(
             self.guid()))
コード例 #24
0
    def __getitem__(self, component_name):
        if component_name in self._components:
            return self._components[component_name]

        raise XVEx(
            "Device {} has no component {}. Available components:\n{}".format(
                self.device_id(), component_name,
                "\n".join(self._components.keys())))
コード例 #25
0
 def wait_for_ips(self, timeout):
     start = time.time()
     ips = self.ipv4_addresses()
     while not ips and time.time() - start < timeout:
         time.sleep(1)
         ips = self.ipv4_addresses()
     L.debug("Got ips: {}".format(ips))
     if not ips:
         raise XVEx("Network service {} never got lease")
コード例 #26
0
 def _create_route_to_ip(self, ip):
     default_gateway = netifaces.gateways()['default'][netifaces.AF_INET][0]
     L.debug("Adding route to ip: {} -> {}".format(ip, default_gateway))
     if current_os() == 'windows':
         raise XVEx("TODO: Implement route on windows")
     else:
         route = [ip, default_gateway]
         subprocess.check_output(['route', 'add'] + route)
         self._routes_to_remote.append(route)
コード例 #27
0
 def adapter_by_net_connection_id(id_):
     adapters = WindowsNetwork._adapters_by("NetConnectionID",
                                            id_,
                                            unique=True)
     if not adapters:
         raise XVEx(
             "Couldn't find adapter with network connection ID '{}'".format(
                 id_))
     return adapters[0]
コード例 #28
0
 def _check_config(self, extra_keys=None):
     required_keys = ['ips', 'device_id']
     if extra_keys is not None:
         required_keys += extra_keys
     for key in required_keys:
         if key not in self._config:
             raise XVEx(
                 "Device type {} requires the config key '{}'".format(
                     self.__class__.__name__, key))
コード例 #29
0
 def primary_adapter(self):
     adapters = self.adapters_in_priority_order()
     pingable_adapters = [
         adapter for adapter in adapters if adapter.pingable()
     ]
     if pingable_adapters:
         return pingable_adapters[0]
     raise XVEx(
         "No pingable adapters. All adapters are {}".format(adapters))
コード例 #30
0
def class_by_name(name, base_class):
    available = []
    for subclass in itersubclasses(base_class):
        if subclass.__name__ == name:
            return subclass
        available.append(subclass)
    raise XVEx(
        "Couldn't find class {} deriving from {}. Available classes:\n{}".
        format(name, base_class,
               "\n".join([clazz.__name__ for clazz in available])))