curl · Report #3973194
49: Cookie-jar save transfers group access to a different GID
How it was found
Summary
Curl_fopen() copies the old cookie jar's permission bits onto a newly created temp file, but the temp file may receive a different GID (the process's primary group instead of the old file's group). After rename, group-permission bits that were intended for one group now authorize a different group. A local attacker in the new group who could not access the jar before curl ran can read persisted cookies (mode 0640) or inject cookies (mode 0660) afterward, without needing write access to the directory.
This regresses the safety property introduced for CVE-2022-32207. The original fix (20f9dd6bae) created the temp file as 0600 and applied the old mode via fchmod() only when both UID and GID matched. Commit 03cb1ff4d629 (curl 8.5.0) removed that guard entirely. Commit 8deaf04febba (curl 8.20.0) re-added a UID check but never restored the GID check.
Root cause
In lib/curl_fopen.c:
if(sb.st_uid != geteuid())
sb.st_mode = 0;
/* ... */
fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR | sb.st_mode);
Only UID is guarded. The old file's group bits are passed through to open() unconditionally. On Linux, in an ordinary (non-setgid) directory, the kernel assigns the process's primary GID to the new file. If that GID differs from the old file's GID, the surviving group bits now authorize a different set of users. cookie_output() then renames the temp file over the jar, making the change permanent.
Affected versions
curl 8.5.0 through 8.21.0, plus affected 8.22.0 prerelease/development versions. The partial correction in 8.20.0 does not validate the GID.
Reproduction
- Build an affected curl release, starting with 8.5.0, on a POSIX system where newly created files normally receive the process's effective or primary GID.
- Create two groups, group A and group B.
- Create a victim user whose primary group is group B and who is also a member of group A.
- Create an attacker user who belongs to group B but not group A.
- Create a directory owned by the victim that both users can traverse but only the victim can modify.
- Inside that directory, create a valid cookie jar owned by the victim and group A with mode
0640, and place a recognizable test cookie in it. - Confirm that the attacker cannot read the cookie jar and cannot write to its containing directory.
- Run curl as the victim with a nonrestrictive-enough umask such as
0022, loading and saving the same jar throughCURLOPT_COOKIEFILEandCURLOPT_COOKIEJAR, or the equivalent-b jar -c jaroptions. - Allow curl to complete a benign request so that it writes a sibling temporary file and renames that file over the original cookie jar.
- Inspect the replacement file's UID, GID, and mode.
- Observe the vulnerable result: the owner and
0640mode are retained, but the GID changes from group A to the victim's primary group B. - Confirm that the attacker can now read the replacement jar and recover the recognizable cookie despite having no access before curl saved it.
- Repeat the setup with an original cookie jar owned by group A at mode
0660, and run curl as the victim with a umask such as0002. - Confirm that the replacement jar receives group B while retaining group-write permission.
- As the attacker, append a syntactically valid cookie to the jar, then run curl again as the victim with that jar as input and output.
- Confirm that curl loads and persists the attacker-injected cookie.
- Repeat the read test with a restrictive
0077umask and verify that the replacement becomes0600, preventing attacker access. - Repeat with the original jar already assigned to group B and verify that curl does not introduce access for a new principal.
- Repeat in a setgid directory that causes the sibling file to inherit group A and verify that the attacker remains unable to read it.
- Confirm a fix by creating the sibling as
0600, inspecting its actual ownership, and copying group permission bits only when its GID matches the original file's GID. - Repeat the mismatched-GID tests and verify that saving the jar grants neither new read nor new write access to the attacker.
Expected result
Replacing a cookie jar must not transfer its group permissions to a different group. Users who could not access the original file must not gain access after curl saves it.
Vulnerable result
Curl copies the old mode bits to a sibling file without preserving or validating its GID. After rename, the same group permissions apply to a different group, granting new local read or write access.
Impact
- Confidentiality: a local user who could not read the cookie jar before can read persisted cookies after curl saves.
- Integrity: with group-write bits, the attacker can inject cookies that curl subsequently loads and re-saves.
- The attacker does not need write access to the directory containing the jar.
The preconditions are restrictive (multi-user system, mismatched GIDs, surviving group bits, traversable path), so I expect Low severity at most.
Suggested fix
Create the temp file as 0600. After creation, fstat() the descriptor and copy group permission bits only when the temp file's GID matches the old file's GID. This restores the safety property from the original CVE-2022-32207 fix.
Impact
Summary:
A local attacker who belongs to the replacement file’s new group may gain access to a cookie jar they previously could not read or modify. They can potentially steal authentication cookies and hijack sessions or inject cookies that curl later sends to remote services, even without write permission to the directory containing the jar.