I’m a regular listener of Steve Gibson’s Security Now! podcast. The last several episodes (episodes 557, 558, and 559) have discussed the security implications of piping a script into Bash (or some other script interpreter, such as Ruby or Python). For example, the install instructions for Homebrew, a fantastic package manager for Macs that I use on my work laptop, offers a really slick 1-liner for installation:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
This idiom is so common that there’s an entire Tumblr site showing various examples.
So, why is this a bad idea?
Well, let’s take a step back. In the above Homebrew example, you can view the content of the script easily just by visiting https://raw.githubusercontent.com/Homebrew/install/master/install. You can read through the script, clear as day, in your browser to verify that there’s nothing fishy going on. Looks great, right?
Not so fast! Homebrew is hosted by GitHub, which we can trust. However, a number of other random scripts aren’t hosted by sites that we might inherently trust (just look at the Tumblr site for examples). If a script is hosted by a malicious site, it turns out they can easily trick the user into thinking the script is ok, when it’s really not. I’ve cooked up a simple example to show this.
Suppose I wrote a script that you might want to install:
bash <(curl -s https://research.gfairchild.com/bash_pipe/script.sh)
You can open up the script in your browser (https://research.gfairchild.com/bash_pipe/script.sh), you can see that it's clearly a friendly script, right? Wrong! If you actually run the script in bash, you get this:
$ bash <(curl -s https://research.gfairchild.com/bash_pipe/script.sh) I am a bad script! :(
I created a single simple PHP file,
script.php. I also setup an
.htaccess file to rewrite the URL so that it really does look like you're downloading a bash script. The magic happens in
<?php if(substr($_SERVER['HTTP_USER_AGENT'], 0, 4) === 'curl' or substr($_SERVER['HTTP_USER_AGENT'], 0, 4) === 'Wget') echo 'echo "I am a bad script! :("'; else echo 'echo "I am a good script! :)"'; ?>
RewriteEngine On RewriteRule ^script\.sh$ script.php [NC]
Here, all I do is detect the HTTP request's user agent. In short, the user agent is tacked on to most HTTP requests and tells the server how it is being contacted. For example, I'm running Firefox 46, and my user agent string is this:
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:46.0) Gecko/20100101 Firefox/46.0
curl and wget have their own user agents. My system curl and wget user agents are
Wget/1.15 (linux-gnu), respectively. PHP makes it really easy to check the user agent, so all I have to do is check to see if the user is using curl or wget. If they are, I send them the malicious script. If they aren't using curl or wget, I show them the good script. That way, if a user checks the script in their browser but doesn't check it once it's downloaded using curl, my malicious attempt is successful!
So what do I do?
These sorts of 1-liner scripts aren't inherently bad. Installing Homebrew without their install script would be a nightmare! So what do you do?
Simple: download the file using curl or wget, inspect it on the machine it's going to be installed on, and then run the script. Don't take your browser's word for it. Keep in mind that the script might actually need to download/install more things (perhaps the script itself curls a file), so if you want to thoroughly vet a script, it may be a small rabbit hole. But it's obviously doable. In the case of Homebew, you'd do this:
$ wget https://raw.githubusercontent.com/Homebrew/install/master/install $ vim install # review the install script for any malicious content $ /usr/bin/ruby install
Ultimately, as Steve Gibson says, we have to trust someone or else we'll never use a computer. However, it's important to recognize when attacks are relatively easy and prioritize spending time analyzing those situations. Obviously, I'm not going to break open my CPU and use a scanning electron microscope to ensure it's doing what it says it is. But there are small, relatively quick things we can do to help maintain a secure environment, and not blindly piping scripts into Bash is one of those things.