На этой неделе Маркус Оберхамер, наконец-то, исправил свой баг 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 года, Маркус Оберхамер исправил свою ошибку.



Оставить мнение