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.)