author: fish 2003 [ fork bombs ] Introduction ------------- Fork bombs are based on a very simple concept, can be very small and, can pack quite a punch. Basically, fork bombs eat up system resources exponentially, the end result being that there are no resources left for other processes to use. Mechanics ---------- Modern operating systems allow for multiple processes to execute at once. Programs are able to take advantage of this feature by spawning child processes for concurrent executions of codes. The standard system call for creating child processes is fork(). A fork bomb combines recursion with fork() to create a process which will then call itself over which will create a process which calls itself etc. Every process on a system is given a section of the system resources for it's execution. Fork bombs work by using up all the system resources. When a program tries to execute or use system resources and there are no resources for it to run, it simply can't run. As such, a fork bomb is a local DoS attack. Danger ------- Since all that is required to execute a fork bomb is the ability to run a process on the system, users of any privelege level can potentially DoS the entire computer. Sometimes an administrator will be able to kill the offending fork bomb process, but many times a system hard boot is necessary. Attack ------- The fork bomb attack could be done using any programming or scripting language and it doesn't need to be very big at all. Since files like shell scripts and binaries leave evidence on the system, many attackers usually opt to construct their fork bombs directly from the command line. The following command will fork bomb the system: perl -e '&bomb;sub bomb{fork();&bomb;}' Let's pick this command apart some to see what it's doing. First we can see that we are invoking the perl interpreter to execute our argument. Our argument includes the perl commands that make up our fork bomb. First the command calls the subroutine named 'bomb' with the &bomb call. The bomb subroutine then calls the fork() system call to create a child process and then it calls itself (that's the recursive part right there). The execution then goes back to the fork() call in the subroutine then calls itself again and this vicious cycle continues until the process is killed. Advances --------- The fork bomb examples given thus far have been very simple. Naturally improvements can be made to them to make them more dangerous. Some common techniques include adding code to make each individual process suck up as much memory as it can, to alter the process names for each spawned process so they're more difficult for administrators to kill, to make each process open files so all the file descriptors are used, or to make each proc create files to use up all the i-nodes. The rest of this text will only deal with the simple fork bombs since understanding those should help you understand the more advanced ones. Motivation ----------- Why would anyone want to fork bomb a system? At first it seems quite script kiddyish and it's true that many script kiddies do like the attack due to its simplicity and fast results. However, there are other reasons a more advanced attacker may fork bomb a system. When responding to a suspected intrusion incident it is crucial to obtain all of the volatile data on the victim box. Many times this volatile data will hold the greatest clues and information about the attacker. Volatile data is data that will be lost when the system reboots. Some examples of the important volatile data administrators will gather before bringing a system down include process listings, netstat output, and utmp/wtmp info. Attackers may sometimes fork bomb a system when they are done hacking it to prevent the administrators from being able to collect the volatile info that may incriminate the attacker. Obviously it's better for an attacker to quietly slip away after removing the evidence that they had been there, but this isn't always an option. Perhaps an administrator started investigating before the attacker had time to slip away in a more quiet manor or maybe the attacker doesn't have the priveleges or abilities to munge the logs etc. Mitigation ----------- Mechanisms do exist for administrators to protect their systems from the threat of fork bombs, but many systems don't come with this protection enabled by default and most administrators seem to overlook the configuration steps to enable them. The basic technique for protecting a system from fork bombs is to put limits on the system resources available to any single user account. There are a couple ways in which this is usually implemented. The first method is to place the ulimit command in the users shell initialization scripts that will place the limits on the user as they login. This method is flawed however as the limits are only set if the user logs in. There are plenty of cases in which an attacker doesn't need to log into the system in order to execute a command on that system. The attacker could for example take advantage of a weakness in a poorly programmed cgi script such that they are able to execute a command (for our argument the command they choose to execute will be a fork bomb). Any command that the attacker is able to execute via a hole in a cgi script will be executed by the owner of the webserver process, which in many cases these days is the user "nobody". Now since the user nobody is a system account and no one logged into the system to execute the command, there are no resource restrictions in place preventing the fork bomb from doing it's damage. The second more effective method of fighting fork bombs is to set system wide resource limits via the limits.conf file. The limits.conf file works via PAM modules and it's restrictions are effective whether the system was officially logged into or not. Ulimit ------- ulimit is used to set limitations on a shell and its descendents for sh based shells (including bash and ksh). csh uses the `limit` command instead. Here we will focus on ulimit for the sh shells since they are more popular. The -a argument to the ulimit command will display all of the current limits for the shell. It's output on an unconfigured system will look something like: core file size (blocks) 0 data seg size (kbytes) unlimited file size (blocks) unlimited max locked memory (kbytes) unlimited max memory size (kbytes) unlimited open files 1024 pipe size (512 bytes) 8 stack size (kbytes) 8192 cpu time (seconds) unlimited max user processes 1023 virtual memory (kbytes) unlimited Only a process with high enough priveleges can change the limits set on a shell. The ulimit command line arguments for changing the limits on specific things are as follows: -c ### : maximum core file size -d ### : maximum data segment (heap) size in kbytes -f ### : maximum file size on 512-byte blocks -l ### : maximum locked memory in kbytes -m ### : maximum memory in kbytes -n ### : maximum file descriptors + 1 -p ### : maximum pipe size in 512-byte blocks -s ### : maximum stack segment size in kbytes -t ### : maximum cpu time in seconds -u ### maximum user processes -v ### : maximum size of virtual memory in kbytes -H : set as a hard limit -S : set as a soft limit Since this text is about preventing fork bombs we should focus on the maximum number of user processes setting. The actual settings used will likely depend on the specific server it's on. The values chosen can be affected by the systems hardware (maybe it's capable of handling higher loads) and it's jobs (maybe it's a shell server and the users should have more freedoms). In many cases setting the maximum number of processes to something around 100 will prevent the serious damages from fork bombs but you may find lower is acceptable for your system: ulimit -H -u 100 It's important to note that users can lower the limits on their shells but they can't raise them above those set by the admin. Admins will usually enforce the ulimits via system wide shell initialization files like /etc/profile. Limits.conf ------------ The limits.conf file can be used to set limits on a system wide basis rather than on each shell. The format of limits.conf entries is as follows: As usual, the # char is for commenting the rest of the lines out. : tells the system who the limit applies to. Valid values for the are a username, a groupname (prepended by an '@' char), or the '*' wildcard char which specifies everyone. : lets you specify if it's a hard limit or a soft limit. hard limits are absolute while soft limits aren't. valid values for this field are 'hard' and 'soft' : specifies what the limit is being set for. valid values are: 'core' : core file size in kbytes 'data' : data size in kbytes 'fsize' : file size in kbytes 'memlock' : locked-in-memory address space in kbytes 'nofile' : number of open files 'rss' : resident set size in kbytes 'stack' : stack size in kbytes 'cpu' : cpu time in minutes 'nproc' : number of processes 'as' : address space 'maxlogins' : number of logins for user 'priority' : priority for user processes 'locks' : number of file locks : the values for the limit, simple enough. It could be 'unlimited' for no restrictions. Following are two entries which will configure the system to limit all users except root to a maximum of 100 processes: root hard nproc unlimited * hard nproc 100 Setting system wide limits works better on production servers that not many users will be using all the time. The limits could possibly get in the way of the legit actions performed by the users. You will have to find acceptable limits for each system that are able to limit the system enough to prevent abuse yet let it function as it should. Conclusion ----------- Fork bombs can be toys for script kiddies or tools for real hackers, either way they can be a serious problem. With proper system configurations their effects can be limited.