curl · Report #3468098
Certificate Pinning Bypass with wolfSSL backend over HTTP/3
How it was found
Summary:
A security feature bypass exists in libcurl when built with the wolfSSL backend and HTTP/3 support. The Certificate Pinning feature (--pinnedpubkey) is silently ignored if the user also disables peer verification -k or --insecure ) . This behavior is inconsistent with other backends (like OpenSSL) and exposes connections to Man-in-the-Middle attacks.
Affected version
Reproduced on curl 8.8.0-DEV .
docker run --rm smbd/curl-http3-wolfssl:5.7.0 curl -V
curl 8.8.0-DEV (x86_64-pc-linux-gnu) libcurl/8.8.0-DEV wolfSSL/5.7.0 zlib/1.2.13 nghttp2/1.52.0 ngtcp2/1.5.0 nghttp3/1.3.0
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTP2 HTTP3 IPv6 Largefile libz SSL threadsafe UnixSockets
Steps To Reproduce:
We compare the behavior of a standard build (OpenSSL) versus the vulnerable build (wolfSSL) using a bogus pinned hash (AAAA...) against a valid server (google.com).
-
Control Test (Standard OpenSSL ):
Run:
curl -k --pinnedpubkey "sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" https://www.google.com/Result:
curl: (90) SSL: public key does not match pinned public key -
Exploit Test (Vulnerable wolfSSL + HTTP/3 build):
Run the same command using the vulnerable environment (reproduced here via Docker
smbd/curl-http3-wolfssl:5.7.0) :docker run --rm smbd/curl-http3-wolfssl:5.7.0 curl -v --http3 -k --pinnedpubkey "sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" https://www.google.com/
Result: ` docker run --rm smbd/curl-http3-wolfssl:5.7.0 curl -v --http3 -k --pinnedpubkey "sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" https://www.google.com/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Host www.google.com:443 was resolved.
- IPv6: 2800:3f0:4001:83c::2004
- IPv4: 172.217.30.68
- Trying 172.217.30.68:443...
- Trying [2800:3f0:4001:83c::2004]:443...
- Immediate connect fail for 2800:3f0:4001:83c::2004: Network is unreachable
- Trying 172.217.30.68:443...
- Connected to www.google.com (172.217.30.68) port 443
- successfully set certificate verify locations:
- CAfile: /etc/ssl/certs/ca-certificates.crt
- CApath: /etc/ssl/certs
- ALPN: curl offers h2,http/1.1 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to www.google.com (172.217.30.68) port 443
- using HTTP/3
- [HTTP/3] [0] OPENED stream for https://www.google.com/
- [HTTP/3] [0] [:method: GET]
- [HTTP/3] [0] [:scheme: https]
- [HTTP/3] [0] [:authority: www.google.com]
- [HTTP/3] [0] [:path: /]
- [HTTP/3] [0] [user-agent: curl/8.8.0-DEV]
- [HTTP/3] [0] [accept: /]
GET / HTTP/3 Host: www.google.com User-Agent: curl/8.8.0-DEV Accept: /
- Request completely sent off < HTTP/3 200 < date: Tue, 16 Dec 2025 16:44:58 GMT < expires: -1 < cache-control: private, max-age=0 < content-type: text/html; charset=ISO-8859-1 < content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-zhAjavKpmSo8XJCkxA_NgQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp < accept-ch: Sec-CH-Prefers-Color-Scheme < p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info." < server: gws < x-xss-protection: 0 < x-frame-options: SAMEORIGIN < set-cookie: __Secure-STRP=AD6DoguQyJNF6fdQjWRffsBqL5E7sCEdu2diQU92k64Obdab6qaZZ_YrOXrt5mjSi_mZUvgnouZxGUgkqQQ0qUaPWIKNTV8dktGI; expires=Tue, 16-Dec-2025 16:49:58 GMT; path=/; domain=.google.com; Secure; SameSite=strict < set-cookie: AEC=AaJma5sy9-_cBlJAKCzRhnuHw8Hi2jSxiSB0qdTwn8F7JAvuHTabjD7SuO4; expires=Sun, 14-Jun-2026 16:44:58 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax < set-cookie: NID=527=UAu9nT29vQQ1b9IRZfHQWcggNUyZO7X6TTM2XOkZfaPi93mxevHPyD3CB1KhkeUivwLhDrFjkCOg1iVwpetsU5zpb7HuBvlYJxY0sFo_V2wH_u2IJ4IvB-pyvLSn4YsS-qeG5RWrGwQy1hEHoNLK-PByHY1YI_10N-Jred14vtoFNySz1nc0la5zEC8qWvqhVzf0davySmgyeGedns-JHN2ceE3i_Tdh; expires=Wed, 17-Jun-2026 16:44:58 GMT; path=/; domain=.google.com; HttpOnly < set-cookie: __Secure-BUCKET=CN8C; expires=Sun, 14-Jun-2026 16:44:58 GMT; path=/; domain=.google.com; Secure; HttpOnly < alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 < accept-ranges: none < vary: Accept-Encoding < { [104 bytes data]
Root Cause Analysis:
The issue is located in lib/vquic/vquic-tls.c. The logic that triggers the pinning check (Curl_wssl_verify_pinned) appears to be conditionally skipped or improperly handled when the primary verification (verifyhost) is disabled via configuration. Unlike the OpenSSL implementation, the wolfSSL wrapper for QUIC does not treat Pinning as an independent check.
Impact
This vulnerability allows a Man-in-the-Middle (MitM) attacker to intercept and decrypt traffic, even when the user has explicitly configured Certificate Pinning to prevent such attacks.