In the Jelly Engineering chat room, during the lulls in cat-gif sharing, a topic related to programming occasionally comes up. A few weeks ago, we started talking about the havoc one could wreak on an unsuspecting person’s system via the shell. Jerks With Terminals, we called it. There’s the obvious (“How do you open that file? Type ‘sudo rm -rf /’”), but if the target has some knowledge of *nix type systems, you have to get a little creative.

*note – modern systems are too smart for this one to actually work

By the way, I want to stress that these are all terrible things that you should not do.

The Fork Bomb

The first idea that came up was the innocent sounding yes command, which is an infinite loop of printing “y” to your screen. Then someone presented this little gem:

:(){ :|: & };:

My immediate reaction was: “Huh? That random collection of punctuation can’t possibly be that bad, can it?” It’s deceiving. It uses a griefing technique called a fork bomb, along with some good old fashioned obfuscation.

So what happens when you enter that command? I don’t recommend trying it for yourself unless you want your system to slow to a crawl. One step up from an infinite loop, it will spawn an infinite number of processes—or at least as many as your system can create.

We can get a better idea of how it works by rewriting it:

:() {
     : | : &
}
:

Okay, maybe that’s not much better, but we can see now that it’s defining a shell function, albeit one with the strange name “:”. What if we give it a more readable name?

foo() {
    foo | foo &
}
foo

Much easier to see now. We’re defining a function and then calling that function. Simple enough. The function calls itself (twice). It’s easy to see already that this is going to cause some problems, what with the infinite recursion and all. But that can’t be the only problem, or we could have done this:

func() {
    func &
}
func

That, however, is a relatively harmless function that won’t do much at all. Go ahead and try it. The program runs for a very short time and exits/crashes once it’s filled up its stack space. The rest of the system isn’t affected at all. If we really want infinite regression, we need an infinite stack space, and that’s where we get to the “fork” part of the fork bomb: func | func &

The pipe command pipes the output from the first function into the second, but we don’t care about that. What we do care about is how the shell achieves this. Since both functions have to be running at the same time, the shell forks two new processes and runs both of them. Since all the function is doing is calling itself twice, we get an exponential growth in the number of processes. And like rice grains on a chessboard, it doesn’t take long to consume all the system resources.

That ampersand is important as well. It causes the second invocation to be run in the background, ensuring that it keeps running and forking new processes even if someone hits control-C to attempt to stop it.

Kindler And Gentler

Jerks With Terminals isn’t really about crippling someone’s system, though. What if you just want to gently encourage someone not to leave their workstation unlocked? Here’s one non-destructive way:

while true; do head -30 /dev/random; sleep 60; done &

Another infinite loop, although this one is kind enough to spend most of its time sleeping. Every 60 seconds, it wakes up and spits out the first 30 lines of /dev/random, which is a file that, well, has random bits in it. The loop is running in the background, so you wouldn’t know someone had done something unless you saw the command. Type that in someone’s terminal, ctrl-L to clear the screen, and they’ll be none the wiser. It would be especially annoying if they’re trying to edit a file. Maybe put this in ~/.bash_profile:

alias vi='while true; do sleep 60; head -30 /dev/random; done & vi'

If your, we’ll call them “friend”, is using OS X, there’s some additional fun built in to the system. The say command takes in text and passes it to the text-to-speech synthesizer. We can do:

while true; do sleep 60; say 'Lock your terminal'; done &

But that’s a little on the nose. We could be a little more subtle and get some words from a list available on the web:

curl https://raw2.github.com/atebits/Words/master/Words/en.txt |
while read line; do sleep 60; say $line; done &

But that’s not sufficiently annoying enough.  You might notice pretty quickly if your computer says a word every minute. We can do better than that. Let’s add in some randomness, courtesy of the bash variable $RANDOM, and let’s also make sure the process keeps running after the terminal closes:

nohup bash -c 'curl https://raw2.github.com/atebits/Words/master/Words/en.txt | while
read line; do sleep $[ (( $RANDOM % 10 ) + 1) * 60 ]s; say $line;
done' &

Now the computer will wait between 1 and 11 minutes, and then say the next word on the list, confusing its user in the process. Mission accomplished.

By now you should have all the tools you need to be a Jerk With a Terminal. Just remember to use your powers for good, not evil. As a parting gift, here’s another variant that doesn’t require a file from the internet. I’ll leave it as an exercise for the reader to figure out what it does. Or you can just run it.

nohup bash -c 'files=($(find ~ -maxdepth 2 -exec basename {} \;));
while true; do sleep $[ (( $RANDOM % 10 ) + 1) * 60 ]s; say
${files[RANDOM % ${#files[@]}]}; done' > /dev/null 2>&1 &



Tagged with: