CVE-2026-26309: Envoy has an off-by-one write in JsonEscaper::escapeString()

Published Mar 10, 2026
·
Updated

### Summary An off-by-one write in Envoy::JsonEscaper::escapeString() can corrupt std::string null-termination, causing undefined behavior and potentially leading to crashes or out-of-bounds reads when the resulting string is later treated as a C-string. ### Details The bug is in the control-character escaping path in source/common/common/ json_escape_string.h:67. - The function pre-sizes result to the final length: std::string result(input.size() + required_size, '\\'); - For control characters (0x00..0x1f), it emits a JSON escape sequence of length 6: \u00XX. - It uses sprintf(&result[position + 1], "u%04x", ...), which writes 5 chars + a trailing NUL (\0) starting at result[position + 1]. - Then it does position += 6; and writes result[position] = '\\'; to overwrite the NUL. - If the control character occurs at the end of the output (e.g., the input ends with \x01), then after position += 6, position == result.size(), so result[position] is one past the end (off-by-one), violating std::string bounds/contract. Concretely, the problematic lines are: - source/common/common/json_escape_string.h:69 (sprintf(...)) - source/common/common/json_escape_string.h:72 (result[position] = '\\';) Potentially reachable from request-driven paths that escape untrusted data, e.g. invalid header reporting: - source/common/http/header_utility.cc:538 ~ source/common/http/ header_utility.cc:546 (escapes invalid header key for error text) Even when this doesn’t immediately crash, it can break the std::string requirement that c_str()[size()] == '\0', which can later trigger UB (e.g., if passed to strlen, printf("%s"), or any C API that expects NUL termination). ```cpp //clang++ -std=c++20 -O0 -g -fsanitize=address -fno-omit-frame-pointer repro_json_escape_asan.cc -o repro_json_escape_asan ASAN_OPTIONS=abort_on_error=1 ./repro_json_escape_asan #include <cstdint> #include <cstdio> #include <cstring> #include <string> #include <string_view> static uint64_t extraSpace(std::string_view input) { uint64_t result = 0; for (unsigned char c : input) { switch (c) { case '\"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': result += 1; break; default: if (c == 0x00 || (c > 0x00 && c <= 0x1f)) { result += 5; } break; } } return result; } static std::string escapeString(std::string_view input, uint64_t required_size) { std::string result(input.size() + required_size, '\\'); uint64_t position = 0; for (unsigned char character : input) { switch (character) { case '\"': result[position + 1] = '\"'; position += 2; break; case '\\': position += 2; break; case '\b': result[position + 1] = 'b'; position += 2; break; case '\f': result[position + 1] = 'f'; position += 2; break; case '\n': result[position + 1] = 'n'; position += 2; break; case '\r': result[position + 1] = 'r'; position += 2; break; case '\t': result[position + 1] = 't'; position += 2; break; default: if (character == 0x00 || (character > 0x00 && character <= 0x1f)) { std::sprintf(&result[position + 1], "u%04x", static_cast<int>(character)); position += 6; // Off-by-one when this escape is the last output chunk: // position can become result.size(), so result[position] is out of bounds. result[position] = '\\'; } else { result[position++] = static_cast<char>(character); } break; } } return result; } int main() { std::string input(4096, 'A'); input.push_back('\x01'); // ends with a control char -> triggers the buggy path at the end const uint64_t required = extraSpace(input); std::string escaped = escapeString(input, required); std::printf("escaped.size=%zu\n", escaped.size()); unsigned char terminator = static_cast<unsigned char>(escaped.c_str() [escaped.size()]); std::printf("escaped.c_str()[escaped.size()] = 0x%02x\n", terminator); // If NUL termination is corrupted, this can read past the logical end. std::printf("strlen(escaped.c_str()) = %zu\n", std::strlen(escaped.c_str())); return 0; }```

Affected Software

8 affected components
go/github.com/envoyproxy/envoy<=1.34.12
go/github.com/envoyproxy/envoy>=1.35.0<=1.35.8
go/github.com/envoyproxy/envoy>=1.36.0<=1.36.4
go/github.com/envoyproxy/envoy=1.37.0
Envoyproxy Envoy<1.34.13
Envoyproxy Envoy>=1.35.0<1.35.8
Envoyproxy Envoy>=1.36.0<1.36.5
Envoyproxy Envoy=1.37.0

Event History

Mar 10, 2026
Advisory Published
via GitHub·06:30 PM
Data Sourced
via GitHub·06:30 PM
DescriptionSeverityWeaknessAffected Software
CVE Published
via MITRE·07:04 PM
Data Sourced
via MITRE·07:04 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·08:16 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-26309?

CVE-2026-26309 has a moderate severity level due to the potential for undefined behavior and application crashes.

2

How do I fix CVE-2026-26309?

To mitigate CVE-2026-26309, upgrade Envoy to version 1.34.13 or later, or to versions 1.35.8 or later.

3

What is the impact of CVE-2026-26309?

The impact of CVE-2026-26309 includes potential crashes and out-of-bounds reads due to corrupted string null-termination.

4

Which Envoy versions are affected by CVE-2026-26309?

Versions of Envoy from 1.34.0 up to 1.34.12, 1.35.0 to 1.35.8, 1.36.0 to 1.36.4, and 1.37.0 are affected by CVE-2026-26309.

5

Is CVE-2026-26309 a critical vulnerability?

CVE-2026-26309 is not classified as critical, but it poses a risk of application instability that should be addressed.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203