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

    

Return to $2600 Index