badkeys - Finding Insecure Public Keys

https:/badkeys.info/

Hanno Böck

badkeys NLnet NGI0

Public Key Cryptography

RSA, ECDSA, Ed25519, ML-DSA

badkeys

Tool to identify public keys with known vulnerabilities


-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDiVjMo36v2gYhga5Ey
QoHB1YpEVkMbCdUQs1/syfMHyhgihG+iZxNxqagbrA41dJ2hZANiAARbCQG4hSMp
brkZ1Q/6GpyzdLxNQJWGKCv+yhGx2VrbtUc0r1cL+CtyKM8ia89MJd28/jsaOtOU
MO/3Y+HWjS4VHZFyC3eVtY2ms0Y5YTqPubWo2kjGdHEX+ZGehCTzfsg=
-----END PRIVATE KEY-----

This private key is not secure.

Can you see why?

Public Private Keys

Private key on Github Private Key on PasteBin

Current work on badkeys

1. Find more Public Private Keys

2. Check Public Keys with badkeys (TLS certificates, DNSSEC, DKIM, ...)

NLnet NGI0

Supported by NLnet/NGI0

Where to find Public Private Keys?

  • Source Code (tests, ...)
  • The World Wide Web (documentation, blog posts, ...)
  • Firmware (many hardcoded keys!)
  • ...

Private Keys come in different shapes and forms


k = "-----BEGIN EC PRIVATE KEY-----\n" +
    "MHcCAQEEIObLW92AqkWunJXowVR2Z5/+yVPBaFHnEedDk5WJxk/BoAoGCCqGSM49\n" +
    "AwEHoUQDQgAEQiVI+I+3gv+17KN0RFLHKh5Vj71vc75eSOkyMsxFxbFsTNEMTLjV\n" +
    "uKFxOelIgsiZJXKZNCX0FBmrfpCkKklCcg==\n" +
    "-----END EC PRIVATE KEY-----\n";


Private-key-format: v1.2
Algorithm:       8 (RSASHA256)
Modulus:         wVwaxrHF2CK64aYKRUibLiH30KpPuPBjel7E8ZydQW1HYWHfoGm
                 idzC2RnhwCC293hCzw+TFR2nqn8OVSY5t2Q==
PublicExponent:  AQAB
PrivateExponent: UR44xX6zB3eaeyvTRzmskHADrPCmPWnr8dxsNwiDGHzrMKLN+i/
                 HAam+97HxIKVWNDH2ba9Mf1SA8xu9dcHZAQ==
Prime1:          4c8IvFu1AVXGWeFLLFh5vs7fbdzdC6U82fduE6KkSWk=
Prime2:          2zZpBE8ZXVnL74QjG4zINlDfH+EOEtjJJ3RtaYDugvE=
Exponent1:       G2xAPFfK0KGxGANDVNxd1K1c9wOmmJ51mGbzKFFNMFk=
Exponent2:       GYxP1Pa7CAwtHm8SAGX594qZVofOMhgd6YFCNyeVpKE=
Coefficient:     icQdNRjlZGPmuJm2TIadubcO8X7V4y07aVhX464tx8Q=

keyfinder

https://github.com/badkeys/keyfinder

GitHub Secret Scanning

If you try to upload a private key to GitHub that belongs to a GitHub account, push will be blocked and it will tell you

GitHub Secret Scanning

You want to know to which user it belongs?

ssh -i [privatekey] git@github.com

Hi [username]! You've successfully authenticated, but GitHub does not provide shell access.

DNSSEC

DNSSEC: Not many findings

2 hosts using example key from RFC

DKIM

Key Formats are hard, DKIM edition


v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAplysJ1bZ2xrWbnLwLr0yRijIeLSKIkmHKIRFYoDc4omsQOGU7yv6xxpRfjiUDoTTO2E4InayR68pWGSU0lPv8Q7fBVE/UcH5JfP7IJGsEsPTCUMxZY2M9e7Kg7J1LrPNpiV8P6xcGyDHA1RyGMUwFS86rjzXK//9o+8szgzSTY7X7cio/J+a/5bKaVF0vva9YNS+WHI6LxnYzHZQbFGZNIZnAlKPd+LHnbwfYKU20QgZKeHguGOi4VMb77wUFlXrRjEafmKQlfsAyyKrkzs42nYk/UuxRZcqadJAEo87umgeQPjbyc64LazMTcQOSBqTBX59CaF0DBP3RggmN0CpdwIDAQAB

RFC 6376 (DKIM spec)


The "rsa" key type
indicates that an ASN.1 DER-encoded [ITU-X660-1997] RSAPublicKey
(see [RFC3447], Sections 3.1 and A.1.1) is being used in the "p="
tag.

RFC 6376 (DKIM spec)


   To extract the public-key component from the private key, use openssl
   like this:

   $ openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM
[...]
   $ORIGIN _domainkey.example.org.
   brisbane IN  TXT  ("v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ"
                      "KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt"
                      "IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v"
                      "/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi"
                      "tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB")

DKIM spec says PKCS #1, but uses SPKI in example

In practice, everyone uses SPKI

Scanning DKIM keys

To get DKIM keys, you need a "selector"

selector._domainkey.example.com

Get DKIM selectors for domains

Option 1: Scrape from existing emails (public mailing list archives)

Option 2: Bruteforce common selectors (key, dkim, ...)

CVE ID:     CVE-2016-9963
Date:       2016-12-15
Credits:    Bjoern Jacke <bjoern@j3e.de>
Version(s): 4.69 -> 4.87
Issue:      If several conditions are met, Exim leaks private information
            to a remote attacker.

https://www.exim.org/static/doc/CVE-2016-9963.txt

Indication
==========

You can check if you where affected already. The mainlog entries look like this:

2016-12-17 09:44:33 10HmaX-0005vi-00 ** baduser@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: PRDR error after -----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDXRFf+VhT+lCgFhhSkinZKcFNeRzjYdW8vT29Rbb3NadvTFwAd\n+cVLPFwZL8H5tUD/7JbUPqNTCPxmpgIL+V5T4tEZMorHatvvUM2qfcpQ45IfsZ+Y\ndhbIiAslHCpy4xNxIR3zylgqRUF4+Dtsaqy3a5LhwMiKCLrnzhXk1F1hxwIDAQAB\nAoGAZPokJKQQmRK6a0zn5f8lWemy0airG66KhzDF0Pafb/nWKgDCB02gpJgdw5rJ\nbO7/HI3IeqsfRdYTP7tjfmZtPiPo1mnF7D1rSRspZjOF2yXY/ky7t7c5xChRcSxf\n+69CknwjrfteY9Aj0j6o7N+2w2uvHO+AAq8BHDgXKmPo0SECQQDzQ/glyhNH9tlO\nx+3TTMwwyZUf2mYYosN3Q9NIl3Umz/3+13K5b6Ed6fZvS/XwU55Qf5IBUVj2Fujk\nRv2lbGPpAkEA4okpnzYz5nm1X5WjpJPQPyo8nGEU1A5QfoDbkAvWYvVoYrpWPOx5\nHFpOAHkvSk1Y1vhCUa+zHwiQRBC8OMp6LwJBAOAUK/AjQ792UpWO9DM++pe2F/dP\nZdwrkYG6qFSlrvQhgwXLz5GgkfjMGoRKpDDL1XixCfzMwfVtBPnBqsNGJIECQGYX\nSIGu7L7edMXJ60C9OKluwHf9LGTQuqf4LHsDSq+4Rz3PGhREwePsMqD1/EDxEKt4\noHKtyvyeYF28aQbzARMCQQCRtJlR6vlKhxYL8+xoPrCu3MijKgVruRUcNstXkDZK\nfKQax6vhiMq+0qIiEwLA1wavyLVKZ7Mfag+/4NTcDUVC\n-----END RSA PRIVATE KEY-----\n: 550 PRDR R=<baduser@test.ex> refusal

That key was used in production until 2025

Anyone remembers CVE-2008-0166?

Also known as the Debian OpenSSL bug

DKIM scan in early 2024 (Tranco Top 1 Million)

Around 350,000 TXT records with a valid RSA key.

855 vulnerable to Debian OpenSSL bug (0.24%).

Domains with vulnerable keys

@cisco.com, @oracle.com, @skype.net, @github.partners, @partner.crowdstrike.com, @partners.dropbox.com, @1password.com, @seznam.cz

Why?

  • 2006: Debian OpenSSL bug was introduced
  • 2007: DKIM was published (RFC 4870)
  • 2008: Debian OpenSSL bug was found

Gmail with email logos

https://16years.secvuln.info/

More details in talk at MiniDebConf 2024

Fortinet / FortiGate

CVE-2022-40684

Authentication Bypass in FortiGate devices

It was known that this was actively exploited in 2022

January 2025

~15.000 configuration files from FortiGate devices published on BreachForums

These configuration files contain private keys, but they are encrypted with a password

The configuration files also contain the password, but the password is also encrypted with another password

The password to decrypt the key password is "Mary had a littl"

FortiGate (2025)

Private keys for ~100 still valid TLS certificates and ~300 ACME accounts for Let's Encrypt.

Remember: incident was known since 2022!

"Enterprise Security"

OpenID Connect

https://example.com/.well-known/openid-configuration

JSON Web Key Public


{
"kty": "EC",
"crv": "P-256",
"x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
}

JSON Web Key Private


{
"kty": "EC",
"crv": "P-256",
"x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"
}

JSON Web Keys have the peculiar property that the format for public and private keys is almost identical

Public key? Private key?

Nobody would confuse them, right?

Scan result OpenID Connect setups

  • Private keys instead of public keys
  • Keys that were examples from OpenID Connect libraries
  • 512 bit RSA keys

Thanks for listening!

Use badkeys!

badkeys NLnet NGI0

Sponsoring? Consulting? Contact me!

badkeys.info