File bsc1198062.patch of Package gzip.23708
From 53f56145cc4fc9f14060c0a022f502c210d175a7 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.collin@tukaani.org>
Date: Mon, 28 Mar 2022 09:35:27 -0700
Subject: [PATCH 1/6] zgrep: avoid exploit via multi-newline file names
* zgrep.in: The issue with the old code is that with multiple
newlines, the N-command will read the second line of input,
then the s-commands will be skipped because it's not the end
of the file yet, then a new sed cycle starts and the pattern
space is printed and emptied. So only the last line or two get
escaped. This patch makes sed read all lines into the pattern
space and then do the escaping.
This vulnerability was discovered by:
cleemy desu wayo working with Trend Micro Zero Day Initiative
---
zgrep.in | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
Index: gzip-1.6/zgrep.in
===================================================================
--- gzip-1.6.orig/zgrep.in
+++ gzip-1.6/zgrep.in
@@ -66,30 +66,33 @@ while test $# -ne 0; do
case $option in
(-[0123456789EFGHIKLPRTUVZabchilnoqrsuvwxyz]*[!0123456789]*)
- arg2=-\'$(expr "X$option" : 'X-.[0-9]*\(.*\)' | sed "$escape")
+ arg2=-\'$(LC_ALL=C expr "X$option" : 'X-.[0-9]*\(.*\)' |
+ LC_ALL=C sed "$escape")
eval "set -- $arg2 "'${1+"$@"}'
- option=$(expr "X$option" : 'X\(-.[0-9]*\)');;
+ option=$(LC_ALL=C expr "X$option" : 'X\(-.[0-9]*\)');;
(--binary-*=* | --[lm]a*=* | --reg*=*)
;;
(-[ABCDXdefm] | binary-* | --file | --[lm]a* | --reg*)
case ${1?"$option option requires an argument"} in
(*\'*)
- optarg=" '"$(printf '%s\n' "$1" | sed "$escape");;
+ optarg=" '"$(printf '%s\n' "$1" | LC_ALL=C sed "$escape");;
(*)
optarg=" '$1'";;
esac
shift;;
(-f?*\'*)
- optarg=" '"$(expr "X$option" : 'X-f\(.*\)' | sed "$escape")
+ optarg=" '"$(LC_ALL=C expr "X$option" : 'X-f\(.*\)' |
+ LC_ALL=C sed "$escape")
option=-f;;
(-f?*)
- optarg=" '"$(expr "X$option" : 'X-f\(.*\)')\'
+ optarg=" '"$(LC_ALL=C expr "X$option" : 'X-f\(.*\)')\'
option=-f;;
(--file=*\'*)
- optarg=" '"$(expr "X$option" : 'X--file=\(.*\)' | sed "$escape")
+ optarg=" '"$(LC_ALL=C expr "X$option" : 'X--file=\(.*\)' |
+ LC_ALL=C sed "$escape")
option=--file;;
(--file=*)
- optarg=" '"$(expr "X$option" : 'X--file=\(.*\)')\'
+ optarg=" '"$(LC_ALL=C expr "X$option" : 'X--file=\(.*\)')\'
option=--file;;
(--)
break;;
@@ -98,7 +101,8 @@ while test $# -ne 0; do
(*)
case $option in
(*\'*)
- operands="$operands '"$(printf '%s\n' "$option" | sed "$escape");;
+ operands="$operands '"$(printf '%s\n' "$option" | LC_ALL=C sed "$escape")
+ ;;
(*)
operands="$operands '$option'";;
esac
@@ -145,7 +149,7 @@ while test $# -ne 0; do
case $option in
(*\'?*)
- option=\'$(printf '%s\n' "$option" | sed "$escape");;
+ option=\'$(printf '%s\n' "$option" | LC_ALL=C sed "$escape");;
(*)
option="'$option'";;
esac
@@ -158,7 +162,7 @@ eval "set -- $operands "'${1+"$@"}'
if test $have_pat -eq 0; then
case ${1?"missing pattern; try \`$0 --help' for help"} in
(*\'*)
- grep="$grep -- '"$(printf '%s\n' "$1" | sed "$escape");;
+ grep="$grep -- '"$(printf '%s\n' "$1" | LC_ALL=C sed "$escape");;
(*)
grep="$grep -- '$1'";;
esac
@@ -209,19 +213,17 @@ do
case $i in
(*'
'* | *'&'* | *'\'* | *'|'*)
- i=$(printf '%s\n' "$i" |
- sed '
- $!N
- $s/[&\|]/\\&/g
- $s/\n/\\n/g
- ');;
+ icolon=$(printf '%s\n' "$i:" |
+ LC_ALL=C sed -e 's/[&\|]/\\&/g' -e '$!s/$/\\/');;
+ (*) icolon="$i:";;
esac
- sed_script="s|^|$i:|"
+ sed_script="s|^|$icolon|"
# Fail if grep or sed fails.
r=$(
exec 4>&1
- (eval "$grep" 4>&-; echo $? >&4) 3>&- | sed "$sed_script" >&3 4>&-
+ (eval "$grep" 4>&-; echo $? >&4) 3>&- |
+ LC_ALL=C sed "$sed_script" >&3 4>&-
) && exit $r
r=$?
test 1 -lt $r && exit $r || exit 2
Index: gzip-1.6/tests/Makefile.am
===================================================================
--- gzip-1.6.orig/tests/Makefile.am
+++ gzip-1.6/tests/Makefile.am
@@ -28,6 +28,7 @@ TESTS = \
unpack-valid \
zdiff \
zgrep-f \
+ zgrep-abuse \
zgrep-context \
zgrep-signal \
znew-k
Index: gzip-1.6/tests/zgrep-abuse
===================================================================
--- /dev/null
+++ gzip-1.6/tests/zgrep-abuse
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Show how zgrep applied to a crafted file name may overwrite
+# a selected file with chosen content. Fixed in gzip-1.12.
+
+# Copyright (C) 2022 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+# limit so don't run it by default.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+f=z
+echo test > $f || framework_failure_
+gzip $f || framework_failure_
+cp $f.gz 'zfile_crafted|
+p
+1s|.*|hacked1|
+1w hacked
+etouch .\x2fhacked2
+d
+#
+#' || framework_failure_
+
+fail=0
+
+zgrep test z*
+
+# Before, each of these would be created.
+test -f hacked && fail=1
+test -f hacked2 && fail=1
+
+Exit $fail
Index: gzip-1.6/gzexe.in
===================================================================
--- gzip-1.6.orig/gzexe.in
+++ gzip-1.6/gzexe.in
@@ -96,10 +96,11 @@ for i do
continue
fi
if test $decomp -eq 0; then
- if sed -e 1d -e 2q "$file" | grep "^skip=[0-9][0-9]*$" >/dev/null; then
+ case `LC_ALL=C sed -n -e 1d -e '/^skip=[0-9][0-9]*$/p' -e 2q "$file"` in
+ skip=[0-9] | skip=[0-9][0-9] | skip=[0-9][0-9][0-9])
echo >&2 "$0: $i is already gzexe'd"
- continue
- fi
+ continue;;
+ esac
fi
if test -u "$file"; then
echo >&2 "$0: $i has setuid permission, unchanged"
@@ -198,7 +199,7 @@ EOF
else
# decompression
skip=44
- skip_line=`sed -e 1d -e 2q "$file"`
+ skip_line=`LC_ALL=C sed -e 1d -e 2q "$file"`
case $skip_line in
skip=[0-9] | skip=[0-9][0-9] | skip=[0-9][0-9][0-9])
eval "$skip_line";;
Index: gzip-1.6/sample/zfile
===================================================================
--- gzip-1.6.orig/sample/zfile
+++ gzip-1.6/sample/zfile
@@ -1,5 +1,8 @@
#!/bin/sh
+LC_ALL=C
+export LC_ALL
+
if test $# = 0; then
echo 'zfile: file(1) for programs which may be compressed with gzexe'
echo usage: `basename $0` files...
Index: gzip-1.6/zdiff.in
===================================================================
--- gzip-1.6.orig/zdiff.in
+++ gzip-1.6/zdiff.in
@@ -59,7 +59,7 @@ while :; do
--h*) printf '%s\n' "$usage" || exit 2; exit;;
--v*) echo "$version" || exit 2; exit;;
--) shift; break;;
- -*\'*) cmp="$cmp '"`printf '%sX\n' "$1" | sed "$escape"`;;
+ -*\'*) cmp="$cmp '"`printf '%sX\n' "$1" | LC_ALL=C sed "$escape"`;;
-?*) cmp="$cmp '$1'";;
*) break;;
esac
@@ -78,7 +78,7 @@ exec 3>&1
if test $# -eq 1; then
case $1 in
*[-.]gz* | *[-.][zZ] | *.t[ga]z)
- FILE=`expr "X$1" : 'X\(.*\)[-.][zZtga]*$'`
+ FILE=`LC_ALL=C expr "X$1" : 'X\(.*\)[-.][zZtga]*$'`
gzip_status=$(
exec 4>&1
(gzip -cd -- "$1" 4>&-; echo $? >&4) 3>&- | eval "$cmp" - '"$FILE"' >&3
Index: gzip-1.6/znew.in
===================================================================
--- gzip-1.6.orig/znew.in
+++ gzip-1.6/znew.in
@@ -23,6 +23,9 @@ case $1 in
esac
PATH=$bindir:$PATH; export PATH
+LC_ALL=C
+export LC_ALL
+
version="znew (gzip) @VERSION@
Copyright (C) 2010-2013 Free Software Foundation, Inc.
This is free software. You may redistribute copies of it under the terms of