Repository navigation
Conversation
SCRAM_Hi() copies the salt into a 128 byte buffer on the stack and appends INT(1) to it, so it can only handle salts of up to 124 bytes. That limit was only enforced by an assert(), which is compiled out as soon as NDEBUG is defined. The salt isn't under our control though. It is taken verbatim from the 's=' attribute of the server-first-message, base64-decoded by sasl_scram() without any limit on the resulting length, and passed down to SCRAM_Hi() through SCRAM_ClientKey(). A malicious server can therefore send an 's=' attribute that decodes to more than 124 bytes and overflow the stack frame of SCRAM_Hi(), which aborts the client in debug builds and silently corrupts the stack otherwise. Turn the assert() into a runtime check and propagate the failure through SCRAM_ClientKey() up to sasl_scram(), which already aborts the authentication when it can't compute a response. This is CVE-2026-112063. Reproduced with a 125 byte salt in an NDEBUG build under ASan, before the fix: ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 4 ... strophe#1 SCRAM_Hi scram.c:189 strophe#2 SCRAM_ClientKey scram.c:215 strophe#3 sasl_scram sasl.c:457 Signed-off-by: Bohdan <bohdan.molodan@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SCRAM_Hi()concatenates the SCRAM salt withINT(1)into a 128 byte buffer on thestack, so it can only handle salts of up to 124 bytes. That limit was enforced only by
an
assert(), which is compiled out as soon asNDEBUGis defined.The salt isn't under our control, though. It is taken verbatim from the
s=attribute ofthe
server-first-message, base64-decoded bysasl_scram()without any limit on theresulting length, and passed down to
SCRAM_Hi()throughSCRAM_ClientKey():src/sasl.c:427—xmpp_base64_decode_bin()decodess=intosval/sval_len,unbounded
src/sasl.c:457—sval/sval_lengo straight intoSCRAM_ClientKey()src/scram.c:181—assert(salt_len <= sizeof(tmp) - sizeof(int1)), thenmemcpy(tmp, salt, salt_len)atsrc/scram.c:188So a malicious or compromised server can send an
s=attribute that decodes to more than124 bytes and overflow the stack frame of
SCRAM_Hi(). In a debug build the clientaborts; with
NDEBUGthe stack is silently corrupted. This is CVE-2026-112063 (affects0.14.0 and current master).
The fix
assert()into a runtime check and giveSCRAM_Hi()anintreturn value.SCRAM_ClientKey()up tosasl_scram(), which alreadyaborts the authentication when it can't compute a response —
_handle_scram_challenge()in
src/auth.c:504takesgoto erron aNULLreturn.SCRAM_SALT_MAXinsrc/scram.cand sizetmpfrom it(
uint8_t tmp[SCRAM_SALT_MAX + sizeof(int1)]), so the buffer and the check can't driftapart.
tests/test_scram.cpicks the constant up through the existing#include "scram.c"rather than hardcoding 124.sasl_scram()withstrophe_error(ctx, "SASL", ...). Withoutit the failure reaches the generic
err:label insrc/auth.c, which callsdisconnect_mem_error()and reports"Memory allocation error"— misleading for anoversized salt, and it would send someone chasing a memory bug that isn't there.
SCRAM_ClientKey()is declared insrc/scram.h, which is not an installed header(
Makefile.am:114installs onlystrophe.h), so the signature change is internal.SCRAM_ClientSignature()andSCRAM_ClientProof()are untouched — both are bounded byalg->digest_size.Reproducer
Against unmodified master,
NDEBUGbuild under ASan, driving the realsasl_scram()witha
server-first-messagewhoses=decodes to 125 bytes:With the patch, the same harness returns
NULLand aborts authentication instead:Tests
tests/test_scram.cgainstest_salt_len(), run for SCRAM-SHA-1, -SHA-256 and -SHA-512.It is table-driven like the existing
test_df()/test_scram()/test_hmac()andchecks both the at-limit and one-over-limit cases through
SCRAM_Hi()andSCRAM_ClientKey(). It uses the existingENSURE_EQfromtests/test.hrather thanassert()on purpose:assert()is compiled out underNDEBUG, which is exactly theconfiguration this CVE is about, so an assert-based test would pass vacuously there.
Existing RFC 6070, RFC 6120, RFC 2202 and RFC 4231 vectors still pass. Verified in both
debug and
-DNDEBUGbuilds, clean under ASan and-Wall -Wextra -Werror. Removing thenew bounds check makes the test fail loudly (ASan stack-buffer-overflow), so it does pin
the regression.
Note on a deeper fix
Rejecting long salts hardens a self-imposed limit into protocol-visible behaviour — RFC
5802 sets no upper bound on salt length, so a conforming server using an unusually long
salt is now refused. The alternative is to stop concatenating altogether:
crypto_HMAC()already drives the hash through
alg->init/alg->update/alg->final, so it couldtake the message in two chunks and
SCRAM_Hi()could feed it(salt, salt_len)and(int1, 4)separately. That removes the buffer, the limit and this error path entirely.I kept this PR to the minimal security fix since it touches a crypto primitive and changes
behaviour for long salts, but I'm happy to follow up with that refactor — or to fold it in
here instead — whichever you prefer.
Unrelated to this CVE but in the same attacker-controlled message:
ivalatsrc/sasl.c:431isstrtol()'d and cast touint32_twithout validation, soi=-1yields ~4.3 billion HMAC iterations. Happy to send that separately.