Weiter Zurück [Inhalt] Online Suche im Handbuch LITTLE-IDIOT NETWORKING

22.7 Warum Filter anfällig gegen buffer overflows sind

Das folgende Beispiel stammt von Eric Dumazet und findet sich auf ftp://ftp.ris.fr/pub/linux/proxy/. Es ist ein Beispiel für einen Proxy, wie man ihn auf vielen Firewalls implementiert finden kann. Viele Firewall - Hersteller benutzen oft Code aus dem Internet, um Ihrer Firewall noch ein paar spezielle Protokolle, hier einem VDO Proxy, hinzuzufügen. Dies ist nicht der in dem LINUX Kernel implementierte Proxy, sondern einer, der auf jedem UNIX einfach zu installieren ist. Der Autor hat auch einen transparenten HTTPD-PROXY geschrieben, der auch auf o.a. URL zu finden ist. Das Problem mit diesem Proxy ist folgendes. Es fehlen überall Begrenzungen für die maximal zulässige Länge der Übergabeparameter. Um feststellen zu können, welche Daten von wo aus an den Filter übergeben werden, muß man eine vollständige Flußanalyse des (hier noch überschaubaren Programmes) durchführen. Es müssen also folgende Fragen geklärt werden. Ein einziger Fehler im Quellcode ist bereits für einen Angreifer ausreichend, um in das Netzwerk hinter dem Proxy vorzudringen. Wo ist der Fehler ?:

Wer sich intensiver mit dieser Materie auseinandersetzt, wird beim Lesen dieses Artikels über sicheres Programmieren unter UNIX, siehe http://www.whitefang.com/sup/secure-faq.html, daß viele Programme unter LINUX weit davon entfernt sind, sicher zu sein....

Für C-Spezialisten hier nun der vollständige Quellcode, der, bevor er auf einer Firewall eingesetzt werden kann, nach obigen Punkten abgesucht werden sollte. Wie schwer dieses ist, davon kann man sich an diesem sehr kleinen Beispiel selber überzeugen. Man bekommt vielleicht dann einen Eindruck davon, wieviel Mist oft Sicherheitsexperten erzählen (Windows NT ist sicher !, u.s.w.) und wie schwierig es ist, den Quellcode von 1500 Programmierern (Microsoft) und mehrere millionen Zeilen Quellcode nach Fehlern zu durchleuchten. Spätestens nach dieser kleinen Übung sollte auch der letzte Verfechter von Software ohne freien Quellcode davon überzeugt sein, daß viele Augen mehr sehen, als wenige, und daß es immer besser ist, nach dem KISS (Keep It Small and Simple) Prinzip die Software auszuwählen:

/*
 * vdoproxy.c
 *
 * AUTHOR : Eric Dumazet  edumazet@ris.fr
 * DATE : 19961015
 * This is a VDO live proxy for LINUX (with TRANSPARENT PROXY enabled)
 * Usage :
 *   First you must insure that connections for port 7000 are redirected
 *  ipfwadm -I -a acc -P tcp -S yournet/24 -D any/0 7000 -r 7000
 *  Then, launch vdoproxy (no arguments)
 */
#include    <stdio.h>
#include    <sys/types.h>
#include    <sys/time.h>
#include    <sys/socket.h>
#include    <netdb.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <signal.h>
#include    <syslog.h>
#include    <string.h>
#include    <stdlib.h>
#include    <ctype.h>
#include    <unistd.h>

#define VDO_TCP_PORT 7000

int  listen_port = 7000 ;
int  dflg ; /* debug flag */
int lflg ; /* log flag */
int  dest_port = 7000 ;
struct sockaddr_in dest_addr;
int trace_fd = 1 ;

void hexdump_err(p, len)
const char *p ;
int len ;
{
char _aux_bb[128] , c ;
int i , n ;
int dep = 0 ;
    while (len > 0) {
        sprintf(_aux_bb, "%3X: ", dep) ; dep += 16 ;
        i = 5 ;
        for (n = 0 ; n < 16 ; n++) {
            if (n < len) sprintf(&_aux_bb[i], "%02X ", p[n]&255) ;
            else strcpy(&_aux_bb[i], "   ") ;
            i+=3 ;
            }
        for (n = 0 ; n < 16 ; n++) {
            if (n < len) {
                c = p[n] ;
                _aux_bb[i] = (' ' <= c && c < '\177') ? c : '?' ;
                }
            else _aux_bb[i] = ' ' ;
            i++ ;
            }
        _aux_bb[i++] = '\n' ;
        write(trace_fd, _aux_bb, i) ;
        p += 16 ,
        len -= 16 ;
        }
}
/*
 * Basic functions : allocates a socket, and does a connect to the server,
 * on port 7000
 */
int serverconnect(struct sockaddr *nm)
    {
    int fd ;

    fd = socket(AF_INET, SOCK_STREAM, 0) ;
    if (fd == -1) {
                if (lflg) fprintf(stderr, "couldnt allocate socket\n") ;
                return -1 ;
                }
    dest_addr.sin_port = htons(VDO_TCP_PORT);
    dest_addr.sin_family = AF_INET ;
    dest_addr.sin_addr.s_addr = ((struct sockaddr_in *)nm)->sin_addr.s_addr
;
    if ( connect(fd, (struct sockaddr *)_addr, sizeof(dest_addr)) < 0 )
{
                if (lflg) fprintf(stderr, "couldnt connect to server :
%s\n", strerror(errno)) ;
        close(fd) ;
        return -1 ;
        }
    return fd ;
    }
/*
 * Open a socket in order to receive UDP datagrams
 */
int alloue_server_udp(int *sockudp, int *server_udp_port)
    {
    struct sockaddr_in name ;
    int len ;
        int res ;

    *sockudp = socket(AF_INET, SOCK_DGRAM, 0) ;
    if (*sockudp == -1) return -1 ;
    memset(, 0, sizeof(name)) ;
    name.sin_family = AF_INET;
    name.sin_addr.s_addr = INADDR_ANY;
/*    name.sin_port = 0 ;*/
    res = bind(*sockudp, (struct sockaddr *), sizeof(name)) ;
        if (res == -1) {
                perror("bind") ;
                close(*sockudp) ;
                return -1 ;
                }
        if (dflg) fprintf(stderr, "Avant getsockname port=%d\n",
ntohs(name.sin_port)) ;
    len = sizeof(name);
    getsockname(*sockudp, (struct sockaddr *) , ) ;
    *server_udp_port = ntohs(name.sin_port) ;
    if (dflg) fprintf(stderr, "port %d allocated\n", *server_udp_port) ;
    return 0 ;
    }
/*
 * Each connection is handled by a separate process.
 */
void do_child(int newfd)
    {
    struct  sockaddr name, namepeer ;
    struct  sockaddr_in where ;
        struct  sockaddr_in outudp ;
    struct sockaddr_in from;       /* Sending host address */
    fd_set rfd ;
        int already_bind = 0 ;
    char buffer[4096] ;
    char zone[64] ;
    int pos = 0 ;
    int namelen , fromlen, i ;
    int fd_to_proxy ;
    int maxfd ;
    int lu , res ;
    int sockudp , sockcl ;
    int client_udp_port, server_udp_port ;

    namelen = sizeof(namepeer) ;
    i = getpeername(newfd, , ) ;

    /* This hack, is the TRANSPARENT PROXY magic :
     * We want to know wich destination the client want to connect
     */
    namelen = sizeof(name) ;
    i = getsockname(newfd, , ) ;

        if (lflg || dflg) {
                time_t tnow ;
                struct tm *tm ;

                time() ;
                tm = localtime() ;
                fprintf(stderr, "%02d:%02d:%02d %d.%d.%d.%d:%d ->
%d.%d.%d.%d ",
                        tm->tm_hour,
                        tm->tm_min,
                        tm->tm_sec,
                        namepeer.sa_data[2] & 255,
                        namepeer.sa_data[3] & 255,
                        namepeer.sa_data[4] & 255,
                        namepeer.sa_data[5] & 255,
                        ntohs(((struct sockaddr_in *))->sin_port),
                        name.sa_data[2] & 255,
                        name.sa_data[3] & 255,
                        name.sa_data[4] & 255,
                        name.sa_data[5] & 255) ;
                }
    fd_to_proxy = serverconnect() ;
    if (fd_to_proxy == -1) exit(1) ;

    lu = read(newfd, buffer, sizeof(buffer)) ;
    if (dflg) {
        printf("From client : (%d)\n", lu) ;
        fflush(stdout) ;
        hexdump_err(buffer, lu) ;
        }
/*
 * On extrait de la demande du client le port UDP qu'il desire employer.
 */
    /*
     * Recherche "VDO Live" 
     */
    for (i = 0 ; i < lu ; i++) {
        if (memcmp(buffer + i, "VDO Live", 8) == 0) {
                        i += 8 ;
                    client_udp_port = ((buffer[i+2] & 255)<< 8) +
(buffer[i+3] & 255) ;
                    buffer[i+2] = server_udp_port >> 8 ;
                    buffer[i+3] = server_udp_port ;
                        break ;
                        }
                }
    alloue_server_udp(, _udp_port) ;

        /* on fait en sorte que les paquets que nous emettons aient comme
adresse source */
        /* l'adresse du serveur */
    sockcl = socket(AF_INET, SOCK_DGRAM, 0) ;



    where.sin_family = AF_INET ;
    where.sin_addr.s_addr = ((struct sockaddr_in
*))->sin_addr.s_addr ;
        if (dflg) fprintf(stderr, "s_addr %x ", 
                ntohl(where.sin_addr.s_addr)) ;
    where.sin_port = htons(client_udp_port) ;
    if (dflg) fprintf(stderr, "port udp du client : %d\n", client_udp_port)
;
    /*
     * Recherche 2eme "VDO Live" 
     */
    for ( ; i < lu ; i++) {
        if (memcmp(buffer + i, "VDO Live", 8) == 0) {
            pos = i + 10 ;
            break ;
            }
        }
    if (pos) {
        buffer[pos] = server_udp_port >> 8 ;
        buffer[pos+1] = server_udp_port ;
                if (lflg) fprintf(stderr, "%s\n", buffer + pos + 10) ;
        }

    write(fd_to_proxy, buffer, lu) ;
    if (dflg) {
        printf("To server : (%d)\n", lu) ;
        fflush(stdout) ;
        hexdump_err(buffer, lu) ;
        }

    FD_ZERO() ;
    maxfd = (fd_to_proxy > newfd) ? fd_to_proxy : newfd ;
    if (sockudp > maxfd) maxfd = sockudp ;
    maxfd++ ;
    for (;;) {
        FD_SET(newfd, ) ;
        FD_SET(fd_to_proxy, ) ;
        FD_SET(sockudp, ) ;
        i = select(maxfd, , 0, 0, 0) ;

        /* Is there a DATAGRAM ? */
        if (FD_ISSET(sockudp, )) {
                        fromlen = sizeof(from) ;
            res = recvfrom(sockudp, buffer, sizeof(buffer), 0,
                        (struct sockaddr *) , ) ;
            if (res > 0) {
                if (dflg) fprintf(stderr, "UDP (%d) %x:%d %x\n", res,
                        ntohl(from.sin_addr.s_addr),
                        ntohs(from.sin_port),
                        ntohs(from.sin_port)) ;
                                if (!already_bind) {
                                        int on = 1 ;
                                        already_bind = 1 ;
                                    outudp = from ;
                                    if (setsockopt(sockcl, SOL_SOCKET,
SO_REUSEADDR, (char *) , sizeof(on)) < 0) {
                                        perror("setsockopt(REUSEADDR)
problem") ;
                                        }
                                    if (bind(sockcl, (struct sockaddr *)
, sizeof(outudp)) == -1)
                                        perror("bind sockl") ;
                                        }
                sendto(sockcl, buffer, res, 0, (struct sockaddr *),
                    sizeof(struct sockaddr_in)) ;
                }
            }
        /* Is there any data from the client ? */
        if (FD_ISSET(newfd, )) {
            lu = read(newfd, buffer, sizeof(buffer)) ;
            if (lu <= 0) break ;
            write(fd_to_proxy, buffer, lu) ;
            if (dflg > 2) {
                printf("From client: (%d)\n", lu) ;
                fflush(stdout) ;
                hexdump_err(buffer, lu) ;
                }
            }
        /* Is there any data from server ? */
        if (FD_ISSET(fd_to_proxy, )) {
            lu = read(fd_to_proxy, buffer, sizeof(buffer)) ;
            if (lu <= 0) break ;
            write(newfd, buffer, lu) ;
            if (dflg > 2) {
                printf("From Proxy (%d):\n", lu) ;
                fflush(stdout) ;
                hexdump_err(buffer, lu) ;
                }
            }
        }
    _exit(0) ;
    }

/*
 * This function setups the listen port, and forks a child for each
 * connection
 */
void wait_conn(void)
    {
    struct  sockaddr_in addr;
    int sock, newfd;
    int on ;

    if( (sock = socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
        perror("socket problem");
        exit(1);
    }
    memset(,0,sizeof(addr));
    addr.sin_port = htons(listen_port);
    addr.sin_family = AF_INET;
    on = 1 ;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) , sizeof(on))
< 0) {
        perror("REUSEADDR problem") ;
        }

    if (bind(sock, (struct sockaddr *), sizeof(addr)) ) {
        perror("bind problem");
        exit(1);
    }
    if( listen(sock, 5) < 0 ) {
        perror("listen problem");
        exit(1);
    }
    signal(SIGCLD, SIG_IGN) ;
    while (1) {
        if ((newfd=accept(sock, 0, 0) ) < 0) {
            perror("accept");
            continue ;
/*          exit(1);*/
            }
        if (fork() == 0) {
            close(sock) ;
            do_child(newfd) ;
            }
        close(newfd) ;
        }
    }

void usage(int exitcode)
{
fprintf(stderr, "Usage : vdoproxy [-V] [-d] [-l]\n") ;
fprintf(stderr, " -V : Display usage and version.\n") ;
fprintf(stderr, " -d : increase debug level.\n") ;
fprintf(stderr, " -l : log\n") ;
exit(exitcode) ;
}

int main(int argc, char **argv)
{
int c ;
extern int optind ;
extern char *optarg ;

while ((c = getopt(argc, argv, "Vdl")) != EOF) {
    switch (c) {
        case 'V' : usage(0) ; break ;
        case 'd' : dflg++ ; break ;
        case 'l' : lflg++ ; break ;
        default : usage(1) ;
        }
    }
wait_conn() ;
return 0 ;
}

Als Vergleich mag der Abschnitt über die Absicherung von PERL Skripten dienen: Kapitel PERL Sicherheit bei WWW-Servern....


Weiter Zurück [Inhalt] Online Suche im Handbuch LITTLE-IDIOT NETWORKING