bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Bash is incorrectly and inconsistently expanding tilde in $PATH


From: Nikolay Aleksandrovich Pavlov (ZyX)
Subject: Bash is incorrectly and inconsistently expanding tilde in $PATH
Date: Thu, 04 May 2017 01:40:50 +0300

configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: x86_64-pc-linux-gnu-gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' 
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-pc-linux-gnu' 
-DCONF_VENDOR='pc' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL 
-DHAVE_CONFIG_H   -I. -I./include -I. -I./include -I./lib  
-DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
 -DSTANDARD_UTILS_PATH='/bin:/usr/bin:/sbin:/usr/sbin' 
-DSYS_BASHRC='/etc/bash/bashrc' -DSYS_BASH_LOGOUT='/etc/bash/bash_logout' 
-DNON_INTERACTIVE_LOGIN_SHELLS -DSSH_SOURCE_BASHRC -DUSE_MKTEMP -DUSE_MKSTEMP 
-O2 -march=native -mtune=native -pipe
uname output: Linux zyx-desktop 4.10.12-gentoo #1 SMP Tue Apr 25 19:08:41 MSK 
2017 x86_64 AMD FX(tm)-6200 Six-Core Processor AuthenticAMD GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 4.3
Patch Level: 48
Release Status: release

Description:
        If $PATH in bash contains ~ (e.g. `PATH='~/bin'`) it is incorrectly 
treated 
        as if $HOME is present.

        This may even present a small security risk under certain 
circumstances: 
        e.g. consider an unexperienced admin which has `PATH="~/bin:$PATH"` in 
his 
        bashrc. He received an archive with potentially malicious software and 
wants 
        to analyze it. To do this he created a new directory, made it current 
and 
        unpacked archive to the place. After that he found that archive 
contains 
        executables `~/bin/init.sh` and `~/bin/python3`. So to determine what 
this 
        software targets he opens `~/bin/init.sh` like this: `nvim 
\~/bin/init.sh`. 
        And whoops, he just has some keylogger running as his user: he has 
        https://github.com/Shougo/denite.nvim installed for his nvim so nvim 
needs 
        python3 and just executed `./~/bin/python3`, because the only 
application 
        which expands `~/` in `$PATH` is bash and Neovim python provider is 
using 
        `system([])` form which calls in libuv directly.

        Of course, analyzing potential malware not in a VM is unwise and it 
requires 
        a combination of factors to make the attack successfull. Specifically 
these 
        factors are:

        - Admin needs to have `~/bin` in `$PATH` and not `$HOME/bin`, 
preferably at 
          near the start of the `$PATH`.
        - Attacker needs to be able to somehow put an executable to `./~/bin/…`.
        - Attacker needs to make admin run something not with bash while 
current 
          directory is still set to where executables were put to.

          Though last part is not too hard: `#!/bin/sh` scripts are using 
`dash` in 
          debian and it does not expand tilde. Neovim has run-without-a-shell 
          feature for functions like `system()`, `jobstart()`, etc and it is 
          actively used in a plugins where compatibility with Vim does not 
matter.

        ---

        Second point behind disabling this behaviour is inconsistency. Consider 
        another use-case: user has compiled custom Python and wants to use it 
in 
        Neovim. He has `~/bin` in `$PATH`, put link to his Python installation 
        there, verified that `python --version` shows correct output and 
started 
        Neovim. Suddenly it appears that Neovim is still using system Python. 
He 
        checked that `:!python --version` in Neovim is still his custom version 
        (`:!` uses shell) and now goes to Neovim bug tracker and wastes a lot 
of 
        time waiting for the reply and makes other people waste time deducing 
the 
        solution. If his configuration was correct not only in bash there would 
be 
        no such waste on either side, but bash hides the incorrectness.

        ---

        Some note: I have found

        > nn. Bash no longer expands tildes in $PATH elements while in Posix 
mode.

        in the 4.4 changelog, but it does not look like it is solving the 
issue. 
        What I am talking about is that tilde must *never* be expanded in 
`$PATH`.

Repeat-By:
        Create script.sh with the following contents:

        ```shell
        dir="$(mktemp -d)"
        cd "$dir"
        mkdir -p home/bin
        mkdir -p \~/bin
        HOME="$dir/home"
        printf '#!/bin/sh\necho script'     > home/bin/script
        printf '#!/bin/sh\necho script2'    > home/bin/script2
        printf '#!/bin/sh\necho vulnerable' > \~/bin/script
        chmod a+x home/bin/script
        chmod a+x home/bin/script2
        chmod a+x \~/bin/script
        PATH='~/bin'
        if test $# -gt 0 ; then
            "$@"
        else
            script
        fi
        cd /
        /bin/rm -r "$dir"
        ```

        Run it like `bash script.sh`, it will print `script`.

Additional information:
        To defend a point raised in the title that this behaviour is 
“inconsistent” 
        I have collected results of various different kind of invocations of 
the 
        above script to show places where that bash behaviour is not consistent 
with 
        other applications:

        ```
        % bash script.sh
        script
        % dash script.sh
        vulnerable
        % posh script.sh
        vulnerable
        % sh script.sh =fish -c script
        vulnerable
        % zsh script.sh
        vulnerable
        % mksh script.sh
        vulnerable
        % ksh script.sh
        vulnerable
        % echo 'main() { execlp("script", "script", "script"); }' > prog.c
        % sh script.sh =tcc -run $PWD/prog.c
        vulnerable
        % sh script.sh =vim --cmd 'echo executable("script2")' --cmd qa
        0
        % sh script.sh =nvim --cmd 'echo system(["script"])' --cmd qa
        vulnerable
        % sh script.sh =python -c 'from subprocess import check_call ; 
check_call(["script"])'
        vulnerable
        % sh script.sh =perl -e 'system "script", "x"'
        vulnerable
        ```

        (Note: script may be run as-is in zsh only, replace `=foo` with 
`"$(which 
        foo)"` in other shells. Leaving as-is because `=…` is more readable.)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]