Control: tag -1 + patch pending
Hi Jakub,
found time to analyse this closer.
Axel Beckert wrote:
> Given that the full path including the exploit code is always shown to
> the user before the exploit actually runs, I consider the impact
> rather low:
>
> ┌────────────────────────┤ Select a file (file:./injection.deb)
> ├────────────────────────┐
> │
> │
> │ usr/share/doc/$(cowsay${IFS}pwned>/dev/tty;sleep${IFS}inf)/changelog.gz
> changelog.gz │
> │
> │
> │ <Ok> <Cancel>
> │
> │
> │
> └────────────────────────────────────────────────────────────────────────────────────────┘
And this even though $manpages (which holds a space separated list of
all files to offer) is — on purpose — unquoted when passing to the
selection program.
But the real culprit is the use of eval in lines 415, 422 and 424:
414 debug "Opening manpage file: "`printf "$mancmdline" "$PWD/$file"` #
comment
415 eval $(printf "$mancmdline" "$PWD/$file")
416 cd - >/dev/null
417 else
418 # other file (usr/share/doc)
419 debug "Opening other file: "`printf "$othercmdline" "$PWD/$return"`
# comment
420 if [[ "$return" =~ \.gz$ ]]
421 then
422 eval $(printf "gzip -dc $PWD/$return | $othercmdline")
423 else
424 eval $(printf "$othercmdline" "$PWD/$return")
425 fi
Eval is evil. Once again.
My first thought was to just exclude all files with shell special
characters, especially '$(' and friends. But
apt-file search -n \$\( | fgrep /usr/share/doc/
shows that these are actually used (and fail with debmany due to the
issue reported by Jakub):
sdlbasic: /usr/share/doc/sdlbasic/english/sections/commands/Maths/hex$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/chr$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/insert$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/lCase$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/lTrim$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/left$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/mid$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/replace$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/replaceSubstr$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/reverse$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/right$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/space$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/str$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/string$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/trim$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/typeOf$().html
sdlbasic:
/usr/share/doc/sdlbasic/english/sections/commands/Strings/uCase$().html
Additionally there are tons of files with just $ or any kind of brackets.
Just quoting them "properly" inside the eval and printf won't help as
you can always escape from there by just ending the according
quotation by adding a closing quote character into your file path.
In case of the ".gz" case in your example, it's easier to fix as you
can simply move "gzip -dc $PWD/$return |" out of the eval/printf.
But in the other cases it's less simple.
So I came up with the following fix which uses command instead of
eval, and bash pattern substitution to replace %s with the file name
instead letting printf inside an $() doing the substitution:
diff --git a/debmany/debmany b/debmany/debmany
index 7eeb68b..dfea6e9 100755
--- a/debmany/debmany
+++ b/debmany/debmany
@@ -96,6 +96,19 @@ Examples: debmany foo.deb show manpages from a local
package file foo.deb
fi
}
+replace_percent_s_and_execute() {
+ replacement="$1"
+ shift
+ declare -a cmdarr
+ cmdarr=($@)
+ debug "cmdarr before; ${cmdarr[@]}"
+ for i in ${!cmdarr[@]}; do
+ cmdarr[$i]="${cmdarr[$i]/\%s/$replacement}"
+ done
+ debug "cmdarr after; ${cmdarr[@]}"
+ command "${cmdarr[@]}"
+}
+
while [ $# -gt 0 ]
do
case $1 in
@@ -377,6 +390,7 @@ else
dpkg --fsys-tarfile "$file" | tar --wildcards -xf - $mandirs 2>/dev/null
# find all manpage files
manpages=`find usr -type f 2>/dev/null|sort|sed -e 's|\([^/]*\)$|\1 \1|'`
+ # | egrep -v '[\`\\${}*?;<>|]'
fi
while true
@@ -412,16 +426,16 @@ do
cd "$path"
fi
debug "Opening manpage file: "`printf "$mancmdline" "$PWD/$file"` #
comment
- eval $(printf "$mancmdline" "$PWD/$file")
+ replace_percent_s_and_execute "$PWD/$file" "$mancmdline"
cd - >/dev/null
else
# other file (usr/share/doc)
debug "Opening other file: "`printf "$othercmdline" "$PWD/$return"` #
comment
if [[ "$return" =~ \.gz$ ]]
then
- eval $(printf "gzip -dc $PWD/$return | $othercmdline")
+ gzip -dc "$PWD/$return" | replace_percent_s_and_execute '-'
"$othercmdline"
else
- eval $(printf "$othercmdline" "$PWD/$return")
+ replace_percent_s_and_execute "$PWD/$return" "$othercmdline"
fi
fi
else
This is now committed as
https://salsa.debian.org/debian/debian-goodies/-/commit/495eaafa94d2b4cbee4eed01cd78238e311d096b
but I'd be happy about a review before I upload.
I will also do some more testing on my side.
Regards, Axel
--
,''`. | Axel Beckert <[email protected]>, https://people.debian.org/~abe/
: :' : | Debian Developer, ftp.ch.debian.org Admin
`. `' | 4096R: 2517 B724 C5F6 CA99 5329 6E61 2FF9 CD59 6126 16B5
`- | 1024D: F067 EA27 26B9 C3FC 1486 202E C09E 1D89 9593 0EDE