ACCESS System call to be a security hole

ACCESS System call to be a security hole

Post by bhishm shar » Tue, 11 Sep 2001 16:58:27



HI ,

I am unable to understand why access() system call is a security hole
.Can anyone explain how it can be a security hole .

In what kind of situations we should use access system call and in
what kind of situations we should avoid it .

Thanx

Bhishm

 
 
 

ACCESS System call to be a security hole

Post by John Gord » Tue, 11 Sep 2001 23:03:37



> I am unable to understand why access() system call is a security hole
> .Can anyone explain how it can be a security hole .

here's a very informative post on the subject of access(), written
by Chris Torek a while back:

---------------------------------------------------------------------
The access() system call does the wrong thing.  It really should
not even exist!  (The "system call", that is.  If you think access()
*should* exist, read on. :-) )

Unix has the concept of "set user ID", where a program can run "as
user X" but "on behalf of user R".  That is, user R starts the
program, but the program runs with the permissions of user X.

Now, when "user X" is the super-user, i.e., the program is setuid
0 or "root", the program will automatically have all possible
permissions.  (This is more or less the definition of "the super-user":
uid 0 is all-powerful; anything anyone else can do, the super-user
can also do, and even a few more things on top of that.)

So, suppose you have a setuid-root program so that when run it can
always do anything.  Suppose further that this program wants to
make sure that User R -- the "real" user -- is allowed access to
some particular file.  The original (broken) solution, stuck into
Unix and forever after perpetuated, was to add the access() system
call.  It is defined as: "tell me whether the real user -- user R
-- has permission to read, write, and/or execute this here file
name."

The problem with this approach is quite simple.  By the time you get
the answer, it is out of date!

Suppose that setuid-root program "writefile" gets the name of a file
from "31337 h4x0r", who is trying to break security on the machine.
The writefile program attempts to keep the cracker from breaking
security by calling access().  The cracker says:

        please write on /tmp/zorch

so writefile() does:

        char *filename;
        ...
        if (access(filename, W_OK)) {
                fprintf(stderr, "%s: permission denied.\n", filename);
                exit(1);
        }
        ... do a little more work, or maybe even no more work, then ...
        ... open or fopen the file and write on it.

Now all the cracker has to do is run:

        % touch /tmp/zorch
        % ls -l /tmp/zorch
        -rw-r--r-- ... cracker ...
        % writefile /tmp/zorch & (rm /tmp/zorch; ln /etc/passwd /tmp/zorch)

Now there is a race, which the cracker hopes will go like this:
First, "writefile" access()es /tmp/zorch while "cracker" still owns
it, so the access() call will see that /tmp/zorch is indeed writable
by user "cracker".  Next, the "rm" runs and removes /tmp/zorch.
Then the "ln" runs and makes /tmp/zorch a link (or, with "ln -s",
a symlink) to /etc/passwd.  Last, "writefile" opens /tmp/zorch --
which is really /etc/passwd -- and writes on it.  The open succeeds
because "writefile" is running as the super-user.

It may take many runs to "win" the race and cause writefile to
stomp on /etc/passwd, but eventually it *will* happen.  There is
no fix for this: the problem is one of atomicity, and separating
the access() test and the actual opening results in a "non-atomic"
sequence, so no matter what else you do, if you use access() first,
and later do the open(), there will still be a race.

The solution is not to use access() at all.  Modern Unix systems
offer some method -- usually seteuid() or setreuid() -- by which
a setuid program can temporarily "give up" its specialness, actually
do the open (or whatever), and then "resume" its specialness.  This
way the open call is atomic: it tests to make sure that the cracker
has permission to work on /tmp/zorch, and that succeeds or fails
based on whether /tmp/zorch is a link or not.  If the cracker
removes and replaces /tmp/zorch after the open() has returned, it
does not matter; the program still has the original file, not the
link.

Thus, the access() call completely fails at its job of providing
security for setuid programs.  What about for "non-secure" programs,
that do not run setuid?

Here the access() call does not create security holes, so this
objection to its very existence evaporates.  "Non-secure" programs
can indeed use access().  But it still has the very same flaw that
created the security hole in the original program!  It still gives
an "out of date" answer.  The out-of-date answer no longer poses
a security risk since there is no security -- but it is just as
out of date.  Your program is going to have to make sure that the
ultimate operation (open, or whatever) succeeds, in case access()'s
answer was right at the time but became wrong.

Thus (I argue) you might as well just do the operation, and see if
it worked.  The answer from access() cannot be trusted, so why
bother?

Now, there is a valid argument for having some function that predicts
whether some future operation is likely to succeed.  In particular,
this is good for interactive programs that give users a chance to
change their minds.

This function can, of course, be implemented directly via the stat()
call.  The stat() operation will tell you whether the file exists,
and if so, what its permissions are.  You can then calculate whether
you have sufficient permissions to have done the operation on the
file whose information stat() gave you, back at the time you did
the stat().

There is no particular reason not to have a C library "wrapper"
function, perhaps even called access(), that does the stat() for
you.  But there is no reason that this has to be a system call --
and it definitely *should not* be used in "secure", setuid programs,
because it just does not work for that.

Note that the stat() method, while more work, is also more general.
You can not only figure out *whether* some future operation is
fairly likely to succeed or fail, but also *why*.  If the operation
is likely to fail, you can work backwards through the path, looking
at directories, to see exactly where the problem is.  So access(),
if it exists at all, is merely a convenience function for the lazy.
Better programs will end up needing stat() anyway.  Thus, the
important system call is stat(), not access().  Also, I claim that
access() is probably better spelled: "could_likely_access()". :-)

 
 
 

ACCESS System call to be a security hole

Post by Casper H.S. Dik - Network Security Engine » Tue, 11 Sep 2001 23:59:43


[[ PLEASE DON'T SEND ME EMAIL COPIES OF POSTINGS ]]


>This function can, of course, be implemented directly via the stat()
>call.  The stat() operation will tell you whether the file exists,
>and if so, what its permissions are.  You can then calculate whether
>you have sufficient permissions to have done the operation on the
>file whose information stat() gave you, back at the time you did
>the stat().

I challenge you to write access() in a portable manner based on stat()
or other system calls.

Perl tried and failed.

Though you're mostly right about access() and especially its useless
use of the real uid, there are several things stat() doesn't know that
access() can tell you:

        EROFS - read only filesystem (access(file, W_OK))

        take into accounts ACLs or other access control mechanisms

        take into account access controls enforced by a fileserver,
        such as "root mappings"  (access() maps neatly on NFS3_ACCESS,
        though the latter is more akin to faccess_euid(), as it uses
        a filehandle not a pathname

        take into account that euid 0 may not be able to do everything
        in your particular Unix implementation (how would stat determine
        "sufficient privilege" in that case?).

And both stat() and access() will give you information that is out-of-date
the moment you get it.

Casper
--
Expressed in this posting are my opinions.  They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.

 
 
 

ACCESS System call to be a security hole

Post by David Schwart » Wed, 12 Sep 2001 07:35:20


[Quoting Chris Torek]

Quote:> There is no particular reason not to have a C library "wrapper"
> function, perhaps even called access(), that does the stat() for
> you.  But there is no reason that this has to be a system call --
> and it definitely *should not* be used in "secure", setuid programs,
> because it just does not work for that.

        There is a reason this has to be a system call -- the C library has no
way to know what security rules the kernel is using.

Quote:> Note that the stat() method, while more work, is also more general.

        Actually, it's useless, since there's no way to know what the kernel's
security policy looks like.

Quote:> You can not only figure out *whether* some future operation is
> fairly likely to succeed or fail, but also *why*.  If the operation
> is likely to fail, you can work backwards through the path, looking
> at directories, to see exactly where the problem is.  So access(),
> if it exists at all, is merely a convenience function for the lazy.
> Better programs will end up needing stat() anyway.  Thus, the
> important system call is stat(), not access().  Also, I claim that
> access() is probably better spelled: "could_likely_access()". :-)

        Suppose a user ID other than zero is considered root equivalent by the
kernel and can write to any file. How would the C library or your own
code know this? Can a superuser execute a file for which he doesn't have
execute permissions? It varies from kernel to kernel.

        So while Chris is 100% right about 'access' being useless for security
purposes, his other arguments are questionable.

        DS

 
 
 

1. Security Hole on webservers run on variuos OS, How to close UNIS hole

Hi,

I have written a program that enables me via the web, to access any file
(access is see it and download it) on a webservers hard drive. It
basically exlpores the drive (/ root downwards). Now the Apache Server
that run my program delivers the results to my browser. I wrote it
initailly to test the security of our account.

So what permissions should I use on our web directory:-

/usr/www/foobar

That will still allows the server to deliver the webpages and yet stop
people from other accounts from accessing it via chdir (telnet/ftp)?

We also hove home directoys that are not visible to the www called:-
/usr/home/foobar

Now my programs can access this directory too! So what sould I set the
file permissions too, again to stop other account holders with our ISP
from accessing it?

For the record my program traversed the enitire Disc that the web server
is running on. It was only denied access to a few directorys.

Also this mean that no logs are kept!! As my 2nd program called by the
first program, downloads the actual file to my browser and the only log
kept ofcousre is that someone ran the download program.

Is this what Goverments do to obtain without permission peoples files?:-

Say they are targetting http://www.foobar.co.uk, they can do this:-

1) Find out who the ISP is for http://www.foobar.co.uk
2) Buy a cgi account with the same ISP
3) Use a program like mine. (Will not give details publically)
4) Run the program and download the files including any within NON web
directorys.
--
Mark Worsdall
Home  :- shadowwebATworsdall.demon.co.uk
Any opinion given is my own personal belief...

2. how to send attachment via email in script?

3. Serial (ie modem) access - security hole?

4. ALCATEL USB SpeedTouch ADSL modem & Linux RH 7.0?

5. access() security hole question

6. Processor Corrected Errors

7. Is the system function a security hole ?

8. Nessus

9. How I could add a new system call to linux or modify a system calls

10. How to use open system call in a new system call

11. "Interrupted system call" at "low level" - system calls

12. best-of-security mailing list (was: Solaris 2.5 Security Hole: local users can get root)

13. Really serious security hole in Microport Unix (Re: SECURITY BUG IN INTERACTIVE UNIX SYSV386)