guidovranken / cryptofuzz

Fuzzing cryptographic libraries. Magic bug printer go brrrr.

Home Page:https://guidovranken.com/2019/05/14/differential-fuzzing-of-cryptographic-libraries/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bignum to Unsigned Long Conversion is Incorrect

marquitos0119 opened this issue · comments

While running cryptofuzz against openssl-3.0, I encountered an input that caused the program to crash. After further investigation, the 2 functions being called are in the ExpMod::Run class, specifically BN_mod_exp_mont_word and BN_mod_exp_mont_consttime.

Both of these functions take in 3 numbers, but BN_mod_exp_mont_consttime takes them in BIGNUM format, whereas BN_mod_exp_mont_word takes 1 unsigned long and 2 BIGNUMS:

int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
                         const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)

So, bn[0] gets converted to a ulong, and investigating with gdb shows that the conversion failed. In this case, bn[0] was 0000000000000013536853764361904580 and the output ulong is 14150647586910952635.

Later, during the result comparison, cryptofuzz fails as the 2 results are different. And in fact they are, since one input value varied between the 2 operations.

If I just trim the leading zero's and use 13536853764361904580, I get the same result as in BN_mod_exp_mont_consttime. Maybe the leading zero's could be trimmed before the BIGNUM->ulong conversion?

Which platform is this, s390x?

It's probably due to

/* Manual reversing is required because
* BN_bn2lebinpad is not supported by BoringSSL.
*
* TODO This must be omitted on big-endian platforms.
*/
v =
((v & 0xFF00000000000000) >> 56) |
((v & 0x00FF000000000000) >> 40) |
((v & 0x0000FF0000000000) >> 24) |
((v & 0x000000FF00000000) >> 8) |
((v & 0x00000000FF000000) << 8) |
((v & 0x0000000000FF0000) << 24) |
((v & 0x000000000000FF00) << 40) |
((v & 0x00000000000000FF) << 56);

13536853764361904580 is 14150647586910952635 backwards (in hex). Can you try replacing it with something like

#if (defined(__s390__) || defined(__s390x__) || defined(__zarch__))
                        /* Manual reversing is required because
                         * BN_bn2lebinpad is not supported by BoringSSL.
                         *
                         * TODO This must be omitted on big-endian platforms.
                         */
                        v =
                            ((v & 0xFF00000000000000) >> 56) |
                            ((v & 0x00FF000000000000) >> 40) |
                            ((v & 0x0000FF0000000000) >> 24) |
                            ((v & 0x000000FF00000000) >>  8) |
                            ((v & 0x00000000FF000000) <<  8) |
                            ((v & 0x0000000000FF0000) << 24) |
                            ((v & 0x000000000000FF00) << 40) |
                            ((v & 0x00000000000000FF) << 56);
#endif

And see if that works? The zero padding shouldn't matter.

Ah that seems to be exactly the issue. Let me try that out

That was easy enough. Submitted PR #40 to close this.

Since you are testing on s390x, I have some really large corpora for bignum, like one with ~750K ExpMod inputs. I can send you those if you want?

Sure, there's definitely plenty of space for it :)

Ok, I'll e-mail you at your IBM address, ok?

Sounds good. Thanks

Done, let me know if you run into any other issues.

Manual reversing is required because BN_bn2lebinpad is not supported by BoringSSL.

Would it be helpful if I added BN_bn2lebinpad to BoringSSL? Although BN_get_u64 or BN_get_word might be what you actually want here.

Thanks @davidben , i'll try BN_get_u64.