def test_is_record_magic(): assert not tls.is_tls_record_magic(b"POST /") assert not tls.is_tls_record_magic(b"\x16\x03") assert not tls.is_tls_record_magic(b"\x16\x03\x04") assert tls.is_tls_record_magic(b"\x16\x03\x00") assert tls.is_tls_record_magic(b"\x16\x03\x01") assert tls.is_tls_record_magic(b"\x16\x03\x02") assert tls.is_tls_record_magic(b"\x16\x03\x03")
def ignore_connection(self, server_address: Optional[context.Address], data_client: bytes) -> Optional[bool]: """ Returns: True, if the connection should be ignored. False, if it should not be ignored. None, if we need to wait for more input data. """ if not ctx.options.ignore_hosts and not ctx.options.allow_hosts: return False hostnames: List[str] = [] if server_address is not None: hostnames.append(server_address[0]) if is_tls_record_magic(data_client): try: ch = parse_client_hello(data_client) if ch is None: # not complete yet return None sni = ch.sni except ValueError: pass else: if sni: hostnames.append(sni) if not hostnames: return False print("hostnames to check:", hostnames) print("ignore hosts options:", ctx.options.ignore_hosts) print("allow hosts options:", ctx.options.allow_hosts) if ctx.options.ignore_hosts: res = any( re.search(rex, host, re.IGNORECASE) for host in hostnames for rex in ctx.options.ignore_hosts) print("ignore hosts result (True means ignore):", res) return res elif ctx.options.allow_hosts: res = not any( re.search(rex, host, re.IGNORECASE) for host in hostnames for rex in ctx.options.allow_hosts) print("allow hosts result (True means ignore):", res) return res else: # pragma: no cover raise AssertionError()
def _next_layer(self, context: context.Context, data_client: bytes, data_server: bytes) -> Optional[layer.Layer]: if len(context.layers) == 0: return self.make_top_layer(context) if len(data_client) < 3 and not data_server: return None # not enough data yet to make a decision # helper function to quickly check if the existing layer stack matches a particular configuration. def s(*layers): return stack_match(context, layers) # 1. check for --ignore/--allow ignore = self.ignore_connection(context.server.address, data_client) if ignore is True: return layers.TCPLayer(context, ignore=True) if ignore is None: return None # 2. Check for TLS client_tls = is_tls_record_magic(data_client) if client_tls: # client tls usually requires a server tls layer as parent layer, except: # - a secure web proxy doesn't have a server part. # - reverse proxy mode manages this itself. if (s(modes.HttpProxy) or s(modes.ReverseProxy) or s(modes.ReverseProxy, layers.ServerTLSLayer)): return layers.ClientTLSLayer(context) else: # We already assign the next layer here os that ServerTLSLayer # knows that it can safely wait for a ClientHello. ret = layers.ServerTLSLayer(context) ret.child_layer = layers.ClientTLSLayer(context) return ret # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy. if (s(modes.HttpProxy) or # or a "Secure Web Proxy", see https://www.chromium.org/developers/design-documents/secure-web-proxy s(modes.HttpProxy, layers.ClientTLSLayer)): if ctx.options.mode == "regular": return layers.HttpLayer(context, HTTPMode.regular) else: return layers.HttpLayer(context, HTTPMode.upstream) # 4. Check for --tcp if any( (context.server.address and rex.search(context.server.address[0])) or (context.client.sni and rex.search(context.client.sni)) for rex in self.tcp_hosts): return layers.TCPLayer(context) # 5. Check for raw tcp mode. very_likely_http = (context.client.alpn and context.client.alpn in HTTP_ALPNS) probably_no_http = not very_likely_http and ( not data_client[:3].isalpha( ) # the first three bytes should be the HTTP verb, so A-Za-z is expected. or data_server # a server greeting would be uncharacteristic. ) if ctx.options.rawtcp and probably_no_http: return layers.TCPLayer(context) # 6. Assume HTTP by default. return layers.HttpLayer(context, HTTPMode.transparent)
def _next_layer(self, context: context.Context, data_client: bytes, data_server: bytes) -> Optional[layer.Layer]: if len(context.layers) == 0: return self.make_top_layer(context) if len(data_client) < 3 and not data_server: return None client_tls = is_tls_record_magic(data_client) def s(*layers): return stack_match(context, layers) top_layer = context.layers[-1] # 1. check for --ignore/--allow ignore = self.ignore_connection(context.server.address, data_client) if ignore is True: return layers.TCPLayer(context, ignore=True) if ignore is None: return None # 2. Check for TLS if client_tls: # client tls requires a server tls layer as parent layer # reverse proxy mode manages this itself. # a secure web proxy doesn't have a server part. if isinstance(top_layer, layers.ServerTLSLayer) or s(modes.ReverseProxy) or s(modes.HttpProxy): return layers.ClientTLSLayer(context) else: return layers.ServerTLSLayer(context) # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy. if any([ s(modes.HttpProxy), # or a "Secure Web Proxy", see https://www.chromium.org/developers/design-documents/secure-web-proxy s(modes.HttpProxy, layers.ClientTLSLayer), ]): if ctx.options.mode == "regular": return layers.HttpLayer(context, HTTPMode.regular) else: return layers.HttpLayer(context, HTTPMode.upstream) # 4. Check for --tcp if any( (context.server.address and rex.search(context.server.address[0])) or (context.client.sni and rex.search(context.client.sni)) for rex in self.tcp_hosts ): return layers.TCPLayer(context) # 5. Check for raw tcp mode. very_likely_http = ( context.client.alpn and context.client.alpn in HTTP_ALPNS ) probably_no_http = not very_likely_http and ( not data_client[:3].isalpha() # the first three bytes should be the HTTP verb, so A-Za-z is expected. or data_server # a server greeting would be uncharacteristic. ) if ctx.options.rawtcp and probably_no_http: return layers.TCPLayer(context) # 6. Assume HTTP by default. return layers.HttpLayer(context, HTTPMode.transparent)
def _next_layer(self, top_layer): try: d = top_layer.client_conn.rfile.peek(3) except exceptions.TcpException as e: raise exceptions.ProtocolException(str(e)) client_tls = tls.is_tls_record_magic(d) # 1. check for filter skipped_url = "" is_filtered = False if True: #self.config.check_filter: if not top_layer.server_conn.address is None: is_filtered = (self.config.check_filter( top_layer.server_conn.address) or selfc.SelfCShared.isTrusted( top_layer.server_conn.address[0])) if is_filtered: skipped_url = top_layer.server_conn.address[0] if not is_filtered and client_tls: try: client_hello = tls.ClientHello.from_file( self.client_conn.rfile) except exceptions.TlsProtocolException as e: self.log("Cannot parse Client Hello: %s" % repr(e), "error") else: sni_str = client_hello.sni and client_hello.sni.decode( "idna") if not sni_str is None: is_filtered = self.config.check_filter( (sni_str, 443)) or selfc.SelfCShared.isTrusted(sni_str) if is_filtered: skipped_url = sni_str if is_filtered: print("####### MITM Skip, Trusted: " + skipped_url) return protocol.RawTCPLayer(top_layer, ignore=True) # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. if isinstance(top_layer, modes.ReverseProxy): return protocol.TlsLayer(top_layer, client_tls, top_layer.server_tls, top_layer.server_conn.address[0]) if isinstance(top_layer, protocol.ServerConnectionMixin): return protocol.TlsLayer(top_layer, client_tls, client_tls) if isinstance(top_layer, protocol.UpstreamConnectLayer): # if the user manually sets a scheme for connect requests, we use this to decide if we # want TLS or not. if top_layer.connect_request.scheme: server_tls = top_layer.connect_request.scheme == "https" else: server_tls = client_tls return protocol.TlsLayer(top_layer, client_tls, server_tls) # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. if isinstance(top_layer, protocol.TlsLayer): if isinstance(top_layer.ctx, modes.HttpProxy): return protocol.Http1Layer(top_layer, http.HTTPMode.regular) if isinstance(top_layer.ctx, modes.HttpUpstreamProxy): return protocol.Http1Layer(top_layer, http.HTTPMode.upstream) # 4. Check for other TLS cases (e.g. after CONNECT). if client_tls: return protocol.TlsLayer(top_layer, True, True) # 4. Check for --tcp if self.config.check_tcp(top_layer.server_conn.address): return protocol.RawTCPLayer(top_layer) # 5. Check for TLS ALPN (HTTP1/HTTP2) if isinstance(top_layer, protocol.TlsLayer): alpn = top_layer.client_conn.get_alpn_proto_negotiated() if alpn == b'h2': return protocol.Http2Layer(top_layer, http.HTTPMode.transparent) if alpn == b'http/1.1': return protocol.Http1Layer(top_layer, http.HTTPMode.transparent) # 6. Check for raw tcp mode is_ascii = ( len(d) == 3 and # expect A-Za-z all(65 <= x <= 90 or 97 <= x <= 122 for x in d)) if self.config.options.rawtcp and not is_ascii: return protocol.RawTCPLayer(top_layer) # 7. Assume HTTP1 by default return protocol.Http1Layer(top_layer, http.HTTPMode.transparent)
def _next_layer(self, top_layer): logging.info('root_context._next_layer(%s)', top_layer) try: d = top_layer.client_conn.rfile.peek(3) except exceptions.TcpException as e: logging.info('ignoring protocol: %s', e) raise exceptions.ProtocolException(str(e)) client_tls = tls.is_tls_record_magic(d) host, port = '(unknown)', '(unknown)' # 1. check for filter if self.config.check_filter: if top_layer.server_conn.address: host, port = top_layer.server_conn.address is_filtered = self.config.check_filter( top_layer.server_conn.address) logging.info('_next_layer: %s is_filtered: %s', vars(top_layer.server_conn), is_filtered) if not is_filtered and client_tls: try: client_hello = tls.ClientHello.from_file( self.client_conn.rfile) except exceptions.TlsProtocolException as e: self.log("Cannot parse Client Hello: %s" % repr(e), "error") else: sni_str = client_hello.sni and client_hello.sni.decode( "idna") host, port = (sni_str, 443) is_filtered = self.config.check_filter((host, port)) logging.info('is_filtered: %s', is_filtered) if is_filtered: if self.config.options.block_not_ignore: self.log( '-> {}:{} {} blocked'.format(host, port, datetime.now()), 'info') raise exceptions.Kill( f'blocked https request to filtered host {host}') else: return protocol.RawTCPLayer(top_layer, ignore=True) else: self.log( '-> {}:{} {} allowed'.format(host, port, datetime.now()), 'info') # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. if isinstance(top_layer, modes.ReverseProxy): return protocol.TlsLayer(top_layer, client_tls, top_layer.server_tls, top_layer.server_conn.address[0]) if isinstance(top_layer, protocol.ServerConnectionMixin): return protocol.TlsLayer(top_layer, client_tls, client_tls) if isinstance(top_layer, protocol.UpstreamConnectLayer): # if the user manually sets a scheme for connect requests, we use this to decide if we # want TLS or not. if top_layer.connect_request.scheme: server_tls = top_layer.connect_request.scheme == "https" else: server_tls = client_tls return protocol.TlsLayer(top_layer, client_tls, server_tls) # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. if isinstance(top_layer, protocol.TlsLayer): if isinstance(top_layer.ctx, modes.HttpProxy): return protocol.Http1Layer(top_layer, http.HTTPMode.regular) if isinstance(top_layer.ctx, modes.HttpUpstreamProxy): return protocol.Http1Layer(top_layer, http.HTTPMode.upstream) # 4. Check for other TLS cases (e.g. after CONNECT). if client_tls: return protocol.TlsLayer(top_layer, True, True) # 4. Check for --tcp if self.config.check_tcp(top_layer.server_conn.address): return protocol.RawTCPLayer(top_layer) # 5. Check for TLS ALPN (HTTP1/HTTP2) if isinstance(top_layer, protocol.TlsLayer): alpn = top_layer.client_conn.get_alpn_proto_negotiated() if alpn == b'h2': return protocol.Http2Layer(top_layer, http.HTTPMode.transparent) if alpn == b'http/1.1': return protocol.Http1Layer(top_layer, http.HTTPMode.transparent) # 6. Check for raw tcp mode is_ascii = ( len(d) == 3 and # expect A-Za-z all(65 <= x <= 90 or 97 <= x <= 122 for x in d)) if self.config.options.rawtcp and not is_ascii: return protocol.RawTCPLayer(top_layer) # 7. Assume HTTP1 by default return protocol.Http1Layer(top_layer, http.HTTPMode.transparent)