Skip to content

Escape space in mtree filename output to prevent keyword injection - #344

Merged
folbricht merged 1 commit into
masterfrom
fix-mtree-space-keyword-injection
May 18, 2026
Merged

folbricht merged 1 commit into
masterfrom
fix-mtree-space-keyword-injection

Conversation

@folbricht

Copy link
Copy Markdown
Owner

Problem

mtreeFilename() escaped backslash, #, control bytes (c < 32), and bytes > 126, but not space (0x20). mtree(5) lines are space-delimited keyword=value pairs following the filename (joined via strings.Join(attr, " ")), so an untrusted catar entry name containing a space was emitted verbatim:

usr ignore type=dir mode=0755 ...

mtree(8) then parses usr as the filename and ignore as a value-less keyword that suppresses verification of the entire subtree. optional and nochange suppress detection similarly. The attack is reachable: safeComponent() explicitly treats names with spaces as valid, and CreateSymlink passes the attacker-controlled target through the same function — a second injection vector.

Fix

Add space to the octal-escape set in mtreeFilename(), matching the escaped set used by libarchive and BSD mtree (non-printable, \, , \t, #; tab and other control bytes were already covered by c < 32). Space becomes \040, which mtree(8) readers decode back correctly, so legitimate spaced filenames still round-trip. The doc comment is updated to document the space rule and the rationale.

Tests

  • TestMtreeFilename — table covering space → \040, \, #, tab, newline, DEL, and multi-byte UTF-8, plus an untouched plain name.
  • TestMtreeFSNoKeywordInjection — drives MtreeFS.CreateDir with Name: "usr ignore" and asserts the filename is a single escaped field and no bare ignore keyword field appears.

Full suite (go test ./...) passes; binary builds.

mtreeFilename() escaped backslash, '#', control bytes, and bytes >126
but not space (0x20). mtree(5) lines are space-delimited keyword=value
pairs following the filename, so an untrusted catar entry name like
"usr ignore" was emitted verbatim, letting mtree(8) parse "ignore"
(also "optional"/"nochange") as a value-less keyword that suppresses
integrity verification of the whole subtree. Symlink targets, passed
through the same function, were a second injection vector.

Add space to the octal-escape set, matching the escaped set used by
libarchive and BSD mtree (non-printable, '\', ' ', '\t', '#'); space
becomes \040 and still round-trips through mtree(8) readers. Update
the doc comment and add tests covering the escaping and a regression
guard for the keyword-injection attack.
@folbricht
folbricht merged commit 3c55d53 into master May 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

⌂ 换网址