CVE-2026-8784
Description
A vulnerability was detected in npitre cramfs-tools up to 2.2. Affected is the function change_file_status of the file cramfsck.c. Performing a manipulation results in symlink following. The attack requires a local approach. The exploit is now public and may be used. The patch is named b4a3a695c9873f824907bd15659f2a6ac7667b4f. It is recommended to apply a patch to fix this issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
cramfsck in cramfs-tools up to 2.2 follows a preexisting symlink at the extraction root in continue-on-error mode, enabling local attackers to write files outside the intended directory.
Vulnerability
Overview
A vulnerability was detected in npitre cramfs-tools up to version 2.2. The affected function is change_file_status in cramfsck.c. The issue is a classic symlink following weakness (CWE-59) that occurs when the tool's continue-on-error mode (-c) is used during extraction with the -x flag. If the specified output directory already exists as a symlink, cramfsck fails to properly resolve the path before writing files, leading to writes that follow the symlink to arbitrary locations on the filesystem [1][2].
Exploitation
Scenario
The attack requires local access and a crafted pre-condition. An attacker can prepare a repository or firmware bundle containing a symlink (e.g., out -> ~/.ssh/) and a seemingly benign cramfs image. When a victim runs cramfsck -c -x out rootfs.cramfs — a common workflow for extracting potentially corrupted firmware images — the tool logs an error for the failed mkdir(out) call but continues extraction due to -c mode. The subsequent file writes then traverse the symlink, allowing the attacker to place files such as pwn.txt inside the victim's ~/.ssh/ directory or other sensitive locations [1][2].
Impact
Successful exploitation allows a local attacker to write arbitrary files (with the victim's permissions) to locations outside the intended extraction directory. This could lead to unauthorized modification of user configuration files (e.g., SSH authorized_keys) or other sensitive data, depending on the attacker's control over the cramfs image contents and the victim's environment [1][2].
Mitigation
The maintainer has released a fix in commit b4a3a695c9873f824907bd15659f2a6ac7667b4f [4]. The patch introduces a warn_error() function that behaves as die() except under -c mode, where it logs the error and continues. However, critically, it also hardens the extraction path by failing on symlink-related failures or by treating the extraction root symlink as an error that cannot be safely continued. Users are advised to update to the patched version immediately [3][4].
- cramfs-tools `cramfsck -c -x` — Continue-on-Error Extraction Through Preexisting Root Symlink
- cramfs-tools `cramfsck -c -x` — Continue-on-Error Extraction Through Preexisting Root Symlink
- GitHub - npitre/cramfs-tools: Tools to create a cramfs filesystem image
- cramfsck: make die() actually die; introduce warn_error() for -c mode · npitre/cramfs-tools@b4a3a69
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <=2.2
Patches
1b4a3a695c987cramfsck: make die() actually die; introduce warn_error() for -c mode
1 file changed · +41 −11
cramfsck.c+41 −11 modified@@ -122,26 +122,50 @@ static void __attribute__((noreturn)) usage(int status) exit(status); } -static void die(int status, int syserr, const char *fmt, ...) +static void print_error(int saved_errno, int syserr, const char *fmt, va_list arg_ptr) { - va_list arg_ptr; - int save = errno; - fflush(0); - va_start(arg_ptr, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, arg_ptr); if (syserr) { - fprintf(stderr, ": %s", strerror(save)); + fprintf(stderr, ": %s", strerror(saved_errno)); } fprintf(stderr, "\n"); +} + +static void __attribute__((noreturn)) die(int status, int syserr, const char *fmt, ...) +{ + int save = errno; + va_list arg_ptr; + + va_start(arg_ptr, fmt); + print_error(save, syserr, fmt, arg_ptr); + va_end(arg_ptr); + exit(status); +} + +/* + * Non-fatal counterpart to die(): under -c (continue-on-error) it logs + * the error, folds the status into log_errors_result for the final + * exit code, and returns to the caller. Without -c it behaves the + * same as die(). Use this only at sites where it is genuinely safe + * to proceed -- typically best-effort metadata updates. Corruption + * checks and filesystem side-effect failures should use die(). + */ +static void warn_error(int status, int syserr, const char *fmt, ...) +{ + int save = errno; + va_list arg_ptr; + + va_start(arg_ptr, fmt); + print_error(save, syserr, fmt, arg_ptr); va_end(arg_ptr); if (opt_continue > 0) { log_errors_continue++; - log_errors_result |= status; + log_errors_result |= status; } else { exit(status); - } + } } static void test_super(int *start, size_t *length) { @@ -510,22 +534,28 @@ static void change_file_status(char *path, struct cramfs_inode *i) { struct utimbuf epoch = { 0, 0 }; + /* + * Metadata updates are best-effort: under -c we keep going after + * a failure (e.g. chown/utime failing when not running as root), + * which is what continue-on-error mode was introduced for. The + * extracted content itself has already been written. + */ if (euid == 0) { if (lchown(path, i->uid, i->gid) < 0) { - die(FSCK_ERROR, 1, "lchown failed: %s", path); + warn_error(FSCK_ERROR, 1, "lchown failed: %s", path); } if (S_ISLNK(i->mode)) return; if ((S_ISUID | S_ISGID) & i->mode) { if (chmod(path, i->mode) < 0) { - die(FSCK_ERROR, 1, "chown failed: %s", path); + warn_error(FSCK_ERROR, 1, "chmod failed: %s", path); } } } if (S_ISLNK(i->mode)) return; if (utime(path, &epoch) < 0) { - die(FSCK_ERROR, 1, "utime failed: %s", path); + warn_error(FSCK_ERROR, 1, "utime failed: %s", path); } }
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
6News mentions
0No linked articles in our index yet.