<<< Date Index >>>     <<< Thread Index >>>

[Ulf Harnhammar]: LHA Advisory + Patch



-- 
David Mirza Ahmad
Symantec 

PGP: 0x26005712
8D 9A B1 33 82 3D B3 D0 40 EB  AB F0 1E 67 C6 1A 26 00 57 12
LHa buffer overflows and directory traversal problems v2


PROGRAM: LHa (Unix version)
VENDOR: various people
VULNERABLE VERSIONS: 1.14d to 1.14i
                     1.17 (Linux binary)
                     possibly others
IMMUNE VERSIONS: 1.14i with my patch applied
                 1.14h with my patch applied
LHa 1.14: http://www2m.biglobe.ne.jp/~dolphin/lha/lha.htm
          http://www2m.biglobe.ne.jp/~dolphin/lha/prog/
LHa 1.17: http://www.infor.kanazawa-it.ac.jp/~ishii/lhaunix/
REFERENCES: CAN-2004-0234 (buffer overflows)
            CAN-2004-0235 (directory traversal)


* DESCRIPTION *


LHa is a console-based program for packing and unpacking LHarc
archives.

It is one of the packages in Red Hat Linux, Fedora Core, SUSE
Linux, Debian GNU/Linux (non-free), Mandrakelinux, Slackware Linux,
Gentoo Linux, Yellow Dog Linux, Conectiva Linux and ALT Linux.
It is also included in the port/package collections for FreeBSD,
OpenBSD and NetBSD. There are also other programs like WinZip, WinRar
and CommuniGate Pro McAfee plugin that use this codebase.


* OVERVIEW *


LHa has two stack-based buffer overflows and two directory traversal
problems. They can be abused by malicious people in many different
ways: some mail virus scanners require LHa and run it automatically
on attached files in e-mail messages. Some web applications allow
uploading and unpacking of LHarc archives. Some people set up their
web browsers to start LHa automatically after downloading an LHarc
archive. Finally, social engineering is probably quite effective
in this case.


* TECHNICAL DETAILS *


a) two stack-based buffer overflows

The buffer overflows in LHa occur when testing (t) or extracting
(x) archives where the archive contents have too long filenames
or directory names. The cause of the problem is the function
get_header() in header.c. This function first reads the lengths of
filenames or directory names from the archive, and then it reads
that many bytes to a char array (one for filenames and one for
directory names) without checking if the array is big enough.

By exploiting this bug, you get control over several registers
including EIP, as you can see in this session capture:

$ lha t buf_oflow.lha
LHa: Error: Unknown information UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUU
Segmentation fault
$ lha x buf_oflow.lha
LHa: Error: Unknown information UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUU
Segmentation fault
$ gdb lha
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you are welcome to change it and/or distribute copies of it under
certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for
details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb) r x buf_oflow.lha
Starting program: /usr/bin/lha x buf_oflow.lha
LHa: Error: Unknown information UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUU

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()
(gdb) bt
#0  0x55555555 in ?? ()
Cannot access memory at address 0x55555555
(gdb) i r
eax            0x4001e4a0       1073865888
ecx            0xffffffe0       -32
edx            0x24     36
ebx            0x55555555       1431655765
esp            0xbfffdd50       0xbfffdd50
ebp            0x55555555       0x55555555
esi            0x55555555       1431655765
edi            0x55555555       1431655765
eip            0x55555555       0x55555555
eflags         0x210282 2163330
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x33     51
(gdb) r t buf_oflow.lha
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/bin/lha t buf_oflow.lha
LHa: Error: Unknown information UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUU

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()
(gdb) bt
#0  0x55555555 in ?? ()
Cannot access memory at address 0x55555555
(gdb) i r
eax            0x4001e4a0       1073865888
ecx            0xffffffe0       -32
edx            0x24     36
ebx            0x55555555       1431655765
esp            0xbfffe6d0       0xbfffe6d0
ebp            0x55555555       0x55555555
esi            0x55555555       1431655765
edi            0x55555555       1431655765
eip            0x55555555       0x55555555
eflags         0x210286 2163334
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x33     51
(gdb) q
The program is running.  Exit anyway? (y or n) y
$


b) two directory traversal problems

LHa has directory traversal problems, both with absolute paths
and relative paths. There is no protection against relative paths
at all, so you can simply use the lha binary to create an archive
with paths like "../../../../../etc/cron.d/evil". There is some
simple protection against absolute paths, namely skipping the first
character if it is a slash, but again you can simply use the binary
to create archives with paths like "//etc/cron.d/evil".


* ATTACHED FILES *


I have written a patch against version 1.14i that corrects all
four problems. I also have some test archives that I will mail to
anyone who wants them.


// Ulf Harnhammar
Advogato diary :: http://www.advogato.org/person/metaur/
idiosynkratisk (Swedish electropop zine) :: http://idiosynkratisk.tk/
Debian Security Audit Project :: http://shellcode.org/Audit/

--- header.c.old        2000-10-05 19:36:03.000000000 +0200
+++ header.c    2004-04-17 23:55:54.000000000 +0200
@@ -538,6 +538,10 @@
                                /*
                                 * filename
                                 */
+                               if (header_size >= 256) {
+                                 fprintf(stderr, "Possible buffer overflow 
hack attack, type #1\n");
+                                 exit(109);
+                               }
                                for (i = 0; i < header_size - 3; i++)
                                        hdr->name[i] = (char) get_byte();
                                hdr->name[header_size - 3] = '\0';
@@ -547,6 +551,10 @@
                                /*
                                 * directory
                                 */
+                               if (header_size >= FILENAME_LENGTH) {
+                                 fprintf(stderr, "Possible buffer overflow 
hack attack, type #2\n");
+                                 exit(110);
+                               }
                                for (i = 0; i < header_size - 3; i++)
                                        dirname[i] = (char) get_byte();
                                dirname[header_size - 3] = '\0';
--- lhext.c.old 2000-10-04 16:57:38.000000000 +0200
+++ lhext.c     2004-04-18 01:27:44.000000000 +0200
@@ -190,8 +190,13 @@
                q = (char *) rindex(hdr->name, '/') + 1;
        }
        else {
+               if (is_directory_traversal(q)) {
+                 fprintf(stderr, "Possible directory traversal hack attempt in 
%s\n", q);
+                 exit(111);
+               }
+
                if (*q == '/') {
-                       q++;
+                       while (*q == '/') { q++; }
                        /*
                         * if OSK then strip device name
                         */
@@ -419,6 +424,33 @@
        return;
 }
 
+int
+is_directory_traversal(char *string)
+{
+  unsigned int type = 0; /* 0 = new, 1 = only dots, 2 = other chars than dots 
*/
+  char *temp;
+
+  temp = string;
+
+  while (*temp != 0) {
+    if (temp[0] == '/') {
+      if (type == 1) { return 1; }
+      type = 0;
+      temp++;
+      continue;
+    }
+
+    if ((temp[0] == '.') && (type < 2))
+      type = 1;
+    if (temp[0] != '.')
+      type = 2;
+
+    temp++;
+  } /* while */
+
+  return (type == 1);
+}
+
 /* Local Variables: */
 /* mode:c */
 /* tab-width:4 */