407 lines
12 KiB
Diff
407 lines
12 KiB
Diff
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(-)
|
||
|
||
diff --git a/zgrep.in b/zgrep.in
|
||
index 345dae3..bdf7da2 100644
|
||
--- a/zgrep.in
|
||
+++ b/zgrep.in
|
||
@@ -222,9 +222,13 @@ do
|
||
'* | *'&'* | *'\'* | *'|'*)
|
||
i=$(printf '%s\n' "$i" |
|
||
sed '
|
||
- $!N
|
||
- $s/[&\|]/\\&/g
|
||
- $s/\n/\\n/g
|
||
+ :start
|
||
+ $!{
|
||
+ N
|
||
+ b start
|
||
+ }
|
||
+ s/[&\|]/\\&/g
|
||
+ s/\n/\\n/g
|
||
');;
|
||
esac
|
||
sed_script="s|^|$i:|"
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|
||
|
||
From 9973b95d1ea12c3372805074f152bec66d81a9b4 Mon Sep 17 00:00:00 2001
|
||
From: Jim Meyering <meyering@fb.com>
|
||
Date: Mon, 28 Mar 2022 09:35:27 -0700
|
||
Subject: [PATCH 2/6] zgrep: add NEWS and tests for this exploitable bug
|
||
|
||
* tests/zgrep-abuse: New file, based on PoC by cleemy desu wayo.
|
||
* tests/Makefile.am (TESTS): Add it.
|
||
* NEWS: Mention the exploit.
|
||
The bug appears to have been present since the beginning.
|
||
---
|
||
NEWS | 3 +++
|
||
tests/Makefile.am | 1 +
|
||
tests/zgrep-abuse | 43 +++++++++++++++++++++++++++++++++++++++++++
|
||
3 files changed, 47 insertions(+)
|
||
create mode 100755 tests/zgrep-abuse
|
||
|
||
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
||
index d09672e..5f148d6 100644
|
||
--- a/tests/Makefile.am
|
||
+++ b/tests/Makefile.am
|
||
@@ -36,6 +36,7 @@ TESTS = \
|
||
z-suffix \
|
||
zdiff \
|
||
zgrep-f \
|
||
+ zgrep-abuse \
|
||
zgrep-context \
|
||
zgrep-signal \
|
||
znew-k
|
||
diff --git a/tests/zgrep-abuse b/tests/zgrep-abuse
|
||
new file mode 100755
|
||
index 0000000..f1d6699
|
||
--- /dev/null
|
||
+++ b/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
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|
||
|
||
From 7801e55ae389b24dfda1d29bf7cadb434472c8dd Mon Sep 17 00:00:00 2001
|
||
From: Paul Eggert <eggert@cs.ucla.edu>
|
||
Date: Mon, 28 Mar 2022 09:35:27 -0700
|
||
Subject: [PATCH 3/6] zgrep: port to POSIX sed
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
* zgrep.in (res): When escaping the file name do not rely on GNU
|
||
sed’s extension to POSIX with respect to s/.../\n/. Instead, use
|
||
features that should also work with AIX and/or Solaris sed. This is
|
||
simpler anyway, and would have prevented the recently-fixed bug.
|
||
---
|
||
zgrep.in | 15 ++++-----------
|
||
1 file changed, 4 insertions(+), 11 deletions(-)
|
||
|
||
diff --git a/zgrep.in b/zgrep.in
|
||
index bdf7da2..6a16dd1 100644
|
||
--- a/zgrep.in
|
||
+++ b/zgrep.in
|
||
@@ -220,18 +220,11 @@ do
|
||
case $i in
|
||
(*'
|
||
'* | *'&'* | *'\'* | *'|'*)
|
||
- i=$(printf '%s\n' "$i" |
|
||
- sed '
|
||
- :start
|
||
- $!{
|
||
- N
|
||
- b start
|
||
- }
|
||
- s/[&\|]/\\&/g
|
||
- s/\n/\\n/g
|
||
- ');;
|
||
+ icolon=$(printf '%s\n' "$i:" |
|
||
+ sed -e 's/[&\|]/\\&/g' -e '$!s/$/\\/');;
|
||
+ (*) icolon="$i:";;
|
||
esac
|
||
- sed_script="s|^|$i:|"
|
||
+ sed_script="s|^|$icolon|"
|
||
|
||
# Fail if grep or sed fails.
|
||
r=$(
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|
||
|
||
From ce2bf56a5e3e1814c08513c2d4141a50041e1ae4 Mon Sep 17 00:00:00 2001
|
||
From: Paul Eggert <eggert@cs.ucla.edu>
|
||
Date: Mon, 28 Mar 2022 09:35:27 -0700
|
||
Subject: [PATCH 4/6] gzip: mention when fixed bugs were introduced
|
||
|
||
---
|
||
NEWS | 2 ++
|
||
1 file changed, 2 insertions(+)
|
||
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|
||
|
||
From 42641236d0bb239768794eec93a86c42cbfc2f6a Mon Sep 17 00:00:00 2001
|
||
From: Paul Eggert <eggert@cs.ucla.edu>
|
||
Date: Mon, 28 Mar 2022 09:35:27 -0700
|
||
Subject: [PATCH 5/6] gzexe: optimize out a grep
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
* gzexe.in: Avoid an unnecessary invocation of ‘grep’,
|
||
by using sed instead. Also, look only for at-most-3-digit numbers,
|
||
for consistency with the rest of the script.
|
||
---
|
||
gzexe.in | 7 ++++---
|
||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||
|
||
diff --git a/gzexe.in b/gzexe.in
|
||
index 04b06a9..1a691e0 100644
|
||
--- a/gzexe.in
|
||
+++ b/gzexe.in
|
||
@@ -91,10 +91,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 `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])
|
||
printf >&2 '%s\n' "$0: $i is already gzexe'd"
|
||
- continue
|
||
- fi
|
||
+ continue;;
|
||
+ esac
|
||
fi
|
||
if test -u "$file"; then
|
||
printf >&2 '%s\n' "$0: $i has setuid permission, unchanged"
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|
||
|
||
From 526466454cef8dbfcf552e16a171a44f181c8ca0 Mon Sep 17 00:00:00 2001
|
||
From: Paul Eggert <eggert@cs.ucla.edu>
|
||
Date: Mon, 28 Mar 2022 09:35:27 -0700
|
||
Subject: [PATCH 6/6] maint: use C locale more often
|
||
|
||
* gzexe.in, zdiff.in, zgrep.in:
|
||
Run expr and sed in the C locale when it might help to avoid
|
||
undefined behavior on non-GNU platforms.
|
||
* sample/zfile, znew.in: Run in the C locale, for simplicity and
|
||
to avoid undefined behavior on non-GNU platforms.
|
||
---
|
||
gzexe.in | 4 ++--
|
||
sample/zfile | 3 +++
|
||
zdiff.in | 4 ++--
|
||
zgrep.in | 29 +++++++++++++++++------------
|
||
znew.in | 3 +++
|
||
5 files changed, 27 insertions(+), 16 deletions(-)
|
||
|
||
diff --git a/gzexe.in b/gzexe.in
|
||
index 1a691e0..5fc7204 100644
|
||
--- a/gzexe.in
|
||
+++ b/gzexe.in
|
||
@@ -91,7 +91,7 @@ for i do
|
||
continue
|
||
fi
|
||
if test $decomp -eq 0; then
|
||
- case `sed -n -e 1d -e '/^skip=[0-9][0-9]*$/p' -e 2q "$file"` in
|
||
+ 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])
|
||
printf >&2 '%s\n' "$0: $i is already gzexe'd"
|
||
continue;;
|
||
@@ -203,7 +203,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";;
|
||
diff --git a/sample/zfile b/sample/zfile
|
||
index 6b4514c..d6e7a59 100755
|
||
--- a/sample/zfile
|
||
+++ b/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...
|
||
diff --git a/zdiff.in b/zdiff.in
|
||
index 012024e..eb4752b 100644
|
||
--- a/zdiff.in
|
||
+++ b/zdiff.in
|
||
@@ -59,7 +59,7 @@ do
|
||
--h*) printf '%s\n' "$usage" || exit 2; exit;;
|
||
--v*) printf '%s\n' "$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
|
||
@@ -103,7 +103,7 @@ case $file2 in
|
||
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
|
||
diff --git a/zgrep.in b/zgrep.in
|
||
index 6a16dd1..2cb2426 100644
|
||
--- a/zgrep.in
|
||
+++ b/zgrep.in
|
||
@@ -64,30 +64,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;;
|
||
@@ -96,7 +99,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
|
||
@@ -169,7 +173,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
|
||
@@ -182,7 +186,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
|
||
@@ -221,7 +225,7 @@ do
|
||
(*'
|
||
'* | *'&'* | *'\'* | *'|'*)
|
||
icolon=$(printf '%s\n' "$i:" |
|
||
- sed -e 's/[&\|]/\\&/g' -e '$!s/$/\\/');;
|
||
+ LC_ALL=C sed -e 's/[&\|]/\\&/g' -e '$!s/$/\\/');;
|
||
(*) icolon="$i:";;
|
||
esac
|
||
sed_script="s|^|$icolon|"
|
||
@@ -229,7 +233,8 @@ do
|
||
# 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>&-
|
||
) || { r=$?; test $r -lt 2 && r=2; }
|
||
test 256 -le $r && r=$(expr 128 + $r % 128)
|
||
exit $r
|
||
diff --git a/znew.in b/znew.in
|
||
index 45d0764..ea76a22 100644
|
||
--- a/znew.in
|
||
+++ b/znew.in
|
||
@@ -18,6 +18,9 @@
|
||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
||
+LC_ALL=C
|
||
+export LC_ALL
|
||
+
|
||
version="znew (gzip) @VERSION@
|
||
Copyright (C) 2010-2018 Free Software Foundation, Inc.
|
||
This is free software. You may redistribute copies of it under the terms of
|
||
--
|
||
2.35.1.677.gabf474a5dd
|
||
|