convert_ecdsa_sig() calls i2d_ECDSA_SIG(ecdsa_sig, &p) where p
points into signature->area, a trash chunk of tune.bufsize bytes
(default 16384). i2d writes with no output bound.

The raw R||S input can be up to bufsize bytes (filled by
base64urldec at jwt.c:520-527), giving bignum_len up to 8192. The
DER encoding adds a SEQUENCE header (2-4 bytes), two INTEGER headers
(2-4 bytes each), and up to two leading-zero sign-padding bytes when
the bignum high bit is set. With two 8192-byte bignums having the
high bit set, the encoding is ~16398 bytes, overflowing the 16384-
byte buffer by ~14 bytes.

Triggered by any JWT with alg=ES256/384/512 and a ~21830-character
base64url signature. The signature does not need to verify
successfully; the overflow happens before verification. Reachable
from any config using jwt_verify with an EC algorithm.

Also fixes the existing wrong check: i2d returns -1 on error which
became SIZE_MAX in the size_t signature->data, defeating the
"== 0" test.

This must be backported as far as JWT support exists.
---
 src/jwt.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/jwt.c b/src/jwt.c
index 3dc04b1fbb9b..0ab557afe413 100644
--- a/src/jwt.c
+++ b/src/jwt.c
@@ -331,14 +331,21 @@ static int convert_ecdsa_sig(const struct jwt_ctx *ctx, 
struct buffer *signature
        /* Build ecdsa out of R and S values. */
        ECDSA_SIG_set0(ecdsa_sig, ec_R, ec_S);
 
-       p = (unsigned char*)signature->area;
-
-       signature->data = i2d_ECDSA_SIG(ecdsa_sig, &p);
-       if (signature->data == 0) {
+       /* i2d_ECDSA_SIG writes with no output bound. The DER encoding adds
+        * a SEQUENCE header (~4 bytes), two INTEGER headers (~4 bytes each),
+        * and up to two sign-padding bytes on top of the raw R||S bytes.
+        * Compute the length first to avoid overflowing signature->area.
+        */
+       retval = i2d_ECDSA_SIG(ecdsa_sig, NULL);
+       if (retval <= 0 || (size_t)retval > signature->size) {
                retval = JWT_VRFY_INVALID_TOKEN;
                goto end;
        }
 
+       p = (unsigned char*)signature->area;
+       signature->data = i2d_ECDSA_SIG(ecdsa_sig, &p);
+       retval = 0;
+
 end:
        ECDSA_SIG_free(ecdsa_sig);
        return retval;
-- 
2.53.0



Reply via email to