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.