Hacker News new | past | comments | ask | show | jobs | submit login
A proof-of-concept of a vulnerability in custom shell prompt scripts (github.com/njhartwell)
195 points by njhartwell on March 17, 2017 | hide | past | favorite | 52 comments



The "official" git prompt[1] is safe on Bash; the script doesn't run.

[1]: https://github.com/git/git/blob/master/contrib/completion/gi...


In particular see the long comment at line 324.


> If the shell would expand the contents of PS1 when drawing the prompt, a raw ref name must not be included in PS1. This protects the user from arbitrary code execution via specially crafted ref names. For example, a ref named 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the shell to execute 'sudo rm -rf /' when the prompt is drawn.

[1]: https://github.com/git/git/blob/9d77b0405ce6b471cb5ce3a90436...


It looks like it used to, but was fixed by this patch:

https://github.com/git/git/commit/8976500cbbb13270398d3b3e07...


Nothing gets past Linus


That code appears to be written by someone named Richard Hansen. I don't even see Linus in the git blame output of that file.


Tab autocompletion looks to be affected too, at least on my system. Create a branch called 'complete_$(./foo)'. Then type 'git checkout comp<TAB>'. This gets autocompleted to

    git checkout complete_$(./foo)
If you hit Enter at this point without thinking you'll end up running './foo'. It should have expanded instead to something like

    git checkout 'complete_$(./foo)'
or

    git checkout complete_\$\(./foo\)


Zsh seems to do the escaping correctly there.


Confirming what GP said on version 5.3.1, not seeing any escaping

    git checkout <Tab>
    git checkout $(./pwd)


Escaped here. How are you configuring your completion?

    -% zsh -f
    voi% echo $ZSH_VERSION
    5.3.1
    voi% autoload -U compinit
    voi% compinit
    voi% git checkout <tab><tab>\$\(./pw3n\)
    Already on '$(./pw3n)'
    Your branch is up-to-date with 'origin/$(./pw3n)'.
    voi% git checkout com<tab>plete_\$\(./foo\)
    Switched to branch 'complete_$(./foo)'


I can confirm as well


It's always cute to find new vectors for command injection. Really anytime you mix data and commands this type of crap is bound to happen (ex: sql injection).

Still not as worrisome to me as the full scripts that get executed by package managers when you install dependencies. Effectively every time you run something like "npm install" you're putting your faith in the entire tree of ancestor dependencies as any of them could have a pwnage script as a post install.


More often than not, building and deploying is secondary for developers to writing the library itself, if that. Build scripts and installation don't get the same treatment and care the library does.

The is one of the primary reasons why I prefer distribution-packaged libraries to pulling a library and its dependencies directly from developers. Staleness is a small price to pay relative to having at least two sets of eyes glance over the source and not execute arbitrary build scripts on my system.


Cute idea. I'm willing to bet that the Bash script I wrote for some of my co-workers is vulnerable. I guess that's something I should find out ... by making their computers do stuff.

However, I currently use Fish shell myself and it seems to be safe: http://imgur.com/a/rjocA


Maybe someone could craft a similar vulnerability for Fish.


Confirmed that default bash-it Git shell prompt is vulnerable on OS X.

https://github.com/Bash-it/bash-it


On fish and omf here, not vulnerable. I suppose I should test fisherman on my other machine as well.


I've confirmed fisherman is safe on my machine.


Omf is also safe


TIL. Never expected bash would do an expansion on PS1 by default. Thanks a bunch.

bash-powerline is affected. I've sent out a PR: https://github.com/riobard/bash-powerline/pull/12/

My own fork (with some additional features) is already patched: https://github.com/Hexcles/bash-powerline


Since branch name parsing is done by a lot of scripts, and I can't control the default branch name, I've installed this global post-checkout hook for now until I can stop panicking and think clearly.

    #!/bin/sh
    prev_head=$1
    new_head=$2
    changing_branch=$3

    REPO_DIR="$(git rev-parse --show-toplevel 2>/dev/null)"

    if [ "$changing_branch" = 1 -a "$prev_head" = "0000000000000000000000000000000000000000" ]; then
        new_head_name="$(git name-rev --name-only "$new_head"|sed -e 's|`|_|g' -e 's|$(|_|g')"
        new_head_name_untaint="$(git name-rev --name-only "$new_head"|sed -e 's|`|x|g' -e 's|$(|x|g')"
        if [ "$new_head_name" != "$new_head_name_untaint" ]; then
            echo "
    DANGER! INSECURE BRANCH NAME.
    ">&2
            git name-rev --name-only "$new_head"

            echo "
    MOVING $REPO_DIR TO TRASH.
    ">&2
            mkdir -p ~/.Trash
            mv "$REPO_DIR" "$HOME/.Trash/${REPO_DIR##*/}.$(date +"%Y%m%d%H%M%S")"
        fi
    fi


Dang. Unfortunately I am not vulnerable, and I use default ohmyzsh.

This is a neat bug, though. Lots of package managers have similar problems, and I would not be surprised if there's a lot of git/shell/environment problems left to find.


Why is it unfortunate that you're not vulnerable? Isn't that a good thing?


He's an applications security researcher. That stuff is fun for him.



Are you not going to give a reason?


Yeah I guess it was presented without a reason.

Code quality is pretty much the reason though. The oh-my-zsh maintainer himself even wrote a post once upon a time about how not to run an OSS project.

Prezto forked the project and cleaned up everything quite nicely a long time ago.


lol I'm using prezto, and I was pwn'd! Looks like OMZ is doing things nicely where prezto fails :)


Neat. My fancy Git prompt is a program I wrote in C, though. I'm sure it's vulnerable to something, but not to this. Maybe it should escape shell metacharacters in branch names, though.


Nice find. Luckily I am hyper-paranoid about escaping, so even though my custom jankery is probably not as performant as it could be, it's not vulnerable to this or similar exploits.


This is worrisome. Even carelessly trying to checkout a branch named like that is vulnerable without a prompt being involved:

    git co -b foo <Tab> to complete branch names
    git co -b foo $(./pw3n) <- execution right here


Liquid Prompt [0] seems safe.

[0] https://github.com/nojhan/liquidprompt


My OSX shell prompt is vunerable, I'm using zsh, oh-my-zsh, and powerlevel9k/powerlevel9k as my theme.


Forgive me if I'm misunderstanding the vulnerability, but isn't this one of Raymond Chen's "It rather involved being on the other side of this airtight hatchway" exploits? That is, any commands you run has the same permissions as you have.

Now, I understand that you may not intimately understand 100% the code of the commands and shell extensions you use, but is that a "vulnerability"?


The privilege escalation is from "can push code to a repository that you pull from" to "can run code as you". The idea is that you trust the shell extension you're using, but a particular git repo exploits a vulnerability in that shell extension.

git cloning code vs. running code is sometimes a security boundary and sometimes not. For instance, if you go cd into that directory and then run make or ./configure or ./setup.py install or docker run or something, then yeah, you've removed that boundary. But in general, it's reasonable to keep the boundary there. Perhaps you're doing code review of code by an untrusted author, either of someone else's project or of a pull request to your own project. Perhaps you're packaging up the software to run it as a less-privileged account. Perhaps you're a sysadmin helping a user figure something out. And so forth.


Thanks for the explanation.


Cloning a repository and cd'ing into it should always be safe, no matter what that repository is. This vulnerability makes it not safe.


It wouldn't even require an explicit clone. An attacker could send you a zip file containing a malicious .git directory, and then you'd lose the moment you cd'd into the unzipped directory to check it out.


If allows an attacker to execute arbitrary code in a situation where you wouldn't expect that (such as merely CDing to an attacker-controlled directory), then yeah of course it's a vulnerability.


Yes, this is more like the "a PDF that runs arbitrary commands when you open it" class.


Confirmed that the official git prompt for Fish is not vulnerable.


edit: nevermind - pbkac, does not appear to be vulnerable !

Did you try creating a branch called "(./pw3n)" ?

I'm running fish with git-radar. I'm not vulnerable to that specific attack but to a similar one with the branch name (./pw3n)


ohmyzsh `pure` theme is vulnerable, most of the other themes seems fine to me, but no theme is safe when using ohmyzsh in tmux.


only allowing know characters is a simple fix: branch=${BRANCH//[^a-z0-9\/]/-}


git for windows seems to be fine.


bash-git-prompt is vulnerable.


ohmyzsh is vulnerable.


Didn't work for me - I'm using oh-my-zsh in iTerm2, and I'm just using the git plugin & a custom theme, nothing else. Maybe we're using different plugins?


Really? As far as I can tell, the `git_current_branch` function in `lib/git.zsh` is fine.


ohmyzsh seems to be not vulerable, but the powerlevel9k theme is


Mine isn't, YMMV.


which theme? I'm using OSX, iTerm2, ohmyzsh, with the bullet train theme, and the exploit doesn't work for me




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: