VYPR
Low severityOSV Advisory· Published Jan 5, 2026· Updated Jan 6, 2026

badkeys vulnerable to ASCII control character injection on console via malformed input

CVE-2026-21439

Description

badkeys is a tool and library for checking cryptographic public keys for known vulnerabilities. In versions 0.0.15 and below, an attacker may inject content with ASCII control characters like vertical tabs, ANSI escape sequences, etc., that can create misleading output of the badkeys command-line tool. This impacts scanning DKIM keys (both --dkim and --dkim-dns), SSH keys (--ssh-lines mode), and filenames in various modes. This issue is fixed in version 0.0.16.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
badkeysPyPI
< 0.0.160.0.16

Affected products

1

Patches

2
635a2f3b1b50

Escape output (filenames, SSH key comments, etc.) that can contain control characters

https://github.com/badkeys/badkeysHanno BöckJan 2, 2026via ghsa
2 files changed · +15 11
  • badkeys/runcli.py+11 11 modified
    @@ -14,7 +14,7 @@
     from .scanssh import scanssh
     from .scantls import scantls
     from .update import update_bl
    -from .utils import _errexit, _getret, _setret, _warnmsg
    +from .utils import _errexit, _esc, _getret, _setret, _warnmsg
     
     MAXINPUTSIZE = 2048000
     
    @@ -46,19 +46,19 @@ def _printresults(key, where, args):
         if "curve" in key:
             kn += f"[{key['curve']}]"
         if key["type"] == "unsupported":
    -        _warnmsg(f"Unsupported key type, {where}")
    +        _warnmsg(f"Unsupported key type, {_esc(where)}")
         elif key["type"] == "unparseable":
    -        _warnmsg(f"Unparseable input, {where}")
    +        _warnmsg(f"Unparseable input, {_esc(where)}")
         elif key["type"] == "notfound":
    -        _warnmsg(f"No key found, {where}")
    +        _warnmsg(f"No key found, {_esc(where)}")
         elif args.verbose or args.all:
             if key["results"] == {}:
    -            print(f"{kn} key ok, {where}")
    +            print(f"{kn} key ok, {_esc(where)}")
         for check, result in key["results"].items():
             sub = ""
             if "subtest" in result:
                 sub = f"/{result['subtest']}"
    -        print(f"{check}{sub} vulnerability, {kn}, {where}")
    +        print(f"{check}{sub} vulnerability, {kn}, {_esc(where)}")
             _setret(4)
             if args.url and "lookup" in result:
                 url, _ = urllookup(result["blid"], result["lookup"])
    @@ -222,7 +222,7 @@ def runcli():
                 try:
                     records = dns.resolver.resolve(host, "TXT").response
                 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
    -                _warnmsg(f"No TXT record found, {host}")
    +                _warnmsg(f"No TXT record found, {_esc(host)}")
                     continue
                 found = False
                 for record in records.answer[-1]:
    @@ -233,15 +233,15 @@ def runcli():
                         _printresults(r, host, args)
                         found = True
                 if not found:
    -                _warnmsg(f"No DKIM/DomainKeys key in TXT record, {host}")
    +                _warnmsg(f"No DKIM/DomainKeys key in TXT record, {_esc(host)}")
     
         if args.jwk:
             for fn in args.infiles:
                 with open(fn) as f:
                     try:
                         j = json.load(f)
                     except (json.decoder.JSONDecodeError, UnicodeDecodeError):
    -                    _warnmsg(f"No valid JSON, {fn}")
    +                    _warnmsg(f"No valid JSON, {_esc(fn)}")
                         continue
                 if isinstance(j, dict) and "kty" in j:
                     r = checkjwk(j, checks=userchecks)
    @@ -251,7 +251,7 @@ def runcli():
                         r = checkjwk(k, checks=userchecks)
                         _printresults(r, fn, args)
                 else:
    -                _warnmsg(f"No JWK/JWKS, {fn}")
    +                _warnmsg(f"No JWK/JWKS, {_esc(fn)}")
     
         if args.ssh or args.tls or args.dkim_dns or args.jwk:
             sys.exit(_getret())
    @@ -317,7 +317,7 @@ def runcli():
     
                 keyrecs = dnskeyre.findall(fcontent)
                 if not keyrecs:
    -                _warnmsg(f"No DNSSEC key found, {fn}")
    +                _warnmsg(f"No DNSSEC key found, {_esc(fn)}")
                 for rec in keyrecs:
                     r = checkdnskey(rec, checks=userchecks)
                     _printresults(r, fn, args)
    
  • badkeys/utils.py+4 0 modified
    @@ -5,6 +5,10 @@
     _retval = 0
     
     
    +def _esc(inp):
    +    return repr(inp)[1:-1]
    +
    +
     @functools.cache
     def _setret(rv):
         global _retval
    
de631f69f040

DKIM: Don't display value in unknown key type warning

https://github.com/badkeys/badkeysHanno BöckJan 2, 2026via ghsa
1 file changed · +1 1
  • badkeys/dkim.py+1 1 modified
    @@ -45,5 +45,5 @@ def parsedkim(line):
                 return False
             der = EDASN1 + rawed
             return PUBPRE + base64.b64encode(der).decode() + PUBPOST
    -    _warnmsg(f"Unknown DKIM key type {dkim['k']}")
    +    _warnmsg("Unknown DKIM key type")
         return False
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.