Auditing code for crypto flaws: the first 30 minutes

Auditing your code for proper crypto use is extremely important.  However, what if it’s not generally your focus?  If your job today is to find flaws in the cryptographic components of application א, where should you start?

I like to start an audit by checking which crypto primitives are used. This often gives you an idea of the quality of the crypto and can reveal exploitable flaws just by looking at one line of code.

The simplest way to identify weak crypto primitives is to look for their names in the code. For example, this one-liner script that I published a while back, partially as a joke, will find some of the most obvious primitives to be avoided:

#!/bin/sh
exec rg -i -e 'md2|md4|md5|sha1|rc2|rc4|mersenne|mt19937|ecb|3des|getpid|srand|oee|crc|rot13' $1

You can see this as a crypto equivalent of tools like flawfinder, which attempt to find flaws from pattern-based rules.

Of course, such a dumb recursive grep can get you tons of false positives, and is by no means comprehensive—it won’t catch DES, for example—but it will help you spot obvious errors without manual inspection of the code base. And it’s fast, thanks to ripgrep (which will skip binaries, ignore hidden and gitignored files). For example, I could process half a gigabyte worth of git repositories in about one second.

So what should you look for in these first 30 minutes of audit? Common examples are:

  • CRCs or other weak hashes used in place of cryptographic hashing
  • Non-crypto PRNGs, such as Mersenne Twister or srand()/rand()
  • MD5 or SHA-1 used where collision resistance is expected (as signature schemes in public-key certificates, for binary blobs signatures in bootloaders, etc.)
  • Block ciphers in ECB mode, or CBC mode with predictable IV, or CTR mode with repeated nonces
  • Legacy ciphers like DES, TripleDES, or RC4
  • Weak password hashing (mere cryptographic hash, or PBKDF2 with too few iterations)
  • Hash functions used where a MAC or PRF is expected

These are all beginner mistakes, but generally many developers don’t know anything about crypto and will just do their best with the help of Google and Stack Overflow.

If everything looks good so far and you didn’t spot any obvious flaws, there are still a million ways your crypto can fail, and you’ve got to ask questions such as:

  • Do the chosen protocols and primitives provide the security required by your threat model? (Maybe the protocol is secure against passive attackers while you’re dealing with active attackers)
  • Are there parts of the application where cryptography is missing? Parts where it’s not needed or redundant? (can happen too)
  • How consistent are the security levels of the various protocols and primitives? (using AES with a 256-bit rather than 128-bit key is pointless if the key is encrypted by 1024-bit RSA)
  • Are the crypto schemes implemented in a way that is compliant with their official specifications? (probably not)
  • Is everything that should be randomized properly randomized? (RSA-OAEP, ECDSA, etc.)
  • How fast is the crypto? If an algorithm is particularly slow, can it be exploited to DoS the application?
  • If the operating system’s PRNG is used, how reliable is it on the platforms where the application runs?
  • Are any third-party components/libraries used? How secure are these? Is the application using their latest version?
  • Are invalid input values detected? Are return values checked for errors?
  • Are secret values left in memory after the application is executed?

And that’s just the few things I can think of in a the few minutes left I have to write this post. The bottom line is that reviewing cryptographic software is much more than checking what algorithms are used.

One comment

Leave a Reply