Exploitable heap overflow in libgcrypt 1.9.0 (┛ಠ_ಠ)┛彡┻━┻
It's the crypto library that gpg uses. Homebrew has 1.9.0 right now.
https://dev.gnupg.org/T5259
It's the crypto library that gpg uses. Homebrew has 1.9.0 right now.

https://dev.gnupg.org/T5259
It's in cipher/hash-common.c (!) and looks like it's hit when extra data is hashes after finalizing the hash.
Doing that is a partial mitigation for timing side channels on the length of a hashed message. Very partial.
https://dev.gnupg.org/rC512c0c75276949f13b6373b5c04f7065af750b08
Doing that is a partial mitigation for timing side channels on the length of a hashed message. Very partial.
https://dev.gnupg.org/rC512c0c75276949f13b6373b5c04f7065af750b08
Right, so there are function pointers and stuff, but this is in sha256_final.
If there's no space in the block for the length suffix, it uses space from the 128 byte buf to fit another one.
Then it leaves count, the buffered data length, at 120.
No idea why it sets count.
If there's no space in the block for the length suffix, it uses space from the 128 byte buf to fit another one.
Then it leaves count, the buffered data length, at 120.
No idea why it sets count.
But then! md_write might still be called in a very mild attempt at mitigating timing side channels.
blocksize is 64, (blocksize - hd->count) underflows, the if doesn't hit, so buf_cpy copies the whole input to buf.
If it's more than 8 bytes, heap overflow.
blocksize is 64, (blocksize - hd->count) underflows, the if doesn't hit, so buf_cpy copies the whole input to buf.
If it's more than 8 bytes, heap overflow.
What went wrong? Of course, in primis, it's C and memory unsafety.
Arithmetic errors happen. In C they instantly become exploitable vulnerabilities. In a hash function!!
There's also a lot of reliance on distant state invariants, which is unsafe even by C standards.
Arithmetic errors happen. In C they instantly become exploitable vulnerabilities. In a hash function!!
There's also a lot of reliance on distant state invariants, which is unsafe even by C standards.
A defensive strategy here would be to use a copy function that is aware of the constant size of buf.
Instead the patch just adjusts the invariance ad-hoc in md_final.
I still don't understand why md_write sets count to 120. It wrote two blocks, reasonable values are 0 and 128.
Instead the patch just adjusts the invariance ad-hoc in md_final.
I still don't understand why md_write sets count to 120. It wrote two blocks, reasonable values are 0 and 128.
Another fun angle is that this vulnerability is introduced by timing side channel mitigations.
Previously: https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/
Not good ones, though. The suffix is always written at the beginning of a block, and md_final is not constant time.
Previously: https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/
Not good ones, though. The suffix is always written at the beginning of a block, and md_final is not constant time.
I applied the same mitigation to crypto/tls in 2016. (It was my first contribution to the Go crypto libraries!
)
Notice how the md_final equivalent is constant time, and leaves state unmodified. Not perfect, but the number of compressions is constant.
https://golang.org/cl/18130

Notice how the md_final equivalent is constant time, and leaves state unmodified. Not perfect, but the number of compressions is constant.
https://golang.org/cl/18130
I am physically uncomfortable with how state is handled in there.
There is clearly an implicit assumption that (hd->count <= blocksize), but md_write can still leave count at 120, after flushing the buffer.
Why not set it to zero then! Why do it in md_final!
There is clearly an implicit assumption that (hd->count <= blocksize), but md_write can still leave count at 120, after flushing the buffer.
Why not set it to zero then! Why do it in md_final!

Now the question is what code uses md_write after md_final.
Does gpg??
Taking a break to PR libgcrypt 1.9.1 into Homebrew because no one did that yet. Back soon.
Maybe this should have been a http://filippo.io/newsletter issue. Maybe it should still be.
Does gpg??
Taking a break to PR libgcrypt 1.9.1 into Homebrew because no one did that yet. Back soon.
Maybe this should have been a http://filippo.io/newsletter issue. Maybe it should still be.
https://github.com/Homebrew/homebrew-core/pull/69980
Now, breakfast. Then we look at GnuPG code.
It's a fun friday...
Now, breakfast. Then we look at GnuPG code.
It's a fun friday...
"Just decrypting some data can overflow a heap buffer with attacker controlled data, no verification or signature is validated before the vulnerability occurs." — @taviso
https://lists.gnupg.org/pipermail/gnupg-announce/2021q1/000456.html
https://lists.gnupg.org/pipermail/gnupg-announce/2021q1/000456.html
Oh you've got to be kidding me. The fixed version, libgcrypt 1.9.1, breaks the build on Intel CPUs because of unrelated changes.
This is why Go security releases branch and ship ONLY security fixes.
https://github.com/Homebrew/homebrew-core/pull/69980/checks?check_run_id=1791710860#step:7:720
This is why Go security releases branch and ship ONLY security fixes.
https://github.com/Homebrew/homebrew-core/pull/69980/checks?check_run_id=1791710860#step:7:720
--disable-asm builds of the fixed version are just broken on x86_64.
The breakage was introduced by a refactor that made regular code depend on a #define only set when asm is enabled.
The commit includes some RDRAND changes, 'cause why not.
https://dev.gnupg.org/rC8d404a629167d67ed56e45de3e65d1e0b7cdeb24
The breakage was introduced by a refactor that made regular code depend on a #define only set when asm is enabled.
The commit includes some RDRAND changes, 'cause why not.
https://dev.gnupg.org/rC8d404a629167d67ed56e45de3e65d1e0b7cdeb24
Alright, I dropped a patch for --disable-asm builds of libgcrypt 1.9.1 in the Homebrew PR. It's trivial enough.
You might also consider reverting to 1.8.7. https://github.com/Homebrew/homebrew-core/pull/69980/files#diff-0cce95e54eeccaf6db215314be717498f5a436aa23e8df547d12749d7c7feca4R57-R98
You might also consider reverting to 1.8.7. https://github.com/Homebrew/homebrew-core/pull/69980/files#diff-0cce95e54eeccaf6db215314be717498f5a436aa23e8df547d12749d7c7feca4R57-R98
Jesus Christ. /cc @hanno
It goes on. @hanno points out that libgcrypt and GnuPG don't seem to have CI, suggests running tests with ASAN. This is the reply.
(Indeed, libgcrypt 1.9.0 had to be patched in Homebrew because its tests weren't passing. libgcrypt 1.9.1 doesn't build.)
(Indeed, libgcrypt 1.9.0 had to be patched in Homebrew because its tests weren't passing. libgcrypt 1.9.1 doesn't build.)
This... might be such an incredibly perfect heap overflow bug that it's possible even I could exploit it on a modern system.
The last time I wrote an exploit was when http://microcorruption.com came out. https://twitter.com/taviso/status/1355167637538365442
The last time I wrote an exploit was when http://microcorruption.com came out. https://twitter.com/taviso/status/1355167637538365442