Many RSA Signatures May Be Forgeable In OpenSSL and Elsewhere

Thomas Ptacek | September 7th, 2006 | Filed Under: New Findings

Bell Labs crypto shaolin Daniel Bleichenbacher disclosed a freaky attack on RSA signature implementations that may, under some common circumstances, break SSL/TLS.

Do I have your attention? Good, because this involves PKCS padding, which is not exciting. Bear with me.

RSA signatures use a public signing keypair with a message digest (like SHA1) to stamp a document or protocol message. In RSA, signing and verification are analogs of decryption and encryption. To sign a message, you:

  1. Hash it

  2. Expand the (short) hash to the (long) size of the RSA modulus

  3. Convert that expansion to a number

  4. RSA decrypt the number

  5. Convert that to a series of bytes, which is your signature.

It’s step 2 we care about here. It’s called “padding”. You need it because RSA wants to encrypt or decrypt something that is the same size as the RSA key modulus.

There are a bunch of different ways to pad data before operating on it, but the one everyone uses is called PKCS#1 v1.5 (technically, EMSA-PKCS1-V1_5-ENCODE). It involves tacking a bunch of data in front of your hash, enough to pad it out to the size of the modulus:

pkcs-s.png

Note the order and placement of those boxes. They’re all variable length. Let’s call that out:

pkcs-b.png

The two big goals of a padding scheme are (a) expanding messages to the modulus length and (b) making it easy to correctly recover the message bytes later, regardless of what they are. This padding scheme is designed to make that straightforward. The padding bytes are clearly marked (“00 01”, which tells you that PKCS#1 v1.5 is in use), terminated (with a 00, which cannot occur in the padding), and followed by a blob of data with a length field. This whole bundle of data is what RSA works on.

The problem is, despite all the flexiblity PKCS#1 v1.5 gives you, nobody expects you to ever use any of it. In fact, a lot of software apparently depends on data being laid out basically like the picture above. But all the metadata in that message gives you other options. For instance:

pkcs-a.png

For some RSA implementations, this could be an acceptable message layout. It’s semantically invalid, but can appear syntactically valid. And if you don’t completely unpack and check all the metadata in the signature, well, this can happen:

  1. Determine the padding from the fixed header bytes.

  2. Scan until the terminator.

  3. Scoop out the hash information.

  4. Use the hash to confirm you’re looking at the same message that the “signer” signed.

  5. Use the signature to confirm that a real “signer” signed it.

The problem is that this attack breaks the connection between (4) and (5). The hash, now “centered” instead of “right-justified”, doesn’t really mean anything, because the signature covers a bunch more bits.

This is all trivia without some value for “evil” that lets you substitute an arbitrary message hash (and thus an arbitrary message) into an otherwise valid-looking signature. Enter Bleichenbacher’s attack.

RSA takes parameters, one of which is a “public exponent”, which is part of the public key. If that exponent is “3”, which it often is, an attacker can exploit broken signature validation code to forge messages. The math here, which Bleichenbacher claims is simple enough to do with a pencil and paper, gets a bit hairy for me (I lose it at polynomials). Dino explains it better than I do. The long and the short of it is, you validate an RSA signature by computing:

s ^ e = m (mod n)

(where “e” is the public exponent, “n” is the public modulus, and “s” is the signature) and verifying that you get the same result as applying steps (1) and (2) from the signature process yourself. But:

  1. If the public exponent is “3”, and

  2. you inject the right “evil” bits into the PKCS data to make it a perfect cube, then

  3. you can create a something that broken RSA will validate.

Good technical details in Hal Finney’s OpenPGP post (OpenPGP is not vulnerable). And a security advisory for OpenSSL (OpenSSL is vulnerable, through 0.9.8b).

Two things have gone wrong here:

  1. Implementations misparse signatures, assuming that a syntactically valid-looking hash is semantically operative.

  2. For the common RSA exponent parameter “3”, there’s a straightforward way to manipulate a bogus signature to make it look valid.

My understanding is, the demonstrated attack sticks the evil bits outside of the DigestInfo (the hash data). The way I see the bug being described, broken implementations are just scanning the buffer without “fully decoding it”, implying that if they just validated the ASN.1 metadata against the signature buffer they’d be OK. That may be true, but it seems possible that a blind “memcmp” on an over-long SHA1 octet string, which would be ASN.1-valid and leave the Digest at the end of the buffer, could also trigger the same bug.

This is only a problem if:

  1. You’re running broken code

  2. You’re relying on certificate validation

  3. The certificates you’re validating use an exponent of “3”

Unfortunately, although the default for OpenSSL is “65537” (no practical attack known for that), “3” is common alternative: it’s faster, especially in embedded environments. Ferguson and Schneier recommend it in Practical Cryptography. Checking the CA bundle for “curl”:

cat curl-ca-bundle.crt | grep Exponent | grep ": 3" | wc -l

gives 6 hits, from Digital Signature Trust Co. and Entrust.net. Those same certs are in Firefox. Firefox doesn’t use OpenSSL; it uses NSS, and I don’t know if NSS is vulnerable. Here’s the code. I see it extracting a SHA1 hash’s worth of data, and don’t see it checking to make sure that date exhausts the buffer, but I don’t know the code well. Safari also depends on OpenSSL.

Correct me on any of these points and I’ll amend the post. I haven’t tested this (although Google Security did a proof of concept against OpenSSL that precipitated the advisory).

You should upgrade to the most recent OpenSSL. Bleichenbacher also recommended people stop using signing keys with a “3” exponent.

15 Comments so far

  • anonymous

    September 8th, 2006 2:14 am

    who or what is Google Security?

  • Nate

    September 8th, 2006 12:22 pm

    The math is more straightforward if you think about it differently than most people present it. Instead of:
    m = s^e mod n

    Think:
    m = s * s * s - k * n

    This iterative process is actually how modular exponentiation is implemented in computers. Each loop, you multiply by s and occasionally subtract off an n, based on the value of some bits of s. “k” is just an unimportant constant (how many times you subtract off n).

    So, the question is “how big is ’s’?” Normally, size_bits(s) = size_bits(n), i.e. 2048 commonly. This means that every time you multiply by “s”, your accumulator wraps around quite a few times. This makes it hard for an attacker to guess how many times it wrapped, just by observing the final result (discrete log problem). Strict enforcement of padding is intended to keep this property.

    But Bleichenbacher’s attack (and a similar one we found previously in a shipping product) involves making “s” much smaller than n. If you can make size_bits(s)

  • Nate

    September 8th, 2006 12:27 pm

    Uh, great blog comment system.

    … size_bits(s)

  • Nate

    September 8th, 2006 12:30 pm

    … LESS THAN OR EQUAL TO size_bits(n) / 3, means the exponentiation won’t wrap. This means it’s easy to find a value, s_evil, where the cube root produces a value that matches SHA1(evil).

  • Dave G.

    September 8th, 2006 1:44 pm

    Nate:

    I like that you can simultaneously break crypto and not be able to use a web application with one button :) This is why you need to stop using your c64 for web browsing.

    Much love,

    Dave G.

  • anonymous former SunSoft/IR Pentester

    September 9th, 2006 1:20 am

    Jesus “tapdancing” Christ!!!(expletive deleted)

    and thanx

    nuff said

  • Nicko

    September 11th, 2006 6:55 am

    … Safari also depends on OpenSSL

    Actually Safari uses Apple’s “Security Framework”, which is based on the OpenGroup CDSA codebase. Apple’s code does not have the above bug; it makes use of the fact that PKCS#1 signatures are deterministic, constructs the correct value for the cleartext of the signature and check that this is exactly the same as the decryption of the signature.

  • marius

    September 12th, 2006 8:47 pm

    Google employs security enabled engineers in various teams. They like mucking around with this stuff.

    There’s more subtle flavors too:
    padding|asn1((HASHALGO_ID,random[xx]),hash), so the noise to make for a nice cube is hidden inside an ignored, large parameter. Rinse, repeat. Many variants, different implementations fail different ways.
    If you care about others’ code verifying your signatures less error prone, don’t use 3. And don’t delegate powers to keys that use 3 either ;-)

  • sour grep:s

    September 16th, 2006 4:05 am

    cat curl-ca-bundle.crt | grep Exponent | grep “: 3″ | wc -l

    !?!!??

    grep -c “Exponent: 3″ curl-ca-bundle.crt

  • Thomas Ptacek

    September 16th, 2006 11:32 am

    Yeah, yeah, yeah.

  • Thomas Ptacek

    September 16th, 2006 11:33 am

    Also: grep -c? Gross.

  • jan

    November 15th, 2006 10:59 am

    Hi Guys, please email this solution to me quickly. thanks…that is today please, esperate
    Suppose Fred sees your RSA signaure on m1 and m2, (i.e., he sees (m1d mod n) and (m2d mod n)).
    How does he compute the signature on each of m1j mod n (for positive integer j), m1-1 mod n, m1 x m2 mod n, and
    in general m1j m2k mod n (for arbitrary j and k)?

  • Hiba

    April 18th, 2007 2:52 pm

    Elobrate more in follwing please:

    Suppose Fred sees your RSA signaure on m1 and m2, (i.e., he sees (m1d mod n) and (m2d mod n)).
    How does he compute the signature on each of m1j mod n (for positive integer j), m1-1 mod n, m1 x m2 mod n, and
    in general m1j m2k mod n (for arbitrary j and k)?

  • Thomas Ptacek

    April 19th, 2007 12:15 am
  • swapniel

    October 23rd, 2007 12:43 pm

    Suppose Fred sees your RSA signature on m1 and m2 (i.e. he sees m1d mod m and m2d mod n). How does he compute the signature on each of m1j mod n
    (Positive integer j), m1-1 mod n, m1.m2 mod n, and in general m1j.m2k mod n (for arbitrary integers j and k)?
    need the solution to this question plzzz

  • Leave a reply