We've talked a lot about checking: checking environments, checking things from the network, and so forth. Now, let's take a look at inputs. On the next slide, you'll see some examples of invalid input. If you remember the DNS poisoning that I mentioned in the previous set of slides. Here's an example of where something can go wrong, and we have seen this code in programs. We want the host name to be mailed to the user bishop here. So, the host name is stored in p, and it's written into the buffer cmd, as a formatted string, echo, host name, pipe to mail bishop. That's then given to a program that will execute the command in cmd as though it were typed at the shell. The problem is the client in order to resolve that IP address into a host name, calls a function called gethostbyaddr, which uses DNS. Again, the assumption here is that gethostbyaddr will always return the correct answer. But the contents of the DNS not under our control as I said earlier. So, you have to check when it comes back with to be sure that it's a valid hosting. The next slide shows an example of a problem. Let's say the host name results to that. First of all, that's an illegal host name. Back tick is illegal, blank is illegal, semicolon is illegal, and star is illegal. So, there are a whole bunch of illegal characters in that host name and it should've been rejected instantly. But if it's not, looked at the command that is executed. First of all, it echoes info.mabell.com. Then it runs the command rm dash rf star, which basically means delete everything in this directory and everything under this directory. Hopefully the program that's running this as not in slash, because if it is in the root directory your system is gone. But anyway, everything between the back tick is replaced by info.mabell.com, and that gets echoed or piped into mail bishop and gets sent off to bishop. Now, this attack was actually widely used in the 90s, and so all of the Genesis should have this fixed, so it would reject anything like this. It's still a good idea to check because you never know when you run into one that somebody hasn't patched. The resolvers also should be checking this. The next slide generalizes this problem. We talked this, previous one was input from the network. Now, we're going to talk about users. Users can specify any input they like. It may not be what you expect. It may be what you expect. If it's what you expect, well and good. If it's not, odd things can happen. As an example, when you're using a certain windowing system, there is a variable called display. That is used to determine which window, and which system to draw the images on. If I give the correct window and system name, it works. But in many cases, this variable is given to a program to run on the command line. The value of this variable is run on the command line. The attack that I just showed you for DNS works in this case just as well. As an example, if I'm asked, "Who do you want to send the mail to?" When I type the line that you can see here beneath, if user gives the recipient for mail as I get again compromised system. By the way, this was a bug in Version 7 UNIX program, which is long gone. We've seen it in some versions of Sendmail, and as far as I know those are long gone. We've also seen this in Web browsers. I think all of the Web browsers have this fixed. Anyway, it's a good lesson in why you need to check everything. There are two problems here. When I'm doing a formatted print, but that's completely under the control of the user. In other words, the user is supplying the format string. That's what the second bullet shows here, printf str. There are a couple of things that can go wrong here. The first one is, I can supply a format string that will allow me to read information off the stack. I can even supply a format string that will allow me to change the value of variables using percent n which stores in memory at a particular location the number of characters that it's actually printed. So, you should never in your program use any of the printf family of library functions to print things out or put them into a buffer without supplying your own format strings. Don't trust the user or the program, it's input to have that trope format string correct. Furthermore, check the length of whatever's coming in if you're storing it into a buffer. The scan of functions are particularly bad about this. They typically don't check length. So, you have to use a formatted string like percent followed by a number followed by s, sometimes that works. The other problem is, if you're going to read a string and use that string as a command. I mentioned earlier some of the problems but the main one is that again the string may have characters that are meaningful to the shell. So, you have to check and be sure that those characters are allowed. If you're checking commands as well, if there are multiple commands in that input separated by shell metacharacters, you need to be sure that the month check all of the commands. A number of programs would check the command, first command and then check to be sure that the shell metacharacters were the ones that were allowed. Among the ones allowed were semicolon, and a semicolon divides commands. So, that what followed the semi-colon was another command but the program never checked that. In essence, this is processing unreliable information. Whenever you read data from a source that you don't control, do sanity checking. At a minimum for buffers, check the length of the data. For numbers, check the magnitude and the sign. For anything involving network infrastructure like host, names, be sure that the characters that are supplied are all legal. If you're reading input from a user, don't use the scanf functions. Use something like F getus or write your own little input that will input function that will read the characters directly. That way you can stop when things get too long. Incidentally, there are some functions called SN scan f, will only load a particular number of functions. Those are good. You can use those. But the one thing you have to be aware of is that, typically they will not put a null byte at the end. If the number of characters you read is exactly the number you allow. So, beware of that. That's also true of SGR and copy, by the way. As I mentioned earlier, environment counts too. You get information from the system, not just from input. For example, assume that you look at a file and you get the owner name. What does that mean? Well, it means the user owns the file. That's it. Nothing more and nothing less. So if you are going to assume that that implies certain permissions, you have to do something else. The example given here is if the owner has to create the file. Another one involving race conditions that we'll talk about at length later on is, if you use a file name and always refers to the same file object. A file object is what's actually stored on disk. The name is where it's stored. In some cases, this is true. In some cases, this is false. You'll see exactly what happens when it's false later on. Okay. Now, why the ownership issue? Well, there's a program called "at" on Linux and Unix systems that allows you to run things at a later time. So you'd say at 12:00 p.m, and then give the name of file to execute. This file would be stored in a spool directory and every 15 minutes the program called Cron starts up, looks for these files and if the time is right, it will go ahead and execute them. Now, the program at that did destroying was not set UID to root. So that meant the spool directory had to be world writable. The program that actually did the execution that Cron started up is called Atrun, and it did run, started off at least to set UID the root and then it would look the owner of the file and then change its permissions to be that of the owner. So that way the program the owner has to be run would only run with the owner's permissions. So the assumption here is whoever owns that file, is the one who ended the commands and is the one who wants to have the commands run with those privileges. The assumption here is that you can't write, no one else can write into those files and to add runs credit, it would check that the file was not world writable. But there are files you can write to that are still not world writable. Think of a mailbox. If I send you an email that goes into your mailbox, I wrote to that file even though you own it, and that was the trick that was used here. What people would do is mail a set of commands to the system administrator root. Then what they would do is link the roots mailbox into the Atrun directory with the name indicating when it should be run. It turned out all the spool directories were kept on the same file system, so this was very easy to do. An Atrun would come along, see the file, when it was time to execute the contents of the file, it would open the file and start executing the lines as commands. Now, most of the mail is not going to be a valid command so you'll just get error messages. But when it hits the valid commands, it will execute those as root. So effectively, a user could ever do whatever they wanted. The problem here is shown on the next slide, is that Atrun's validation technique was flawed. It assumed that whoever owned the file authorized the commands to be run. The problem is it should also have checked that whoever owned the file, actually put the file in that Atrun directory which just so happen to be world writable. So it shouldn't believe that. The solution by the way, was to make at set GID the spool directory writable by group, and so then add has to do the queuing. But when it did the queuing, the owner would stay associated with the file. You couldn't do the linkage anymore. The linkage of the mail file on this spool directory because the ordinary user didn't have write permission anymore. So that solved the problem. Another failure to check something's kind of classic. If you ever look at how LPR runs, the spools files to the print. Lets say for the moment and this is a little bit of a simplification of how it actually works, but this, in essence, is how the attack worked. Let's say it's identified by a three-digit unique number, and the numbers are signed sequentially. So if I print a file, it'll mean the spool directory is 001, then if you try to print another one while mine's in the spool directory, it will be 002 and so forth. The spool directory was owned by root and writable only by root, so LPR will set UID to root, and so it would open the spool files and then just go ahead and open the file with the appropriate name and just write the data into it. Problem here was it didn't check to see if the file already existed. If the file existed, it would simply delete the one that was there. That also turned out that you could use symbolic links as well as regular files and this was the attack. The problem is if I want to print the password file for example, or some confidential file owned by root, I can't do LPR and name the file because LPR will refuse to open it since I'm not root. But what it can do is have a symbolic link point to that file. Then when LPD comes to print the file, it will see the symbolic link is on by root, follow it, get the fruit on file and go ahead and print it. So that was the attack. Create a very small file containing say a password file, make a copy of it, wipe out root's password, save it. Next, you start printing a very large file and the key here is the file can't finish printing until you're done with what you're going to do. The next file you queue is the password file, the symbolic link, the password file. You then queue 999 files so it goes all the way around and then you queue X. When LPR writes to X, writes X to spool directory, it's going to open the one following the very big file that's printing. That is a symbolic link to the password file so it's going to overwrite the password file. It can do this because LPR saved UID to root. The next slide shows some of the assumptions that were being made. The first assumption is that well, they'll never be more than 1000 files queued at once. As I say, that number is actually bigger. So the attack takes longer to do than what I just showed you but the principle is the same. You do it the same way. The second one is that LPR will never write anything outside the spool directory. Well, if you have a symbolic link it did. By the way, this bug has been long fixed too but it's a very good example of a failure of validation.