CVE-2024-9681: HSTS subdomain overwrites parent cache entry
What the report claims
A subdomain's HSTS header can overwrite the parent domain's HSTS expiration time in curl's HSTS cache, causing incorrect expiration values.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2024-9681 HSTS subdomain overwrites parent cache entry
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
Why this finding?
- The report provides detailed reproduction steps and describes behavior that contradicts RFC 6797 (confirmed by external sources E1, E2), which explicitly states a UA must not modify a superdomain's expiry time.
- However, the curl source code for HSTS handling (hsts.c) is not included in the provided source (only lib/urldata.h lines 183-258 is shown), so the implementation flaw cannot be verified against the code.
- The deterministic checks indicate the revision was 'grounded' but do not provide the actual HSTS code findings.
- The issue appears plausible and would be a security-relevant standards violation (parent domain DoS via subdomain), but code-level confirmation is missing.
What still needs checking
- The hsts.c source file (or relevant functions like Curl_hsts_parse, Curl_hsts_save, or cache update logic) from revision 86d5c2651d3e
- The specific function where HSTS cache entries are read, updated, and written when includeSubDomains is processed
How this analysis ran 5 claims · 0 citations verified
Checked against curl tested commit 86d5c2651d3e (86d5c2651d3e). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the detailed report with clear reproduction steps. The behavior you describe contradicts RFC 6797 Section 8.1 and 11.2, which state a UA must not modify a superdomain's expiry time. However, the provided source code for this review only includes lib/urldata.h, not the HSTS implementation (hsts.c). Could you point to the specific function in hsts.c where the cache update occurs when includeSubDomains is processed? That would allow us to verify the fix location.
Inspect all claims and source code 5 claims · 1 code excerpts
Report claims
When a subdomain sets a HSTS header with includeSubDomains, curl incorrectly updates the parent domain's expiration time with the subdomain's expiration time.
Read the reasoning
The claim describes a flaw in HSTS cache update logic, but the provided curl source only includes lib/urldata.h (lines 183-258), which contains CURLcode typedef and unrelated structures. The HSTS implementation (hsts.c) is not in the provided source, so the code cannot be examined to confirm or contradict the flaw.
The bug occurs in the function that parses and stores HSTS entries from the cache file.
Read the reasoning
The claim points to a function in hsts.c that parses and stores HSTS entries. The provided source does not include hsts.c, so the location cannot be verified.
Connecting to a subdomain that sends a Strict-Transport-Security header with includeSubDomains causes the parent domain's cache entry to be overwritten.
Read the reasoning
The trigger involves CURLOPT_HSTS and CURLHSTS processing when a subdomain sends includeSubDomains. The relevant code path is not in the provided source (only urldata.h is shown).
The parent domain's HSTS expiration time is set to the subdomain's expiration time, potentially extending it incorrectly.
Read the reasoning
The impact claim describes the behavioral result of the alleged bug. Without the HSTS cache update code, the code-level cause cannot be verified.
Create a cache file with a short expiration for a subdomain, then perform two requests to the same subdomain; the second request overwrites the parent domain's expiration time.
Read the reasoning
The reproduction steps reference a test file (testhsts.txt) and command-line invocations. The provided source does not include test files or the command-line tool's HSTS handling code.
What the report says about other projects
confirmed The UA MUST NOT modify the expiry time or the includeSubDomains directive of any superdomain matched Known HSTS Host.
“The UA MUST NOT modify the expiry time or the includeSubDomains directive of any superdomain matched Known HSTS Host.” source
confirmed Section 8.1 of RFC 6797 should say 'Update the UA's cached information for the Known HSTS Host' without the conditional about max-age and includeSubDomains conveying different information.
“Section 8.1 says: o Update the UA's cached information for the Known HSTS Host if either or both of the max-age and includeSubDomains header field value tokens are conveying information different than that already maintained by the UA. It should say: o Update the UA's cached information for the Known HSTS Host.” source
Read the original report (excerpt)
Source code
| 183 | typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, |
| 184 | struct connectdata *conn, |
| 185 | int *didwhat, |
| 186 | int select_res); |
| 187 | #endif |
| 188 | |
| 189 | #include "mime.h" |
| 190 | #include "imap.h" |
| 191 | #include "pop3.h" |
| 192 | #include "smtp.h" |
| 193 | #include "ftp.h" |
| 194 | #include "file.h" |
| 195 | #include "vssh/ssh.h" |
| 196 | #include "http.h" |
| 197 | #include "rtsp.h" |
| 198 | #include "smb.h" |
| 199 | #include "mqtt.h" |
| 200 | #include "ftplistparser.h" |
| 201 | #include "multihandle.h" |
| 202 | #include "c-hyper.h" |
| 203 | #include "cf-socket.h" |
| 204 | |
| 205 | #ifdef HAVE_GSSAPI |
| 206 | # ifdef HAVE_GSSGNU |
| 207 | # include <gss.h> |
| 208 | # elif defined HAVE_GSSAPI_GSSAPI_H |
| 209 | # include <gssapi/gssapi.h> |
| 210 | # else |
| 211 | # include <gssapi.h> |
| 212 | # endif |
| 213 | # ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H |
| 214 | # include <gssapi/gssapi_generic.h> |
| 215 | # endif |
| 216 | #endif |
| 217 | |
| 218 | #ifdef USE_LIBSSH2 |
| 219 | #include <libssh2.h> |
| 220 | #include <libssh2_sftp.h> |
| 221 | #endif /* USE_LIBSSH2 */ |
| 222 | |
| 223 | #define READBUFFER_SIZE CURL_MAX_WRITE_SIZE |
| 224 | #define READBUFFER_MAX CURL_MAX_READ_SIZE |
| 225 | #define READBUFFER_MIN 1024 |
| 226 | |
| 227 | /* The default upload buffer size, should not be smaller than |
| 228 | CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in |
| 229 | a write callback. |
| 230 | |
| 231 | The size was 16KB for many years but was bumped to 64KB because it makes |
| 232 | libcurl able to do significantly faster uploads in some circumstances. Even |
| 233 | larger buffers can help further, but this is deemed a fair memory/speed |
| 234 | compromise. */ |
| 235 | #define UPLOADBUFFER_DEFAULT 65536 |
| 236 | #define UPLOADBUFFER_MAX (2*1024*1024) |
| 237 | #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE |
| 238 | |
| 239 | #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU |
| 240 | #ifdef DEBUGBUILD |
| 241 | /* On a debug build, we want to fail hard on easy handles that |
| 242 | * are not NULL, but no longer have the MAGIC touch. This gives |
| 243 | * us early warning on things only discovered by valgrind otherwise. */ |
| 244 | #define GOOD_EASY_HANDLE(x) \ |
| 245 | (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))? TRUE: \ |
| 246 | (DEBUGASSERT(!(x)), FALSE)) |
| 247 | #else |
| 248 | #define GOOD_EASY_HANDLE(x) \ |
| 249 | ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) |
| 250 | #endif |
| 251 | |
| 252 | #ifdef HAVE_GSSAPI |
| 253 | /* Types needed for krb5-ftp connections */ |
| 254 | struct krb5buffer { |
| 255 | struct dynbuf buf; |
| 256 | size_t index; |
| 257 | BIT(eof_flag); |
| 258 | }; |