]> git.kaiwu.me - haproxy.git/commit
BUG/MINOR: payload: bound ClientHello extension lists by the extension length
authorWilliam Lallemand <wlallemand@irq6.net>
Sat, 29 Aug 2026 10:05:33 +0000 (10:05 +0000)
committerWilliam Lallemand <wlallemand@irq6.net>
Sat, 29 Aug 2026 10:38:00 +0000 (12:38 +0200)
commitcb1eebf9d985a2100c6eff4368cde539d89fd845
treee2655795b654765417087ee3301f8cf69d2829ec
parent98db20a83dd7be7bb1ea70e97b5e8e5b544ce9b0
BUG/MINOR: payload: bound ClientHello extension lists by the extension length

smp_fetch_ssl_supported_groups(), smp_fetch_ssl_sigalgs(),
smp_fetch_ssl_keyshare_groups() and smp_fetch_ssl_hello_sni() all check
the length of the list carried by their extension against

    <list_len> > hs_len - 6

where <hs_len> is what remains of the *whole* extension block, not of the
extension being parsed. The list lives in the extension body, so the
bound has to be <ext_len> - 2. All four sites already reject ext_len < 2
beforehand, so the new bound cannot go negative, and since ext_len is
itself checked against hs_len - 4 a few lines above, it is strictly
tighter than the current one.

With the loose check, an extension whose body holds nothing but the
2-byte list length is accepted while announcing a list that runs into the
extensions that follow, and the fetch returns bytes belonging to another
extension:

    groups=ff010020aaaaaaaaaaaa...   <- next extension's header and body
    sigalgs=ff010020aaaaaaaaaaaa...
    keyshare=ff01
    sni=HIJACKED!!

For req.ssl_sni the effect is the most visible, since name_type, name_len
and the hostname are then all read from the neighbouring extension, and a
hostname is reported that appears in no server_name entry.

Those reads all stay within the ClientHello, whose bytes are supplied by
the client anyway, so this is neither an out-of-bounds access nor an
information disclosure: the returned sample ends at worst on the last
byte of the extension block. It does however let a client decouple what
use-server/use_backend rules and logs observe from what it really
announced, on a ClientHello that a conformant TLS stack would reject.

A reg-test covering, for each of the four fetches, a well-formed
extension and an over-announced list is added.

The req.ssl_supported_groups, req.ssl_sigalgs and req.ssl_keyshare_groups
parts should be backported as far as 3.2, where those fetches appeared
with commit fa063a9e7 ("MINOR: sample: add 4 new sample fetches for
clienthello parsing"). The req.ssl_sni part is much older, it dates back
to commit b6672b547 ("MINOR: acl: add support for TLS server name
matching using SNI") in 1.5-dev8, so backports to branches older than 3.2
need to keep that hunk only.
reg-tests/sample_fetches/ssl_client_hello_ext.vtc [new file with mode: 0644]
src/payload.c