Subject: unace: multiple buffer overflows and directory traversal bugs
Package: unace
Version: 1.2b-2
Severity: grave
Justification: user security hole
Tags: security patch
I have found multiple security vulnerabilities in unace.

There are two buffer overflows when extracting, testing or listing
specially prepared ACE archives. They are caused by wrong usage of
strncpy() with the third parameter coming from the archive. In both
cases, the attacker controls the EIP register.

There are also two buffer overflows when (a) dealing with long (>15600
characters) command line arguments for archive names, and (b) when
preparing a string for printing Ready for next volume messages.

Furthermore, there are directory traversal bugs when extracting ACE
archives. They are both of the absolute ("/etc/nologin") and the relative
("../../../../../../../etc/nologin") type.

All buffer overflows have the identifier CAN-2005-0160, and the directory
traversal bugs have the identifier CAN-2005-0161.

I have attached some test archives and a patch.

// Ulf H�rnhammar for the Debian Security Audit Project
   http://www.debian.org/security/audit/

-- System Information:
Debian Release: 3.1
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)
Kernel: Linux 2.6.8-2-686
Locale: LANG=en_US, LC_CTYPE=en_US (charmap=ISO-8859-1)

Versions of packages unace depends on:
ii  libc6                       2.3.2.ds1-20 GNU C Library: Shared libraries an

-- no debconf information

--- uac_crt.h.old	1998-07-01 10:29:00.000000000 +0200
+++ uac_crt.h	2005-02-14 00:48:35.000000000 +0100
@@ -4,7 +4,7 @@
 
 #include "acestruc.h"
 
-CHAR *ace_fname(CHAR * s, thead * head, INT nopath);
+CHAR *ace_fname(CHAR * s, thead * head, INT nopath, unsigned int size);
 INT  create_dest_file(CHAR * file, INT a);
 
 #ifdef UNIX
--- uac_crt.c.old	1998-07-01 10:29:00.000000000 +0200
+++ uac_crt.c	2005-02-14 02:46:02.000000000 +0100
@@ -33,12 +33,15 @@
 
 /* gets file name from header
  */
-CHAR *ace_fname(CHAR * s, thead * head, INT nopath)
+CHAR *ace_fname(CHAR * s, thead * head, INT nopath, unsigned int size)
 {
-   INT  i;
+   unsigned int i;
    char *cp;
 
-   strncpy(s, (*(tfhead *) head).FNAME, i = (*(tfhead *) head).FNAME_SIZE);
+   i = (*(tfhead *) head).FNAME_SIZE;
+   if (i > (size - 1))
+     i = size - 1;
+   strncpy(s, (*(tfhead *) head).FNAME, i);
    s[i] = 0;
 
    if (nopath)
@@ -56,22 +59,72 @@ CHAR *ace_fname(CHAR * s, thead * head, 
    }
 #endif
 
+   cp = s;
+   while (*cp == '/') cp++;
+   if (cp != s)
+     memmove(s, cp, strlen(cp) + 1);
+
    return s;
 }
 
+int is_directory_traversal(char *str)
+{
+  unsigned int mode, countdots;
+  /* mode 0 = fresh, 1 = just dots, 2 = not just dots */
+  char ch;
+
+  mode = countdots = 0;
+
+  while (ch = *str++)
+  {
+    if ((ch == '/') && (mode == 1) && (countdots > 1))
+      return 1;
+
+    if (ch == '/')
+    {
+       mode = countdots = 0;
+       continue;
+    }
+
+    if (ch == '.')
+    {
+      if (mode == 0)
+        mode = 1;
+
+      countdots++;
+    }
+    else
+      mode = 2;
+  }
+
+  if ((mode == 1) && (countdots > 1))
+    return 1;
+
+  return 0;
+}
+
 void check_ext_dir(CHAR * f)        // checks/creates path of file
 {
    CHAR *cp,
         d[PATH_MAX];
-   INT  i;
+   unsigned int i;
 
    d[0] = 0;
 
+   if (is_directory_traversal(f))
+   {
+      f_err = ERR_WRITE;
+      printf("\n    Directory traversal attempt:  %s\n", f);
+      return;
+   }
+
    for (;;)
    {
       if ((cp = (CHAR *) strchr(&f[strlen(d) + 1], DIRSEP))!=NULL)
       {
          i = cp - f;
+         if (i > (PATH_MAX - 1))
+           i = PATH_MAX - 1;
          strncpy(d, f, i);
          d[i] = 0;
       }
--- unace.c.old	1998-07-01 10:29:00.000000000 +0200
+++ unace.c	2005-02-14 01:43:28.000000000 +0100
@@ -240,6 +240,7 @@ INT read_arc_head(void)         // searc
 INT  open_archive(INT print_err)        // opens archive (or volume)
 {
    CHAR av_str[80];
+   unsigned int copylen;
 
    archan = open(aname, O_RDONLY | O_BINARY);   // open file
 
@@ -263,8 +264,11 @@ INT  open_archive(INT print_err)        
       sprintf(av_str, "\ncreated on %d.%d.%d by ",
               ts_day(adat.time_cr), ts_month(adat.time_cr), ts_year(adat.time_cr));
       printf(av_str);
-      strncpy(av_str, mhead.AV, mhead.AV_SIZE);
-      av_str[mhead.AV_SIZE] = 0;
+      copylen = mhead.AV_SIZE;
+      if (copylen > 79)
+        copylen = 79;
+      strncpy(av_str, mhead.AV, copylen);
+      av_str[copylen] = 0;
       printf("%s\n\n", av_str);
    }
    comment_out("Main comment:");        // print main comment
@@ -300,7 +304,7 @@ void get_next_volname(void)             
 INT  proc_vol(void)                     // opens volume
 {
    INT  i;
-   CHAR s[80];
+   CHAR s[PATH_MAX + 80];
 
    // if f_allvol_pr is 2 we have -y and should never ask
    if ((!fileexists_insense(aname) && f_allvol_pr != 2) || !f_allvol_pr)
@@ -428,7 +432,7 @@ void extract_files(int nopath, int test)
       if (head.HEAD_TYPE == FILE_BLK)
       {
          comment_out("File comment:");   // show file comment
-         ace_fname(file, &head, nopath); // get file name
+         ace_fname(file, &head, nopath, sizeof(file)); // get file name
          printf("\n%s", file);
          flush;
          dcpr_init_file();               // initialize decompression of file
@@ -496,7 +500,7 @@ void list_files(int verbose)
       if (head.HEAD_TYPE == FILE_BLK)
       {
          ULONG ti=fhead.FTIME;
-         ace_fname(file, &head, verbose ? 0 : 1); // get file name
+         ace_fname(file, &head, verbose ? 0 : 1, sizeof(file)); // get file name
 
          size  += fhead.SIZE;
          psize +=
@@ -588,7 +592,8 @@ int main(INT argc, CHAR * argv[])       
 
       init_unace();                              // initialize unace
 
-      strcpy(aname, argv[arg_cnt]);              // get archive name
+      strncpy(aname, argv[arg_cnt], sizeof(aname) - 4);  // get archive name
+      aname[sizeof(aname) - 5] = '\0';
       if (!(s = (CHAR *) strrchr(aname, DIRSEP)))
          s = aname;
       if (!strrchr(s, '.'))

Attachment: bufoflow1.ace
Description: Binary data

Attachment: bufoflow2.ace
Description: Binary data

Attachment: dirtraversal1.ace
Description: Binary data

Attachment: dirtraversal2.ace
Description: Binary data

Reply via email to