1b0a65
From 69d1b3fc29677af8ade8dc15dba83f0589cb63d6 Mon Sep 17 00:00:00 2001
1b0a65
From: Lasse Collin <lasse.collin@tukaani.org>
1b0a65
Date: Tue, 29 Mar 2022 19:19:12 +0300
1b0a65
Subject: [PATCH] xzgrep: Fix escaping of malicious filenames (ZDI-CAN-16587).
1b0a65
1b0a65
Malicious filenames can make xzgrep to write to arbitrary files
1b0a65
or (with a GNU sed extension) lead to arbitrary code execution.
1b0a65
1b0a65
xzgrep from XZ Utils versions up to and including 5.2.5 are
1b0a65
affected. 5.3.1alpha and 5.3.2alpha are affected as well.
1b0a65
This patch works for all of them.
1b0a65
1b0a65
This bug was inherited from gzip's zgrep. gzip 1.12 includes
1b0a65
a fix for zgrep.
1b0a65
1b0a65
The issue with the old sed script is that with multiple newlines,
1b0a65
the N-command will read the second line of input, then the
1b0a65
s-commands will be skipped because it's not the end of the
1b0a65
file yet, then a new sed cycle starts and the pattern space
1b0a65
is printed and emptied. So only the last line or two get escaped.
1b0a65
1b0a65
One way to fix this would be to read all lines into the pattern
1b0a65
space first. However, the included fix is even simpler: All lines
1b0a65
except the last line get a backslash appended at the end. To ensure
1b0a65
that shell command substitution doesn't eat a possible trailing
1b0a65
newline, a colon is appended to the filename before escaping.
1b0a65
The colon is later used to separate the filename from the grep
1b0a65
output so it is fine to add it here instead of a few lines later.
1b0a65
1b0a65
The old code also wasn't POSIX compliant as it used \n in the
1b0a65
replacement section of the s-command. Using \<newline> is the
1b0a65
POSIX compatible method.
1b0a65
1b0a65
LC_ALL=C was added to the two critical sed commands. POSIX sed
1b0a65
manual recommends it when using sed to manipulate pathnames
1b0a65
because in other locales invalid multibyte sequences might
1b0a65
cause issues with some sed implementations. In case of GNU sed,
1b0a65
these particular sed scripts wouldn't have such problems but some
1b0a65
other scripts could have, see:
1b0a65
1b0a65
    info '(sed)Locale Considerations'
1b0a65
1b0a65
This vulnerability was discovered by:
1b0a65
cleemy desu wayo working with Trend Micro Zero Day Initiative
1b0a65
1b0a65
Thanks to Jim Meyering and Paul Eggert discussing the different
1b0a65
ways to fix this and for coordinating the patch release schedule
1b0a65
with gzip.
1b0a65
---
1b0a65
 src/scripts/xzgrep.in | 20 ++++++++++++--------
1b0a65
 1 file changed, 12 insertions(+), 8 deletions(-)
1b0a65
1b0a65
diff --git a/src/scripts/xzgrep.in b/src/scripts/xzgrep.in
1b0a65
index b180936..e5186ba 100644
1b0a65
--- a/src/scripts/xzgrep.in
1b0a65
+++ b/src/scripts/xzgrep.in
1b0a65
@@ -180,22 +180,26 @@ for i; do
1b0a65
          { test $# -eq 1 || test $no_filename -eq 1; }; then
1b0a65
       eval "$grep"
1b0a65
     else
1b0a65
+      # Append a colon so that the last character will never be a newline
1b0a65
+      # which would otherwise get lost in shell command substitution.
1b0a65
+      i="$i:"
1b0a65
+
1b0a65
+      # Escape & \ | and newlines only if such characters are present
1b0a65
+      # (speed optimization).
1b0a65
       case $i in
1b0a65
       (*'
1b0a65
 '* | *'&'* | *'\'* | *'|'*)
1b0a65
-        i=$(printf '%s\n' "$i" |
1b0a65
-            sed '
1b0a65
-              $!N
1b0a65
-              $s/[&\|]/\\&/g
1b0a65
-              $s/\n/\\n/g
1b0a65
-            ');;
1b0a65
+        i=$(printf '%s\n' "$i" | LC_ALL=C sed 's/[&\|]/\\&/;; $!s/$/\\/');;
1b0a65
       esac
1b0a65
-      sed_script="s|^|$i:|"
1b0a65
+
1b0a65
+      # $i already ends with a colon so don't add it here.
1b0a65
+      sed_script="s|^|$i|"
1b0a65
 
1b0a65
       # Fail if grep or sed fails.
1b0a65
       r=$(
1b0a65
         exec 4>&1
1b0a65
-        (eval "$grep" 4>&-; echo $? >&4) 3>&- | sed "$sed_script" >&3 4>&-
1b0a65
+        (eval "$grep" 4>&-; echo $? >&4) 3>&- |
1b0a65
+            LC_ALL=C sed "$sed_script" >&3 4>&-
1b0a65
       ) || r=2
1b0a65
       exit $r
1b0a65
     fi >&3 5>&-
1b0a65
-- 
1b0a65
2.35.1
1b0a65