Exploitable heap overflow in libgcrypt 1.9.0 (β”›ΰ² _ΰ² )┛彑┻━┻ It's the crypto library that gpg uses. Homebrew has 1.9.0 right now. 🚨 dev.gnupg.org/T5259

12:02 PM Β· Jan 29, 2021

7
331
57
772
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. dev.gnupg.org/rC512c0c752769…
2
10
3
83
Reported-by: Tavis Ormandy <taviso@gmail.com> πŸ‘‹ @taviso
2
2
0
127
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.
1
5
0
51
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.
1
3
0
49
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.
2
13
2
127
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.
2
2
0
70
Another fun angle is that this vulnerability is introduced by timing side channel mitigations. Previously: blog.cloudflare.com/yet-anot… Not good ones, though. The suffix is always written at the beginning of a block, and md_final is not constant time.
1
7
0
63
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. golang.org/cl/18130
1
2
0
67
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! 😱
1
0
0
72
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 filippo.io/newsletter issue. Maybe it should still be.
4
1
0
67
"Just decrypting some data can overflow a heap buffer with attacker controlled data, no verification or signature is validated before the vulnerability occurs." β€” @taviso lists.gnupg.org/pipermail/gn…
3
32
5
84
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. github.com/Homebrew/homebrew…
3
30
5
179
--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. dev.gnupg.org/rC8d404a629167…
2
2
0
65
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.)
7
42
16
225
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 microcorruption.com came out.
Haha, the buffer is inside a struct like struct { char buf[128]; int foo; int bar; funcptr baz; }, so you can partial-overwrite the funcptr without even damaging the heap. It gets called immediately after the overflow with controlled params. Couldn't hope for a better heap bug πŸ˜›
4
17
2
142