Бесплатно Экспресс-аудит сайта:

28.06.2014

Закрыта 20-летняя уязвимость в алгоритме сжатия LZO

На этой неделе Маркус Оберхамер, наконец-то, исправил свой баг 20-летней давности в алгоритме сжатия Lempel-Ziv-Oberhumer (LZO). Это очень хорошая новость, учитывая, что уязвимость представляла собой довольно серьёзную опасность, а LZO используется во многих приложениях. Вот некоторые из пострадавших: ядро Linux, различные файловые системы, телефоны Samsung Android, OpenVPN, MPlayer2, Libav, FFmpeg, автомобили, самолёты и даже марсоход Curiosity.

Оригинальную версию LZO Оберхамер написал в 1994 году. Для своего времени элегантный алгоритм стал настоящим откровением: степень сжатия в 4-5 раз превышала популярные тогда архиваторы zlib и bzip.

Свободной программой, реализующей LZO, является lzop. Исходная библиотека была написана на ANSI C и доступна под лицензией GPL. Также существуют реализации LZO на языках Ассемблер (x86), Perl, Python и Java.

С самого начала в LZO присутствовала уязвимость в режиме «безопасного» разархивирования, которая могла привести к переполнению буфера, если на вход подать вредоносные данные. Хотя за прошедшие десятилетия создано несколько вариаций LZO для различных платформ, в том числе последняя версия LZ4, но все они заимствовали без изменений фрагмент оригинального кода c багом.

56 if (likely(state == 0)) { 57 if (unlikely(t == 0)) { 58 while (unlikely(*ip == 0)) { 59 t += 255; 60 ip++; 61 NEED_IP(1); 62 } 63 t += 15 + *ip++; 64 } 65 t += 3;

В режиме безопасного разархивирования есть детектор фрагментов неархивируемого оригинального содержимого (Literal Run). Если встречается нулевой байт, то переменная t из приведённого выше листинга увеличивает своё значение на 255. Уязвимость здесь очевидна, потому что t может увеличиться до очень большого числа. Скажем, если t представляет собой 32-битное число, то требуется 16 мегабайт нулевых байтов, чтобы вызвать переполнение буфера.

В последующем коде t используется в качестве параметра для установки размера буфера памяти.

66 copy_literal_run: 67 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) 68 if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) { 69 const unsigned char *ie = ip + t; 70 unsigned char *oe = op + t; 71 do { 72 COPY8(op, ip); 73 op += 8; 74 ip += 8; 75 COPY8(op, ip); 76 op += 8; 77 ip += 8; 78 } while (ip < ie); 79 ip = ie; 80 op = oe; 81 } else 82 #endif

Такая реализация делает банальной атаку типа «отказ в обслуживании» (DoS), а теоретически можно даже запустить на исполнение произвольный код (атака RCE). Напомним, что уязвимость десятилетиями присутствует в ядре Linux и множестве других программ. Правда, в разных вариациях LZO/LZ4 вектор атаки слегка отличается, но атака всё равно возможна на многих версиях алгоритма и на многих платформах, хотя не на всех. Например, в варианте реализации LZO в ядре Linux возможна DoS-атака на платформах i386 и PowerPC, но не возможна RCE, а в варианте реализации LZ4 в ядре Linux возможны обе атаки (кроме 64-битных версий Linux).

В новой версии LZO 2.07, которая вышла 25 июня 2014 года, Маркус Оберхамер исправил свою ошибку.