Library / SDK
aapooksman/certmitm avatar
aapooksman/certmitm

certmitm: finding TLS clients that accept any certificate

A tool for testing for certificate validation vulnerabilities of TLS connections made by a client device or an application.

735 stars72 forksPythonMIT

At a glance

What is it?
certmitm is a Python tool that intercepts TLS connections from devices on a network and presents four different certificates, so you can see which clients validate certificates and which do not. It suits a tester checking the TLS behaviour of an embedded device or an app, and it has no business being pointed at a network you are not authorized to test.
Who is it for?
certmitm fits a security engineer who needs to answer a specific question about their own devices, namely which TLS clients actually validate the certificate chain and which accept a substituted one, and its four-certificate approach turns a vague trust concern into a per-client answer.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 127 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A question about trust, asked by presenting four different certificates

certmitm is described as a tool for testing for certificate validation vulnerabilities of TLS connections made by a client device or an application. The problem it addresses is narrow and real: plenty of embedded devices, IoT hardware, set-top boxes and older applications complete a TLS handshake without ever checking that the certificate chains to a trusted root.

The mechanism is elegant in a way that makes the tool's output easy to read. When a client connects, certmitm does not present one certificate. It presents four, in separate attempts, and records how the client responded to each. The variants are a self-signed certificate, a certificate with a replaced key, a real certificate such as a Let's Encrypt one served with a real CA, and a real certificate combined with a real CA. Any client that accepts one of the invalid combinations is vulnerable.

The results are reported at two levels. A connection that certmitm could intercept is printed as CRITICAL. With the verbose flag, properly secured connections appear as INFO instead, with the underlying TLS error the client produced, such as a tlsv1 alert unknown ca or a decrypt error. The contrast between the two lines is the finding: two different hosts, the same client, the same moment, and one of them rejected the certificate for a specific, nameable reason.

The project was created by Aapo Oksman and published at DEF CON 31 on August 11 2023, where the talk was described as automatic exploitation of TLS certificate validation vulnerabilities. The slides and a demo recording are linked from the project. The licence is MIT.

Routing the lab, redirecting port 443, and masquerading the rest

The interception is arranged at the network level rather than by asking clients to trust a proxy. The approach in the documentation is to configure the machine running certmitm to act as a router for the other devices on the network, which starts with a DHCP and DNS server:

bash
sudo ip addr add 10.0.0.1/24 dev eth0
sudo dnsmasq --no-daemon --interface eth0 --dhcp-range=10.0.0.100,10.0.0.200 --log-dhcp --log-queries --bind-interfaces -C /dev/null

Then the traffic is redirected so that TLS connections land on certmitm and everything else is forwarded to the internet through the wireless interface:

bash
sudo iptables -A INPUT -i eth0 -j ACCEPT
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 9900
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

The redirect target is port 9900, which is the port certmitm listens on. The masquerade rule on the outgoing interface is what lets devices reach the real internet, so a device under test can still resolve names, fetch updates and talk to real servers while its TLS connections are being inspected.

Finally the tool itself starts:

bash
python3 certmitm.py --listen 9900 --workdir testing --verbose --show-data

The workdir argument is where the intercepted material is written, and --show-data makes the tool print the data it managed to decrypt.

The whole arrangement is a lab. It needs root for the address, dnsmasq and iptables changes, and a second network interface, and the documentation notes that you might need to retry running the applications a couple of times while the tests fail, because a client that aborts the first handshake may not retry at all on the next try.

Reading the client-side error messages to identify the flaw

The documentation shows the same idea reproduced with curl on a Linux client, and the value is in the error text. A client that rejects a self-signed certificate because the issuer is unknown reports a problem getting the local issuer certificate. A client that rejects a certificate whose key was replaced reports an RSA padding check failure rather than a certificate error at all, which is a distinctive signature. A client that rejects a real certificate presented with the wrong name reports that no alternative certificate subject name matches the target host name.

Each of those messages points at a different missing check. An unknown issuer means the chain was not validated. A padding error means the handshake proceeded past the certificate check and failed later, which is a different and often more serious failure. A name mismatch means the host name check is missing while the chain check works.

The result is a per-connection diagnosis rather than a pass or fail. A client can fail one test and pass another, and the documentation's example makes that point by showing connections to one host properly secured while connections to another host on the same client were interceptable.

The curl examples also show what happens when validation is disabled entirely. With the insecure flag, a client that accepts anything proceeds to the application layer, and the requests end in an empty reply from server, since certmitm is not a real proxy for those sessions but a certificate test endpoint. The redirects that appear in the output show the client following a redirect to a second host, which is exactly the case where a client that validates the first certificate but not the second would leak data.

Logging TLS session secrets for traffic you already intercepted

Once a connection has been intercepted, the interesting question is what the plaintext carried. certmitm handles that with the standard TLS debugging mechanism, the SSLKEYLOGFILE environment variable:

bash
SSLKEYLOGFILE=sslkeylogfile.txt python3 certmitm.py --listen 9900 --workdir testing --verbose

The file written is the usual NSS key log format, and the documentation says it can be used to decrypt the captured TLS traffic in tools such as Wireshark, pointing at the setting under Edit, Preferences, Protocols, TLS, and the Pre-Master-Secret log filename field.

This is the part of the workflow that turns certmitm from a certificate checker into a full visibility tool. Without it, you know a client accepted a bad certificate but you cannot see the traffic. With it, you can read the intercepted session in a protocol analyser, which is how you confirm what the vulnerability actually exposes.

The dependency list is short, which is worth noting. requirements.txt pins pyOpenSSL below version 23.3.0 and adds dpkt, a packet manipulation library. That pyOpenSSL ceiling is a real constraint rather than a preference, and it tells you this code predates the current OpenSSL bindings and has not been moved forward. The repository is a single certmitm.py entry point plus a certmitm/ package, a real_certs/ directory for the certificates you supply, and the requirements file.

Where certmitm is the wrong tool

The first limitation is that this is not a general interception proxy. It answers a narrow question about certificate validation and it does that well, but it will not sit in the middle of a real browsing session and give you a readable log of everything a user does. The curl output shows the shape of the problem, with empty replies where a genuine proxy would have returned content. For traffic analysis you want a real intercepting proxy; for certificate validation you want certmitm.

The second is the environment it demands. Root privileges, dnsmasq, iptables NAT rules, a static address on an interface and a second interface for masquerading. That is a deliberately isolated lab setup, and it means certmitm is not something you can point at a corporate network or a shared office Wi-Fi without reorganising it around the tool. The documentation's own framing, configuring the computer to act as a router for other devices, is the boundary.

The third is the age of the code. There are no GitHub releases, so there is nothing to install and no version to pin; you use the source in the repository. The pyOpenSSL pin below 23.3.0 means you will be installing an old cryptography stack on a modern Python, and the project states it has been tested on Debian 11 and Debian 12 with Python 3.10 and up. Whether it runs cleanly on a current distribution is not something the documentation promises.

The fourth is the scope of the test. certmitm tells you that a client accepts a substituted certificate. It does not tell you what an attacker would do with that, it does not scan for other TLS problems such as weak protocol versions, and the results depend on real certificates being available in real_certs. Obtain real certificates such as a Let's Encrypt certificate and save them there, as the installation section instructs, or the real-certificate tests cannot run at all.

Editorial conclusion

certmitm fits a security engineer who needs to answer a specific question about their own devices, namely which TLS clients actually validate the certificate chain and which accept a substituted one, and its four-certificate approach turns a vague trust concern into a per-client answer. It is the wrong choice if you need a general traffic interception toolkit, a web application scanner, or a maintained package set, since the dependencies are pinned to an old pyOpenSSL and there are no published releases to install. Before running it, prepare the lab with a DHCP and DNS server and the iptables rules from the documentation, obtain real certificates such as a Let's Encrypt certificate and place them in real_certs, and confirm the target device is one you own or are authorized to test, since the last push to the repository was on 2026-06-18 and the project ships as source only.

Frequently asked questions

What does certmitm do during a TLS handshake?

It presents several different certificates in turn, including a self-signed one, one with a replaced key, and a real certificate served with a real CA. Connections it can intercept are reported as CRITICAL, while properly secured connections appear as INFO with the client's TLS error.

How do I install certmitm?

Install the Python dependencies with pip install -r requirements.txt, which pulls pyOpenSSL and dpkt, and obtain real certificates such as a Let's Encrypt certificate and save them to the real_certs directory. The project has been tested on Debian 11 and Debian 12 with Python 3.10 and up.

How do I capture the plaintext of an intercepted TLS connection?

Set the SSLKEYLOGFILE environment variable when starting certmitm, for example SSLKEYLOGFILE=sslkeylogfile.txt python3 certmitm.py --listen 9900 --workdir testing --verbose. The resulting file can be loaded into Wireshark as the TLS Pre-Master-Secret log.

Is certmitm limited to Linux?

The setup uses ip, dnsmasq and iptables, so it runs on Linux. The project says it should work on Linux with Python 3.10 and up, and it has been tested on Debian 11 and Debian 12.

What licence is certmitm released under and where was it presented?

MIT, created by Aapo Oksman. It was published at DEF CON 31 on August 11 2023, with the talk slides and a demo recording linked from the repository.

Official sources

  1. aapooksman/certmitm on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aapooksman-certmitm.svg)](https://hysenlabs.com/projects/aapooksman-certmitm)

Was this useful?