def check_is_open_web_socket(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket is open: * Any Origin can connect * No cookies required for authentication * No basic auth required for authentication """ upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=self.W3AF_ORIGIN) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): return False msg = ('An HTML5 WebSocket which allows connections from any origin' ' without authentication was found at "%s"') msg %= web_socket_url v = Vuln.from_fr('Open WebSocket', msg, severity.LOW, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True
def check_is_restricted_by_origin_with_match_bug(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket checks the origin for connections but there is a bug in the matching process """ # # Keep in mind that we get here only if the websocket is NOT an open # (accepts any origin) socket. So we're in a situation where the socket # is either verifying by Origin+Cookies, Origin+Basic Auth or just # Origin. # # We want to check for the "just Origin" now, with a twist, we're # checking if there is a mistake in the Origin domain match process # # This is the trick: origin_domain = web_socket_url.get_domain() origin_domain += '.%s' % self.W3AF_DOMAIN for scheme in {'http', 'https'}: origin = '%s://%s' % (scheme, origin_domain) upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=origin) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): continue msg = ('An HTML5 WebSocket which restricts connections based on the' ' Origin header was found to be vulnerable because of an' ' incorrect matching algorithm. The "%s" Origin was allowed' ' to connect to "%s".') msg %= (origin_domain, web_socket_url) v = Vuln.from_fr('Insecure WebSocket Origin filter', msg, severity.MEDIUM, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True return False
def check_is_restricted_by_origin(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket checks the origin for connections: * Only the same origin can connect * Send any cookie/basic auth known to the scanner """ # # Keep in mind that we get here only if the websocket is NOT an open # (accepts any origin) socket. So we're in a situation where the socket # is either verifying by Origin+Cookies, Origin+Basic Auth or just # Origin. # # We want to check for the "just Origin" now # origin_domain = web_socket_url.get_domain() for scheme in {'http', 'https'}: origin = '%s://%s' % (scheme, origin_domain) upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=origin) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): continue msg = ('An HTML5 WebSocket which allows connections only when the' ' origin is set to "%s" was found at "%s"') msg %= (origin_domain, web_socket_url) v = Vuln.from_fr('Origin restricted WebSocket', msg, severity.LOW, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True return False
def check_need_cookie_origin_not_restricted(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket does NOT check the origin for connections but DOES require cookies to connect """ # # Keep in mind that we get here only if: # * The websocket is NOT an open (accepts any origin) socket # * The websocket is NOT verifying by Origin # # So we're in one of these cases: # * The websocket authenticates by cookie # * The websocket authenticates by basic auth # # We want to check for the "authenticates by cookie" # upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=self.W3AF_ORIGIN) upgrade_response = self._uri_opener.send_mutant(upgrade_request, # Note the True here! cookies=True, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): return False msg = 'Cross-Site WebSocket Hijacking has been found at "%s"' msg %= web_socket_url v = Vuln.from_fr('Websockets CSRF vulnerability', msg, severity.HIGH, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True