HexBrain

curl · Report #3468098

Certificate Pinning Bypass with wolfSSL backend over HTTP/3

N/Astatus
mediumseverity
7upvotes
17 Dec 2025disclosed

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).

  1. 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

  2. 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]
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="fr-CM"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script nonce="zhAjavKpmSo8XJCkxA_NgQ">(function(){var _g={kEI:'ioxBaZb1NrTU5OUPqOCEUQ',kEXPI:'0,203004,45,1101175,4,2,4,3030729,344796,270476,5262392,36811500,25228681,217550,30632,7033,2105,4600,6553,50098,14067,61059,28338,48236,28322,3,2749,7714,6975,7774,15974,2662,4719,27257,19845,9881,36458,20879,1018,4372,4532,1760,16179,2646,101,2,4,1,4434,12368,2305,5031,1472,2,13032,1382,4,1514,578,2777,195,572,2869,2374,2,4205,5,1789,17,8476,1430,4384,2,876,2229,5089,19,3007,14,782,20847,2407,12338,1031,1034,1197,3106,845,2047,1,1,7,437,1128,3,2842,9,27,8,3055,1012,1745,4,3510,3,11680,4,2170,73,241,296,5,1113,389,4,298,901,1448,5,4819,15,734,298,4309,2806,2138,1275,7,1786,1048,2,2434,690,741,988,1084,3780,4,93,2561,2,5375,4,343,7,606,67,1582,4,1882,6,2,2,2,680,812,745,3310,483,746,5,465,793,1,2,1033,22,323,3,1359,3,2140,3,2788,49,10,215,441 .........................................`

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.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original