Security Incidents mailing list archives

Re: Recognizing compromised binaries


From: dbrez () AMAZON COM (Dominique Brezinski)
Date: Mon, 21 Feb 2000 18:07:21 -0800


At 08:39 PM 2/18/00 -0800, Stephen Friedl wrote:
vivid, but one small point I've not seen before in this forum. The
bad guy had compromised most of the obvious system binaries (ls,
ps, netstat, etc.) so it was hard to identify even what was wrong.

I recommend making a little tool kit on read-only media (CD_ROM is good)
that you can mount on a suspect system to do this kind of analysis (when
you don't know if anything has happened that warrants serious forensic
analysis).  You should only use statically compiled binaries in this tool
kit, and there are some environmental things to watch out for (I did a
little presentation at BlackHat Briefings last year that you can check out
here: http://www.blackhat.com/html/bh-usa-99/bh3-speakers.html)

Here is the source to a little program that will print out stat info for a
file *or* a directory and its contents (but will not recurse to
subdirectories)--it is probably better than ls for forensic purposes.  My
employer nor myself express any warranty, explicit or implied, of any kind
with regards to the following code or its fitness of use:
--------CUT HERE--------------
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>

char* FixupArg(char*, char*, int);

int main(int argc, char** argv)
{
    char                target[PATH_MAX + 1] = {'\0'};
    struct stat         statInfo;
    struct dirent*      pDirEntryInfo = NULL;
    DIR*                pDir = NULL;

    if (argc != 2)
    {
        printf("Usage: %s <filename or directory>\n", argv[0]);
        exit(1);
    }
    else
    {
      /* Initialize resources */
      if (FixupArg(argv[1], target, PATH_MAX) == NULL) {
        exit(0);
      }
    }

    if (lstat(target, &statInfo)) {
      printf("failed to lstat() file - errno %d\n", errno);
      exit(1);
    }
    printf("\n%s\n", target);
    printf("ctime: %s", asctime(gmtime(&(statInfo.st_ctime))));
    printf("atime: %s", asctime(gmtime(&(statInfo.st_atime))));
    printf("mtime: %s", asctime(gmtime(&(statInfo.st_mtime))));
    printf("owner: %d\ngroup: %d\nsize in bytes: %d\nINode number: %d\nfile gen
number: %d\n", statInfo.st_uid, statInfo.st_gid, statInfo.st_size,
statInfo.st_i
no, statInfo.st_gen);

    if (S_ISDIR(statInfo.st_mode)) {
      /* we have a directory, so recurse through it calling lstat() on each
       * entry as we go.
       */
      pDir = opendir(target);
      if (pDir == NULL) {
        printf("opendir() failed\n");
        exit(1);
      }
      for (pDirEntryInfo = readdir(pDir); \
           pDirEntryInfo != NULL; \
           pDirEntryInfo = readdir(pDir)) {
        char targetFile[PATH_MAX + 1] = {'\0'};

        if (!strncmp(pDirEntryInfo->d_name, ".", PATH_MAX) ||
            !strncmp(pDirEntryInfo->d_name, "..", PATH_MAX)) {
          continue;
        }
        else {
          strncpy(targetFile, target, PATH_MAX);
          strncat(targetFile, "/", PATH_MAX - strlen(targetFile));
          strncat(targetFile,
                  pDirEntryInfo->d_name,
                  PATH_MAX - strlen(targetFile));
        }
        if (lstat(targetFile, &statInfo)) {
          printf("failed lstat() file %s -errno %d\n", targetFile, errno);
          continue;
        }
        else {
          printf("\n%s\n", targetFile);
          printf("ctime: %s", asctime(gmtime(&(statInfo.st_ctime))));
          printf("atime: %s", asctime(gmtime(&(statInfo.st_atime))));
          printf("mtime: %s", asctime(gmtime(&(statInfo.st_mtime))));
          printf("owner: %d\ngroup: %d\nsize in bytes: %d\nINode number:
%d\nfil
e gen number: %d\n", statInfo.st_uid, statInfo.st_gid, statInfo.st_size,
statInf
o.st_ino, statInfo.st_gen);
        }

      } /* end for (readdir()) */
      closedir(pDir);

    } /* end if (S_ISDIR) */
    free(target);

    exit(0);
}

char* FixupArg(char* arg, char* fixedArg, int fixedArgLen)
{
  /* check if the arg looks to be absolute path */
  if (*arg == '/') {
    strncpy(fixedArg, arg, fixedArgLen);
  }
  else {
    /* looks like we have a relative path, so handle it appropriately */
    if (strlen(arg) < PATH_MAX && fixedArgLen >= PATH_MAX) {
      if (realpath(arg, fixedArg) == NULL) {
        printf("relative/absolute path translation failed\n");
        return (NULL);
      }
    }
    else {
      printf("Relative path given is too long or supplied buffer too small\n");
      return (NULL);
    }
  }
  return (fixedArg);
}

----------CUT HERE---------------------

The output looks like this:

dbrez@it[104]>./stat ../rstat

/home/dbrez/src/rstat
ctime: Tue Feb 22 00:57:25 2000
atime: Tue Feb 22 00:57:56 2000
mtime: Tue Feb 22 00:57:25 2000
owner: 6254
group: 229
size in bytes: 4096
INode number: 4015042
file gen number: 0

/home/dbrez/src/rstat/stat
ctime: Tue Feb 22 01:26:28 2000
atime: Tue Feb 22 01:26:28 2000
mtime: Tue Feb 22 01:26:28 2000
owner: 6254
group: 229
size in bytes: 24576
INode number: 4323271
file gen number: 0

/home/dbrez/src/rstat/rstat.c
ctime: Tue Feb 22 01:26:22 2000
atime: Tue Feb 22 01:26:22 2000
mtime: Tue Feb 22 01:26:22 2000
owner: 6254
group: 229
size in bytes: 3319
INode number: 1048607
file gen number: 0

---
Dominique Brezinski             Amazon.com Security
office (206) 266-6900           pager (888) 916-2747
8312 ADAB C5B2 1916 CBD8  150E 37CE 044E F45F B5E4



Current thread: