Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Configuring TLS 1.3 Cipher Suites #4260

Open
mhils opened this issue Nov 2, 2020 · 10 comments
Open

Add Support for Configuring TLS 1.3 Cipher Suites #4260

mhils opened this issue Nov 2, 2020 · 10 comments
Labels
area/protocols kind/feature New features / enhancements

Comments

@mhils
Copy link
Member

mhils commented Nov 2, 2020

Due to the major differences between the way that ciphersuites for TLSv1.2 and below and ciphersuites for TLSv1.3 work, they are configured in OpenSSL differently too.

We don't support configuring TLSv1.3 cipher suites yet. If this would be useful to you, please comment here.

@mhils mhils added kind/feature New features / enhancements area/protocols labels Nov 2, 2020
@c1b3rd0rk
Copy link

I think it would be a useful feature and even a necessity at some point. Beyond that, would it be possible to customize the handshake so that the proxy-server connection looks for example like a normal browser-server connection ?
Some environments may act differently upon the clients TLS fingerprint.

https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967

@mhils
Copy link
Member Author

mhils commented Nov 28, 2020

@c1b3rd0rk: Yes. We're currently refactoring our proxy core (#1775), which lays the foundation for this. The entire OpenSSL TLS configuration will be handled in a mitmproxy addon and can then be customized by users:

def create_proxy_server_ssl_conn(self, tls_start: tls.TlsStartData) -> None:
client = tls_start.context.client
server = cast(context.Server, tls_start.conn)
if server.sni is True:
server.sni = client.sni or server.address[0].encode()
if not server.alpn_offers:
if client.alpn_offers:
if ctx.options.http2:
server.alpn_offers = tuple(client.alpn_offers)
else:
server.alpn_offers = tuple(x for x in client.alpn_offers if x != b"h2")
elif client.tls_established:
# We would perfectly support HTTP/1 -> HTTP/2, but we want to keep things on the same protocol version.
# There are some edge cases where we want to mirror the regular server's behavior accurately,
# for example header capitalization.
server.alpn_offers = []
elif ctx.options.http2:
server.alpn_offers = tls.HTTP_ALPNS
else:
server.alpn_offers = tls.HTTP1_ALPNS
# We pass through the list of ciphers send by the client, because some HTTP/2 servers
# will select a non-HTTP/2 compatible cipher from our default list and then hang up
# because it's incompatible with h2.
if not server.cipher_list:
if ctx.options.ciphers_server:
server.cipher_list = ctx.options.ciphers_server.split(":")
elif client.cipher_list:
# We used to filter for known ciphers here, but that doesn't seem to make sense.
# According to OpenSSL docs, the control string str should be universally
# usable and not depend on details of the library configuration (ciphers compiled in).
server.cipher_list = list(client.cipher_list)
args = net_tls.client_arguments_from_options(ctx.options)
client_certs = args.pop("client_certs")
client_cert: Optional[str] = None
if client_certs:
client_certs = os.path.expanduser(client_certs)
if os.path.isfile(client_certs):
client_cert = client_certs
else:
server_name: str = (server.sni or server.address[0].encode("idna")).decode()
path = os.path.join(client_certs, f"{server_name}.pem")
if os.path.exists(path):
client_cert = path
args["cipher_list"] = ':'.join(server.cipher_list) if server.cipher_list else None
ssl_ctx = net_tls.create_client_context(
cert=client_cert,
sni=server.sni.decode("idna"), # TODO: Should pass-through here.
alpn_protos=server.alpn_offers,
**args
)
tls_start.ssl_conn = SSL.Connection(ssl_ctx)
tls_start.ssl_conn.set_tlsext_host_name(server.sni)
tls_start.ssl_conn.set_connect_state()

We likely won't put effort into perfectly mimicking clients until that becomes a more substantial problem in practice.

@c1b3rd0rk
Copy link

That's great to hear. I took a short look a the code a while ago. mitmproxy crafts its own hello. For mimicking would it maybe be possible to just "copy" the original client hello i.e. mitmproxy sends exactly the same data (except maybe the client random)?

Depending on where you are in the network the problem is already substantial. Attackers (and defenders) are already making use of this.
Intercepting proxies are actively targeted because all traffic is funneled through them. Once you pwn them you get all the traffic in clear. From your new beachhead you can now also start to attack the rest of the network. The only thing you need to do if you want to see if an intercepting proxy is in use is make the victim connect to your website. There are TLS fingerprint plugins available for apache and nginx. You then can see if this looks like a normal browser hello or it looks like a browser (from the user agent) but the TLS fingerprint is different. Or, if you have capabilities on a bigger level, just sniff all traffic on the WAN side.
It also makes tracking of specific users easier.
Alternatively you can specifically choose to not attack a client because an intercepting proxy is running. You increase the risk your neat malware will be saved in clear in some traffic dump.

There is only few well maintained software for intercepting proxies around, also some of the commercial ones just use squid. I don't know about the adoption of mitmproxy but it is certainly among those few. Better be sneaky and don't stick out of the crowd :)

https://github.com/fooinha/nginx-ssl-ja3
https://docs.opnsense.org/manual/how-tos/nginx_tls_fingerprints.html
https://docs.trafficserver.apache.org/en/latest/admin-guide/plugins/ja3_fingerprint.en.html
https://blogs.cisco.com/security/tls-fingerprinting-in-the-real-world
https://media.defense.gov/2019/Dec/16/2002225460/-1/-1/0/INFO%20SHEET%20%20MANAGING%20RISK%20FROM%20TRANSPORT%20LAYER%20SECURITY%20INSPECTION.PDF

@fedosgad
Copy link

Hello!
I have a need to configure TLS ciphersuites and extensions (including TLS 1.3) to match those of a client application (the one using proxy). I have a little experience writing mitmproxy addons. What can I use as a starting point/reference? A quick look over tlsconfig.py leads me to TODOs.
By the way, mitmproxy is an absolutely great tool! Thanks a lot for putting your efforts into it!

@mhils
Copy link
Member Author

mhils commented Feb 14, 2022

@fedosgad: In short, we use OpenSSL via cryptography/pyOpenSSL.

For the OpenSSL bits, see https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites and https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_ciphersuites.html. cryptography exposes the relevant bindings, but pyOpenSSL does not. pyca/pyopenssl#963 may help in figuring out how to do things.

@ghost
Copy link

ghost commented Nov 23, 2022

What does the support for TLS 1.3 cipher suites currently look like?

I seem to be able to set 1.3 in the mitmproxy config but I get weird behavior

@fuomag9
Copy link

fuomag9 commented Jul 4, 2023

Is there any progress on this? Support for TLS 1.3 ciphers would be great!

@mhils
Copy link
Member Author

mhils commented Jul 4, 2023

No progress on this as far as I'm aware. :)

@PATAPOsha
Copy link

Still no support for TLSv1.3 cipher suites?

@yoshimo
Copy link

yoshimo commented Feb 17, 2024

to blend in better and get to a point where you can just mirror the client you want to proxy, this would be helpful.
Lower TLS versions are slowly being phased out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/protocols kind/feature New features / enhancements
Projects
None yet
Development

No branches or pull requests

7 participants
@yoshimo @mhils @fuomag9 @fedosgad @PATAPOsha @c1b3rd0rk and others