The Sin Explained

The primary programming mistake that leads to race conditions is doing something any good programming text will tell you not to do, which is programming with side effects. If a function is nonreentrant, and two threads are in the function at once, then things are going to break. As youve probably figured out by now, nearly any sort of programming error, given some bad luck on your part and effort on the part of the attacker, can be turned into an exploit. Heres a C++ illustration:

 list<unsigned long> g_TheList; unsigned long GetNextFromList() {  unsigned long ret = 0;  if(!g_TheList.empty())  {  ret = g_TheList.front();  g_TheList.pop_front();  }  return ret; } 

You might think that your odds of two threads being in the function at once are low, but underneath this very small amount of C++ code lurks a lot of instructions. All it takes is for one thread to pass the check as to whether the list is empty just before another calls pop_front on the last element. As Clint Eastwood said in the movie Dirty Harry : How lucky do you feel? Code very much like this prevented an ISP from servicing their customers for most of one day.

Another incarnation of the problem is signal race conditions. This attack was first publicly detailed in Delivering Signals for Fun and Profit: Understanding, Exploiting and Preventing Signal-Handling Related Vulnerabilities by Michal Zalewski and can be found at www.zone-h.org/files/4/signals.txt. The problem here is that many UNIX applications dont expect to encounter the types of problems youd see in multithreaded apps. After all, even concurrent applications running on UNIX and UNIX-like systems would normally fork a new instance, and then when any global variables get changed, that process gets its own copy of the memory page due to copy-on-write semantics. Many applications then implement signal handlers, and sometimes they even map the same handler to more than one signal. Your app is just sitting there doing whatever it is supposed to do when the attacker sends it a rapid-fire pair of signals, and before you know it, your app has essentially become multithreaded! Its hard enough to write multithreaded code when youre expecting concurrency problems, but when youre not, its nearly impossible .

One class of problem stems from interactions with files and other objects. Theres nearly unlimited ways to get in trouble with these. Here are a few examples. Your app needs to create a temporary file, so it first checks to see if the file already exists, and if not, you then create the file. Sounds like a common thing to do, right? It is, but heres the attackthe attacker figures out how you name the files and starts creating links back to something important after seeing your app launch. Your app gets unlucky, opens a link thats really the file of the attackers choice, and then one of several actions can cause an escalation of privilege. If you delete the file, the attacker might now be able to replace it with one that accomplishes evil purposes. If you overwrite the existing file, it might cause something to crash or encounter an unexpected failure. If the file is supposed to be used by nonprivileged processes, you might change permissions on it, granting the attacker write permission to something sensitive. The worst thing that can happen is for your app to set the file suid root, and now the application of the attackers choice gets to run as root.

So you develop for Windows systems and are sitting there smugly thinking that none of this applies to youthink again. Heres one that hit Windows: when a service starts, it ends up creating a named pipe that the service control manager uses to send the service control messages. The service control manager runs as systemthe most privileged account on the system. The attacker would figure out which pipe to create, find a service that can be started by ordinary users (several of these exist by default), and then impersonate the service control manager once it connects to the pipe. This problem was fixed in two stages: first, the pipe name was made unpredictable, greatly reducing the window of opportunity for the attacker, and then in Windows Server 2003, impersonating other users became a privilege. You might also think that Windows doesnt support links, but it does; see the documentation for CreateHardLink. You dont need much access to the file being linked to. Windows has a large number of different named objectsfiles, pipes, mutexes , shared memory sections, desktops, and othersand any of these can cause problems if your program doesnt expect them to exist to start with.

Sinful Code

Although were going to pick on C, this code could be written in any language, and theres very little that is language-specific about it. This is one mistake thats a combination of design error and a failure to understand and work around the nuances of the operating system. Were not aware of any languages that make race conditions significantly more difficult to create. Here are a few code snippets, and what can go wrong:

 char* tmp; FILE* pTempFile; tmp = _tempnam("/tmp", "MyApp"); pTempFile = fopen(tmp, "w+"); 

This looks fairly innocuous , but the attacker can, in general, guess what the next filename is going to be. In a test run on the authors system, repeated calls generated files named MyApp1, MyApp2, MyApp3, and so on. If the files are being created in an area that the attacker can write into, the attacker may be able to pre-create the temp file, possibly by replacing it with a link. If the application is creating several temporary files, then the attack becomes much easier.

Related Sins

There are several interrelated problems covered here. The primary sin is the failure to write code that deals with concurrency properly. Related sins are not using proper access controls, covered in Sin 12, and failure to use properly generated random numbers , covered in Sin 18. Nearly all of the temp file race conditions are only problems because improper access controls were used, which is typically compounded by older versions of the operating system not providing properly secured per-user temporary directories. Most current operating systems do provide per-user scratch space, and even if it isnt provided, its always possible for the application developer to create scratch space underneath a user s home directory.

Failure to generate random numbers correctly comes into play when you need to create a unique file, directory, or other object in a public area. If you use either a pseudo-random number generator, or worse yet, predictably increment the name, then the attacker can often guess what youre going to create next, which is often the first step on your road to ruin. Note that many of the system-supplied temporary filename functions are guaranteed to create unique filenames, not unpredictable filenames. If youre creating temporary files or directories in a public place, you may want to use proper random number generation functions to create the names . One approach is documented in Chapter 23 of Writing Secure Code, Second Edition by Michael Howard and David C. LeBlanc (Microsoft Press, 2002), and even though the sample code is for Windows, the approach is very portable.



19 Deadly Sins of Software Security. Programming Flaws and How to Fix Them
Writing Secure Code
ISBN: 71626751
EAN: 2147483647
Year: 2003
Pages: 239

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net