May 10, 2005

Defusing a bash forkbomb in netbsd

What happened...
Some one posted on atomicmpc about forkbomb. This lead to me googling forkbombs and arriving at an article that looked at how forkbombs affected modern linux distributions and *BSD. I quote:
I'll admit that I held my breath for a few seconds as I keyed the script into my NetBSD laptop, and then ran it. I was pleasantly surprised when the attack had no effect, confirming that I wasn't losing my mind after all -- limits had been put in place to prevent a normal user from crippling the entire system. Exactly as one would expect.
Naturally I wanted to test this. I have a netbsd box running on an old ibook with the following uname -a:
NetBSD eva00 1.6.2 NetBSD 1.6.2 (GENERIC) #0: Tue Feb 10 23:52:52 UTC 2004 autobuild@tgm.netbsd.org:/autobuild/netbsd-1-6-PATCH
002/macppc/OBJ/autobuild/
netbsd-1-6-PATCH002/src/sys/arch/macppc/compile/
GENERIC macppc
Quickly I typed up a cheap bash forkbomb that's not even as cool as
:(){ :|:&};: -
#!/bin/sh
$0 &
exec $0
Merrily I executed the script in my normal account over SSH... and watched as things slowed down to a crawl. SSH timed out, and local login from the keyboard generated a delay of about 60 seconds from keystroke to echo. Normally one would reboot and install quotas, but this box had good uptime! I wanted to keep it so I set my self the slightly harder task of defusing the fork bomb.

How it was done
I knew I had to some how stop the processes, not kill them because killing them will simply free up resource which would immediately be taken over. ni suggested to use SIGSTOP. To my knowledge POSIX defines a set of signals which can be send to processes. One of these is SIGSTOP which can not be caught or ignored. It causes the process in question to stop. Yes it surprised me too.

So now we know how to halt it and bob's our uncle right? Not yet. The problem was there were over 400 bash processes happily consuming what little resources there were and given the 60 second delay its silly to try and manually halt every one of them. In the absence of killall I devised the following:
ps ax | grep bash | grep -v grep | awk '{print $1}' | xargs kill -STOP
Which worked! After I spent about an hour typing it in. Then the STOP was replaced with -9 and ran again to kill the processes and reclaim my resources. A problem with the above is that it had a large collateral damage. A better script would have been:
ps axu user | grep bash | grep -v grep | awk `{print $2}' | xargs kill -STOP
So next time...
Implement user process quotas!

Cheers,
Steve