首页 -> 安全研究

安全研究

安全漏洞
Zlib压缩库gzprintf()缓冲区溢出漏洞

发布日期:2003-02-23
更新日期:2003-03-03

受影响系统:
GNU zlib 1.1.4
描述:
BUGTRAQ  ID: 6913
CVE(CAN) ID: CVE-2003-0107

zlib是一款流行的压缩库,使用于多种应用程序中,包括有名的SSH实现。

zlib的gzprintf()函数没有正确检查用户提供的数据,远程攻击者可以利用这个漏洞进行缓冲区溢出攻击,可能以使用此函数的应用程序进程权限在系统上执行任意指令。

zlib包含gzprintf()函数,类似fprintf(),如果提交给此函数的参数超过Z_PRINTF_BUFSIZE所定义的字节数(默认4096),可触发缓冲区溢出,精心构建提交的数据可能以使用此函数的应用程序进程权限在系统上执行任意指令。

<*来源:Richard Kettlewell (rjk@greenend.org.uk
  
  链接:http://www.securityfocus.com/archive/1/312869
*>

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* 第三方补丁如下:

diff -Naur zlib-1.1.4/ChangeLog zlib-1.1.4-vsnprintf/ChangeLog
--- zlib-1.1.4/ChangeLog    2002-03-11 15:02:35.000000000 +0000
+++ zlib-1.1.4-vsnprintf/ChangeLog    2003-02-24 05:31:41.000000000 +0000
@@ -1,6 +1,13 @@

        ChangeLog file for zlib

+Changes in 1.1.4-patched (23 February 2003)
+- fix a security vulnerability related to improper use of snprintf/vsnprintf
+  function.
+- ./configure now detects the presence of snprintf/vsnprintf and enables it
+  automatically if present.
+- README.vsnprintf added.
+
Changes in 1.1.4 (11 March 2002)
- ZFREE was repeated on same allocation on some error conditions.
   This creates a security problem described in
diff -Naur zlib-1.1.4/README.vsnprintf zlib-1.1.4-vsnprintf/README.vsnprintf
--- zlib-1.1.4/README.vsnprintf    1970-01-01 00:00:00.000000000 +0000
+++ zlib-1.1.4-vsnprintf/README.vsnprintf    2003-02-24 05:13:28.000000000 +0000
@@ -0,0 +1,23 @@
+During a recent audit of zlib-1.1.4, a buffer-overflow and string-format
+vulnerability was found in the gzprintf() function.  This has been corrected in
+this version of zlib; in addition, some ./configure checks have been added to
+make sure the host system can utilize the corrections fully.
+
+As a result, it is now strongly recommended that your host system or compiler
+provide a fully C99-compliant implementation of the vsnprintf() function.
+Anything less will reduce the functionality and/or security of the gzprintf()
+function.  The most critical aspect is that vsnprintf() should be present and
+should provide a return value.  If this function is missing, one of the
+fallback functions (vsprintf(), snprintf(), vsnprintf()) will have to be used,
+and if so, they too should return a value.  If your system is lacking in any of
+these aspects, the ./configure script should warn you and refer you to this
+file.
+
+In addition, the HAS_vsnprintf and HAS_snprintf macros are automatically
+defined if these functions are available.  zlib-1.1.4 and older versions did
+not do this, potentially leading to a broken and vulnerable zlib even when the
+host system supported the requisite functionality to avoid this.
+
+
+                                  -- Kelledin <kelledin@users.sourceforge.net>
+
diff -Naur zlib-1.1.4/configure zlib-1.1.4-vsnprintf/configure
--- zlib-1.1.4/configure    1998-07-08 18:19:35.000000000 +0000
+++ zlib-1.1.4-vsnprintf/configure    2003-02-24 05:13:28.000000000 +0000
@@ -156,6 +156,209 @@
fi

cat > $test.c <<EOF
+#include <stdio.h>
+
+#if (defined(__MSDOS__) || defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)) && !defined(STDC)
+#  define STDC
+#endif
+
+int main() {
+  int i;
+
+  i=0;
+#ifndef STDC
+  choke me
+#endif
+
+  return 0;
+}
+EOF
+
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking whether to use vsnprintf() or snprintf()... using vsnprintf()"
+
+  cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...) {
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+  if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+    CFLAGS="$CFLAGS -DHAS_vsnprintf"
+    echo "Checking for vsnprintf() in stdio.h... Yes."
+
+    cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...) {
+  int i;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  i=vsnprintf(buf, sizeof(buf), fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      CFLAGS="$CFLAGS -DHAS_vsnprintf_return"
+      echo "Checking for return value of vsnprintf()... Yes."
+    else
+      echo "Checking for return value of vsnprintf()... No."
+      echo "  WARNING: apparently vsnprintf() does not return a value.  zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities.  See README.vsnprintf for more info."
+      echo
+    fi
+  else
+    echo "Checking for vsnprintf() in stdio.h... No."
+    echo "  WARNING: vsnprintf() not found, falling back to vsprintf().  zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities.  See README.vsnprintf for more info."
+    echo
+
+    cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...) {
+  int i;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  i=vsprintf(buf, fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      CFLAGS="$CFLAGS -DHAS_vsprintf_return"
+      echo "Checking for return value of vsprintf()... Yes."
+    else
+      echo "Checking for return value of vsprintf()... No."
+      echo "  WARNING: apparently vsprintf() does not return a value.  zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities.  See README.vsnprintf for more info."
+      echo
+    fi
+  fi
+else
+  echo "Checking whether to use vsnprintf() or snprintf()... using snprintf()"
+
+  cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest() {
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  snprintf(buf, sizeof(buf), fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest());
+}
+EOF
+
+  if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+    CFLAGS="$CFLAGS -DHAS_snprintf"
+    echo "Checking for snprintf() in stdio.h... Yes."
+
+    cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest() {
+  int i;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  i=snprintf(buf, sizeof(buf), fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      CFLAGS="$CFLAGS -DHAS_snprintf_return"
+      echo "Checking for return value of snprintf()... Yes."
+    else
+      echo "Checking for return value of snprintf()... No."
+      echo "  WARNING: apparently snprintf() does not return a value.  zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities.  See README.vsnprintf for more info."
+      echo
+    fi
+  else
+    echo "Checking for snprintf() in stdio.h... No."
+    echo "  WARNING: snprintf() not found, falling back to sprintf().  zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities.  See README.vsnprintf for more info."
+    echo
+
+    cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest() {
+  int i;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  i=sprintf(buf, fmt, ap);
+  return 0;
+}
+
+int main() {
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      CFLAGS="$CFLAGS -DHAS_sprintf_return"
+      echo "Checking for return value of sprintf()... Yes."
+    else
+      echo "Checking for return value of sprintf()... No."
+      echo "  WARNING: apparently sprintf() does not return a value.  zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities.  See README.vsnprintf for more info."
+      echo
+    fi
+  fi
+fi
+
+cat > $test.c <<EOF
#include <errno.h>
int main() { return 0; }
EOF
diff -Naur zlib-1.1.4/gzio.c zlib-1.1.4-vsnprintf/gzio.c
--- zlib-1.1.4/gzio.c    2002-03-11 13:16:01.000000000 +0000
+++ zlib-1.1.4-vsnprintf/gzio.c    2003-02-24 05:18:44.000000000 +0000
@@ -529,14 +529,42 @@
     int len;

     va_start(va, format);
+
+    /* 2003/02/23: Add proper length checking here, if possible.
+     *
+     *    -- Kelledin
+     */
#ifdef HAS_vsnprintf
-    (void)vsnprintf(buf, sizeof(buf), format, va);
+#  ifdef HAS_vsnprintf_return
+    len=vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+
+    if (len <= 0 || len >= sizeof(buf)) {
+        /* Resulting string too large to fit in the buffer. */
+        return 0;
+    }
+#  else
+    vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+    len=strlen(buf);
+    if (len <= 0) return 0;
+#  endif
#else
-    (void)vsprintf(buf, format, va);
-#endif
+#  ifdef HAS_vsprintf_return
+    len=vsprintf(buf, format, va);
+    va_end(va);
+
+    if (len <= 0 || len >= sizeof(buf)) {
+        /* Resulting string too large to fit in the buffer. */
+        return 0;
+    }
+#  else
+    vsprintf(buf, format, va);
     va_end(va);
-    len = strlen(buf); /* some *sprintf don't return the nb of bytes written */
+    len=strlen(buf);
     if (len <= 0) return 0;
+#  endif
+#endif

     return gzwrite(file, buf, (unsigned)len);
}
@@ -552,15 +580,41 @@
     char buf[Z_PRINTF_BUFSIZE];
     int len;

+    /* 2003/02/23: Add proper length checking here when possible.
+     *
+     *    -- Kelledin
+     */
#ifdef HAS_snprintf
+#  ifdef HAS_snprintf_return
+    len=snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+
+    if (len <= 0 || len >= sizeof(buf)) {
+        /* Resulting string too large to fit in the buffer. */
+        return 0;
+    }
+#  else
     snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
         a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    len=strlen(buf);
+    if (len <= 0) return 0;
+#  endif
#else
+#  ifdef HAS_sprintf_return
+    len=sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+
+    if (len <= 0 || len >= sizeof(buf)) {
+        /* Resulting string too large to fit in the buffer. */
+        return 0;
+    }
+#  else
     sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
        a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
-#endif
-    len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */
+    len=strlen(buf);
     if (len <= 0) return 0;
+#  endif
+#endif

     return gzwrite(file, buf, len);
}

厂商补丁:

GNU
---
zlib-1.4.4版本不存在此漏洞,请下载使用:

http://www.gzip.org/zlib/

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