[LOW] gnutls ocsp stapling: multi-record status misbinding can bypass revocation
hi Gnutls,
gnutls matches a stapled ocsp response to the server certificate by scanning SingleResponse records, but then reads cert_status from record index 0 unconditionally. when a multi-record ocsp response is stapled such that record 0 is for a different certificate (good) and the matching record for the server certificate is later (revoked), a client with ocsp verification enabled can accept a revoked server certificate. this is observable as an order-dependent accept/reject outcome for the same revoked server certificate.
severity
HIGH
relevant links
- repo: https://gitlab.com/gnutls/gnutls
- commit: 165ae258
- file: lib/cert-session.c (check_ocsp_response)
root cause
in check_ocsp_response(), the code finds the matching record index via gnutls_ocsp_resp_check_crt(resp, resp_indx, cert) but later consumes status/time fields via gnutls_ocsp_resp_get_single(resp, 0, ...), ignoring resp_indx. this breaks the binding between “matched record” and “enforced status”.
impact
revocation enforcement can be bypassed for clients and applications that enable ocsp stapling verification, allowing a server to present a revoked certificate while still passing verification depending on ocsp record ordering.
proof of concept
unzip poc.zip -d poc
cd poc
bash ./canonical.sh
expected:
[CALLSITE_HIT]: check_ocsp_response :: matched record index != 0 (multi-record) but cert_status is read from index 0 [PROOF_MARKER]: revoked server cert accepted when revoked ocsp record is not index 0 (multi-record stapled ocsp)
unzip poc.zip -d poc
cd poc
bash ./control.sh
expected:
[NC_MARKER]: revoked server cert rejected when revoked ocsp record is index 0 (environment sanity check)
expected: if the stapled ocsp response contains a revoked status for the server certificate, the client must reject, independent of the record ordering. actual: the client can accept a revoked server certificate when the revoked record is not index 0.
recommended fix
use the matched record index (resp_indx) when calling gnutls_ocsp_resp_get_single() in check_ocsp_response(), and add a regression test covering a multi-record response where the server cert record is not index 0 and is revoked.
credit: 1seal (https://github.com/1seal)
cheers, Oleh Konko