|
from mitmproxy import tls, connection |
|
from mitmproxy.script import concurrent |
|
from tldextract import extract |
|
import secrets |
|
|
|
block_domain = set() |
|
skip_domain = set() |
|
modify_sni = {} |
|
|
|
|
|
def _generate_fake_sni(original_sni: str) -> str: |
|
""" |
|
Well this work by send random subdomain to bypass DPI SNI filtering |
|
example if original_sni is example.com |
|
it will return ab12cd34.example.com wich skip check by DPI |
|
work as well for subdomain like a.b.example.com |
|
it will return ef56gh78.example.com |
|
""" |
|
registered_domain = extract(original_sni).registered_domain |
|
return f"{secrets.token_hex(4)}.{registered_domain}" |
|
|
|
|
|
@concurrent |
|
def tls_clienthello(data: tls.ClientHelloData): |
|
""" |
|
Check if the SNI is in the skip_domain list to ignore the connection |
|
this will prevent mitm tampering with the connection |
|
this help web or app that check certificate pinning to work properly |
|
""" |
|
if data.client_hello.sni is None or data.client_hello.sni in skip_domain: |
|
data.ignore_connection = True |
|
return |
|
|
|
|
|
@concurrent |
|
def tls_start_server(data: tls.TlsData): |
|
""" |
|
This is modify the SNI to bypass DPI filtering |
|
if the SNI is in the block_domain list |
|
""" |
|
|
|
if data.context.client.sni not in block_domain: |
|
return |
|
|
|
if data.context.client.sni in modify_sni: |
|
new_sni = modify_sni[data.context.client.sni] |
|
else: |
|
new_sni = _generate_fake_sni(data.context.client.sni) |
|
data.context.server.sni = new_sni |
|
modify_sni[data.context.client.sni] = new_sni |
|
print(f"Modified SNI from {data.context.client.sni} to {new_sni}") |
|
|
|
|
|
@concurrent |
|
def tls_failed_server(data: tls.TlsData): |
|
""" |
|
Handle TLS failures from the server side |
|
If the failure is due to certificate verification, add the domain to skip_domain |
|
This to avoid future attempts to mitm try to tamper with the connection |
|
and keep connections preserved |
|
""" |
|
|
|
print(f"TLS failed server for SNI: {data.context.server.sni}") |
|
# Add to block domain for future connections |
|
block_domain.add(data.context.client.sni) |
|
if data.conn.error.startswith("Certificate verify failed"): |
|
skip_domain.add(data.context.client.sni) |
|
data.context.server.state = connection.ConnectionState.CLOSED |
|
data.context.client.state = connection.ConnectionState.CLOSED |
|
print( |
|
f"Added {data.context.client.sni} to skip_domain due to certificate verification failure" |
|
) |
|
return |
|
print(f"Reason Error Server: {data.conn.error}") |
|
print(f"Added {data.context.client.sni} to block_domain for SNI bypass") |
|
data.context.server.state = connection.ConnectionState.CLOSED |
|
data.context.client.state = connection.ConnectionState.CLOSED |
|
|
|
|
|
@concurrent |
|
def tls_failed_client(data: tls.TlsData): |
|
""" |
|
Handle TLS failures from the client side this use to add domain to skip_domain |
|
to avoid future attempts to mitm tamper with the connection that ssl pinning apps or website use |
|
Then kill the connection to make reconnection with no mitm tampering |
|
""" |
|
|
|
print(f"TLS failed client for SNI: {data.context.client.sni}") |
|
if data.context.client.sni in block_domain: |
|
return |
|
print(f"Reason Error Client: {data.conn.error}") |
|
skip_domain.add(data.context.client.sni) |
|
print(f"Added {data.context.client.sni} to skip_domain to avoid future attempts") |
|
data.context.server.state = connection.ConnectionState.CLOSED |
|
data.context.client.state = connection.ConnectionState.CLOSED |
|
|
|
|
|
@concurrent |
|
def tls_established_server(data: tls.TlsData): |
|
""" |
|
Clean up the modify_sni mapping after a successful TLS handshake |
|
This prevents the mapping from growing indefinitely |
|
""" |
|
|
|
if data.context.client.sni in modify_sni: |
|
original_sni = data.context.client.sni |
|
new_sni = modify_sni[original_sni] |
|
print( |
|
f"SNI bypass successful for {original_sni}, using modified SNI: {new_sni}" |
|
) |
|
# Clean up the mapping after successful connection |
|
del modify_sni[original_sni] |