feather.c
by Kairi Nakatsuki (kairi@phreaker.net)
/* * feather.c - preserve a program's atime and mtime after executing * Written by Kairi Nakatsuki <kairi@phreaker.net> * * usage: feather command [args ...] * * I wrote this little ditty after a session of pondering--Do people think * about the fact that the access times of commands they execute are modified * on a read(), mknod(), chmod(), or utimes(), when trying to cover one's * tracks? Some breakin attempts I've seen suggest otherwise, which prompted * me to write this program to sate my curiousity. This utility is mainly * useful when one wants to cover their tracks further after gaining a root * shell. Note that if you really wanted to be thorough about being covert, * make sure you use this utility in conjunction with touch(1) to set the * atime and mtime of the shell and compiler to pre-intrusion values. * Sure, one could do the very same with touch(1) after each execution, but * that would be tedious, no? * * On most UNIX workalikes, all that is needed to compile this program is: * * $ cc feather.c -o feather * * The functionality of most of the code is explained thoroughly for those new * to the scene. You may notice that I "over-code" things by rewriting the * functionality of functions that may already exist; note that this is for * portability reasons, as I tested this bit of code on my NEXTSTEP 3.3 * machine. Also tested under GNU/Linux (glibc 2.2), NetBSD 1.6.1, and QNX. * * If you find a legitimate use for this snippet of code, e-mail me, as I would * be very impressed to know. Don't let this program be an open invitation to * "own" somebody's machine simply to test it out. This is intended for * educational purposes only. Allow me to remind you to be responsible. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> /* added */ #include <errno.h> char *appname; extern int errno; static void usage() { fprintf(stderr, "usage: %s command [args ...]\n", appname); exit(1); } char *which(const char *command) { char *path = NULL, *tmp = NULL, *buf = NULL; int len, cmdlen = strlen(command); /* Test to see if we need to find the absolute path name of the command by * seeing if the string passed to which() is the filename of an executable. * If so, duplicate the contents of command, so the pointer returned by * which() can be handled consistiently. */ if (access(command, X_OK) == 0) { if ((buf = (char *) malloc(cmdlen + 1)) == NULL) { fprintf(stderr, "%s: %s: %s\n", appname, "malloc()", strerror(errno)); exit(errno); } strcpy(buf, command); return (buf); /* free() me */ } /* If getenv() doesn't work, there's obviously a serious memory issue going * on. (If someone doesn't code responsibly, who will? */ if ((path = getenv("PATH")) == NULL) { fprintf(stderr, "%s: %s: %s\n", appname, "getenv()", strerror(errno)); exit(errno); } /* For each element of the PATH environment variable, test to see if the * element, with a slash (/) and contents of command appended, is a valid * path to an executable. If this is the case, return the contents of * buf. buf needs to be free()d, or else we'll have memory leaking all over * the place. I am a neat freak. */ for (tmp = strtok(path, ":"); tmp; tmp = strtok(NULL, ":")) { len = strlen(tmp) + cmdlen + 2; if ((buf = (char *) malloc(len)) == NULL) { fprintf(stderr, "%s: %s: %s\n", appname, "malloc()", strerror(errno)); exit(errno); } strcpy(buf, tmp); strcat(buf, "/"); strcat(buf, command); if (access(buf, X_OK) == 0) { return (buf); /* free() me */ } /* Get rid of buf if we didn't find our executable yet. */ free(buf); } /* Obviously, we haven't found the full pathname of our command, so let's * return NULL. */ return (NULL); } int main(int argc, char **argv) { char *filename = NULL, **args = argv + 1; /* self-explanatory. */ struct stat sb; pid_t pid; /* used when fork()ing. Technically not necessary, but here for * correctness. */ /* Used in error and usage messages. */ appname = argv[0]; if (argc == 1) { /* Print usage and die. */ usage(); } if ((filename = which(args[0])) == NULL) { /* Since which() returned NULL, assume that the command name given is not * that of a valid, existing executable. Complain and die. */ fprintf(stderr, "%s: %s: %s\n", appname, args[0], strerror(errno)); exit(1); } if (stat(filename, &sb) < 0) { /* If something happened to make stat() unhappy, complain and die. */ fprintf(stderr, "%s: %s: %s\n", appname, filename, strerror(errno)); free(filename); exit(errno); } /* fork() time */ pid = fork(); /* UNIX mitosis */ if (pid == 0) { /* executed by the new child process */ if (execv(filename, args) < 0) { fprintf(stderr, "%s: %s: %s\n", appname, filename, strerror(errno)); exit(errno); } } else if (pid > 0) { /* executed by the original parent process */ struct timeval times[2]; /* wait for execution of the child process to end before setting the atime * and mtime back to their original values. */ wait(0); /* The first element of the struct timeval array declared above always * corresponds to the access time of an inode. The second one is always * the modification time. */ times[0].tv_sec = sb.st_atime; times[0].tv_usec = 0; times[1].tv_sec = sb.st_mtime; times[1].tv_usec = 0; if (utimes(filename, times) < 0) { /* This happens because the program did not have the permissions to * modify the inode's time stamps. Pity. */ fprintf(stderr, "%s: utimes(): %s: %s\n", appname, filename, strerror(errno)); } } else if (pid < 0) { /* We were unable to fork() altogether. */ fprintf(stderr, "%s: %s: %s\n", appname, "fork()", strerror(errno)); exit(errno); } /* Save the environment! */ free(filename); return (0); }Code: feather.c