curl still prints brotli/1.1.0 after the Debian fix

John Burns

curl still prints brotli/1.1.0 after the Debian fix

curl --version printed brotli/1.1.0 while the loaded decoder was Debian’s security update, and it printed the same token while the loaded decoder was the unpatched trixie archive build. Both files are named libbrotlidec.so.1.1.0. Both are 51376 bytes. Both answer BrotliDecoderVersion() with 0x1001000. A rule that treats 1.1.0 as “older than 1.1.1” flags the fixed package. A rule that treats brotli/1.1.0 as proof of the fix accepts the vulnerable package. Neither rule can see the upload that actually closed CVE-2026-111497 on Debian 13.

CISA added that CVE to the Known Exploited Vulnerabilities catalog on 27 October 2026. The due date in the catalog is 10 November 2026. The entry names Google Brotli and points at the upstream v1.2.1 tag. The GitHub advisory GHSA-9v8h-qqgm-4m35 says versions 1.0.3 through 1.2.0 are affected and tells operators to move to 1.0.10, 1.1.1, or 1.2.1. Debian’s stable fix does not use any of those version numbers. DSA-6582-1, dated 23 October 2026, says the trixie package is fixed in 1.1.0-2+deb13u1. The changelog line in that package is “Detect and report invalid stream earlier”. The upstream version field stays 1.1.0.

The integer behind the curl line

curl 8.14.1 does not store a Brotli version string at compile time and hope the library still matches. In lib/version.c, brotli_version() calls BrotliDecoderVersion() and formats the 32-bit result. The major field is the value shifted right 24 bits. The minor field is the low 24 bits shifted right 12. The patch field is the low 12 bits. For 0x1001000 those fields are 1, 1, and 0, so the version line contains brotli/1.1.0.

A split that treats the same word as three ordinary bytes does not get that answer. Take the high 8 bits as the major, the next 8 bits as the minor, and the remaining 16 bits as the patch, and 0x1001000 prints as 1.0.4096. That decoding is wrong for this library. It is also useless as a patch check, because both Debian packages return the same word. The curl line and a correct decode agree with each other, and both agree with the vulnerable package.

The client in the comparison was the Debian 13 package curl 8.14.1-2+deb13u7. LD_LIBRARY_PATH selected one unpacked tree at a time, and ldd showed libbrotlidec.so.1 and libbrotlicommon.so.1 resolving into that tree. With the unpatched tree first, and again with the security-update tree first, the first line of curl --version still contained brotli/1.1.0. The second line still said the curl package was security patched as 8.14.1-2+deb13u7. That curl revision is a curl fix. It is not a Brotli fix. The feature list still included the word brotli in both runs. Presence of the feature only means this binary was built with a decoder. It does not name the Debian upload.

The decoder file does not grow

The packages were the official amd64 binaries, not a local rebuild. apt-get download fetched libbrotli1 1.1.0-2+b7 from the trixie archive and 1.1.0-2+deb13u1 from trixie-security. The SHA-256 of those .deb files:

1.1.0-2+b7        0fb79f88db210afbd69282ab9649e525f393ec6950ca34da1a6b359250b8d7db
1.1.0-2+deb13u1   db46824c60fee46aff3cc0be3e5f736bc606cae669b1fb17516a41e8073251a9

Inside both packages the decoder SONAME is libbrotlidec.so.1. The file libbrotlidec.so.1.1.0 is 51376 bytes in both. nm -D lists the same exported symbol names. A size check and a symbol-name diff cannot see the security update.

The file hashes can. SHA-256 of libbrotlidec.so.1.1.0:

1.1.0-2+b7        06530583d5927000480f4214d80d0213fec3d34cc448527f932a6eceb50cefef
1.1.0-2+deb13u1   ff4baf3141862726c25ab08cdd94bced37dcae0c6827943d3e40fc1079969814

readelf -n prints a GNU build id that also splits them:

1.1.0-2+b7        ef8a49705d9a38da75aabeb57297da34bd4220c9
1.1.0-2+deb13u1   d4b2f481dc6ce7695fdd44e87d2028e412b3b286

The other two libraries changed as well, and that is a trap if the check hashes the wrong file. libbrotlicommon.so.1.1.0 is 141496 bytes in both packages, with different hashes and different build ids. libbrotlienc.so.1.1.0 is 670376 bytes in 1.1.0-2+b7 and 666280 bytes in 1.1.0-2+deb13u1. A smaller encoder is a real difference. It is the wrong file for this CVE. The advisory places the bug in the decoder. A hash of the common library, or a size change in the encoder, does not prove which decoder a process loaded.

The string that marks the patched decoder in this pair is _ERROR_FORMAT_BLOCK_SWITCH. strings finds it once in the security-update decoder and zero times in the trixie decoder. It is not in either encoder, and it is not in either copy of the common library. The changelog’s phrase “detect and report invalid stream earlier” is the vendor statement. The error name is the byte sequence that statement left in this amd64 build. It is a marker for these two packages, not a portable definition of every fixed Brotli in the world.

A clean round trip is not the patch

A small probe called the version and compress functions with LD_LIBRARY_PATH selecting one tree at a time. BrotliDecoderVersion() and BrotliEncoderVersion() both returned 0x1001000 on both trees. BrotliEncoderCompress of the 21-byte string brotli-revision-check returned success and 23 encoded bytes on both trees. BrotliDecoderDecompress of that buffer returned the same 21 bytes on both trees. A one-byte buffer containing only 0x00 failed decompression on both trees, and both calls reported zero bytes written.

That last result is easy to mistake for a patch test. An input both libraries reject does not show the earlier error the changelog describes. This comparison did not build a stream aimed at the block-switch path, and it did not try to reproduce the out-of-bounds write. A successful decode of ordinary text only shows that the library still implements Brotli. The unpatched library does that too. Do not attach a green decode of a known-good buffer to a KEV ticket and call the decoder patched.

Verify the package, then the file

Read the package version before you read the curl line.

dpkg-query -W -f '${Package} ${Version}\n' libbrotli1

On the Debian 13 amd64 host used for this comparison, that command printed libbrotli1 1.1.0-2+deb13u1. The decoder file on that host had the security-update SHA-256 and build id above, and strings found _ERROR_FORMAT_BLOCK_SWITCH in it. The trixie archive package that is still vulnerable is 1.1.0-2+b7. The +b7 suffix is a binary rebuild, not a security revision. +deb13u1 is the security upload.

dpkg --compare-versions agrees with that order and disagrees with an upstream cutoff. 1.1.0-2+deb13u1 is greater than 1.1.0-2+b7. The same version is not greater than or equal to 1.1.1. A scanner that requires BrotliDecoderVersion() to reach 1.1.1, or that requires the Debian version to sort as at least 1.1.1, will keep failing a host that has installed DSA-6582-1. Compare the full Debian version with dpkg --compare-versions against 1.1.0-2+deb13u1, or require an exact match on that string for trixie.

If you need to know which file a process loaded, hash libbrotlidec.so.1.1.0 or read its build id. The digests above are for the amd64 packages fetched on 29 October 2026. Another architecture will not share them. The package version will. Do not accept a hash of libbrotlicommon.so.1.1.0 as a substitute, even though that file’s hash also changed between these two uploads.

On this amd64 layout the decoder path is:

strings -a /usr/lib/x86_64-linux-gnu/libbrotlidec.so.1.1.0 | grep ERROR_FORMAT_BLOCK_SWITCH

One match means you are looking at the security-update decoder from this comparison, or at a file that contains the same marker. Zero matches, together with package version 1.1.0-2+b7 and a 51376-byte decoder, matched the vulnerable file. A different distribution, a static link, or a copy under another path will not grow this string just because libbrotli1 was upgraded. The string is evidence for this Debian 13 amd64 upload. It is not a test you can paste onto a macOS framework or a Windows DLL and expect the same hit.

curl --version still has a job after that. It shows that the running client can see a decoder, and it shows the integer that decoder returns. After the trixie security update, the line should still say brotli/1.1.0. If it says brotli/1.1.1 or brotli/1.2.1, you are not looking at this backport. If it omits brotli, this curl was not linked to the library you just checked. Point LD_LIBRARY_PATH at a candidate tree and run ldd on curl before you trust the line. In this comparison the line did not change when the loaded decoder changed, which is the result a package gate has to expect.

Upstream 1.1.1 is a different fix

The advisory text is the right cutoff for an upstream tarball or a language package that vendors those tags. Google’s fixed releases are 1.0.10, 1.1.1, and 1.2.1. A container that ships one of those trees should be on one of them. A Debian trixie host on 1.1.0-2+deb13u1 is on the fix DSA-6582-1 published, and it will keep failing a rule copied from the GitHub advisory without a Debian exception. The KEV note that points only at the 1.2.1 tag has the same gap. The catalog is telling you the component is exploited. It is not telling you the Debian revision that closes it.

The reverse mistake is the one the curl line invites. brotli/1.1.0 is also what you get from 1.1.0-2+b7. The version word cannot close the KEV item. The package version can, for this distribution and this source package. A process that statically links a decoder, or that ships a private copy, is outside that package check. This comparison did not open a Node.js binary. The Debian security tracker still lists the trixie nodejs source package as vulnerable after the libbrotli1 update, so a green dpkg-query on libbrotli1 is not a green bill for every program that can decompress Brotli. Check the package that actually provides the decoder the process loads. If that package is not libbrotli1, the revision above does not apply.

Record three facts before you call the host done. The full libbrotli1 version must be 1.1.0-2+deb13u1 on trixie, or a later Debian security revision that supersedes it. The decoder file the process loads must match that package, by path from the package file list or by the SHA-256 or build id of the file you hashed. On this amd64 update, that file contains _ERROR_FORMAT_BLOCK_SWITCH. Keep the curl --version line if you want proof the running client still sees 0x1001000. Do not let that line stand in for the package.

Sources

  • DSA-6582-1, 23 October 2026, fixing brotli on trixie as 1.1.0-2+deb13u1
  • Changelog entry in that package: “Detect and report invalid stream earlier (CVE-2026-111497)”
  • CISA KEV catalog, CVE-2026-111497 added 27 October 2026, due 10 November 2026, noting upstream v1.2.1
  • GHSA-9v8h-qqgm-4m35, affected range 1.0.3 through 1.2.0, fixed upstream tags 1.0.10, 1.1.1, and 1.2.1
  • curl 8.14.1 lib/version.c, brotli_version() formatting of BrotliDecoderVersion()
  • Debian security tracker, CVE-2026-111497, including the trixie fixed version and the still-vulnerable trixie nodejs source package