Control: retitle -1 debmany: CVE-2023-27635: shell injection

On Sun, Feb 19, 2023 at 05:47:20AM +0100, Axel Beckert wrote:
> 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.

The issue itself has CVE-2023-27635 now.

Jakub, were you able to peer review Axel's approach?

Axel, on the severity, I do not believe this will warrant a DSA. It is
sufficiently guarded already as the user will be prompted before
execution with the path.

Regards,
Salvatore

Reply via email to