curl · Report #3972338
12: SASL DIGEST-MD5 does not validate the server's `rspauth` proof
How it was found
Summary
curl never verifies the rspauth value the server sends during SASL DIGEST-MD5 authentication. In Curl_sasl_continue() (lib/curl_sasl.c), the SASL_DIGESTMD5_RESP handler unconditionally sends an empty acknowledgment without reading or checking the server's continuation data. Authentication then succeeds based solely on the final protocol status code (e.g. SMTP 235). This affects SMTP, IMAP, and POP3 — they all share the same SASL state machine.
RFC 2831 §2.1.3 requires the client to validate rspauth to authenticate the server. Without that check, a server that does not know the shared secret can return any rspauth (or garbage), get accepted, and receive subsequent application data.
Root cause
// lib/curl_sasl.c, inside Curl_sasl_continue()
case SASL_DIGESTMD5_RESP:
/* Keep response NULL to output an empty line. */
break;
No call to get_server_message(), no parsing of rspauth, no comparison with the expected digest. The state just falls through to SASL_FINAL, which only checks the status code.
Affected Versions
curl 7.26.0 through 8.21.0 (latest release), and current master.
Reproduciton steps:
- Start a local SMTP server on 127.0.0.1 that advertises AUTH DIGEST-MD5.
- Have the server issue a valid DIGEST-MD5 challenge (realm, nonce, algorithm=md5-sess, qop="auth").
- After receiving the client's digest response, have the server reply with a 334 continuation containing a deliberately wrong rspauth (e.g. rspauth=00000000000000000000000000000000), then 235 Authentication successful.
- Run curl --url smtp://127.0.0.1:<port> --user "user:secret" --mail-from [email protected] --mail-rcpt [email protected] --upload-file message.txt.
- Observe curl exits 0, sends the empty acknowledgment, and delivers the full email body — despite the server never knowing the password.
- As a control, repeat but have the server skip the 334 continuation and send 235 directly after the digest response.
- Observe curl correctly rejects with exit 67, proving it requires the continuation but never validates its content.
Suggested fix
Either add rspauth validation (retain the digest state from the client-response step, compute the expected server proof per RFC 2831 using the server-side A2, parse and compare), or remove DIGEST-MD5 support entirely — the mechanism was moved to Historic by RFC 6331 in 2011.
Impact
Summary:
On a connection without independently authenticated TLS, an impersonating endpoint that does not know the password can complete DIGEST-MD5 authentication and receive whatever the client sends next (email body, IMAP commands, etc.). The password itself is not disclosed. Exploitation requires MITM positioning and DIGEST-MD5 negotiation without TLS, which limits practical severity.