Современные стандарты сжатия видео — настоящее чудо скрытой сложности и результат десятилетий научной работы. Спецификация H.264 — это около 800 страниц правил, определяющих, как декодировать видео. Но чем больше сложности, тем выше риски для безопасности, легче пропустить ошибку в битовом потоке, который слишком труден для понимания и декодирования.

Если посмотреть на экосистему декодирования, то здесь в связке работают инструменты на нескольких уровнях из аппаратных ускорителей на CPU и GPU (список производителей аппаратных декодеров), драйверов и привилегированных программных компонентов. Все вместе они образуют сложнейший неоднородный коктейль привилегированного, практически нетестируемого и уязвимого кода.

В итоге мы приближаемся к тому, что вирусы можно будет незаметно интегрировать в видеоролики и распространять через популярные видеоплатформы, эксплуатируя уязвимости в аппаратных декодерах на смартфонах и в программных декодерах браузеров на ПК.

Группа исследователей Вилли Васкес (Willy R. Vasquez), Стивен Чеквей (Stephen Checkoway) и Ховав Шахам (Hovav Shacham) из университета Остина (США) и Оберлинского колледжа (США) поставили целью изучить безопасность современной инфраструктуры распространения видео, а конкретно — видеодекодеров H.264 как самого популярного формата видео на сегодняшний день. Они разработали специальный фреймворк H26Forge для анализа, генерации и манипуляции синтаксически корректными, но семантически не стандартными видеороликами. Такие видеоролики формально соответствуют формату H.264, но вызывают различные сбои в работе декодеров.


Рис. 1. Архитектура H26Forge (30 000 строк кода на Rust)

С помощью этого фреймворка исследователи обнаружили ряд багов в системе декодирования H.264 на разных платформах, включая повреждение памяти ядра в iOS, повреждение памяти в Firefox и VLC под Windows, а также ошибки в памяти видеоускорителей и в памяти приложений ядра процессора в нескольких устройствах Android.


Рис. 2

Все уязвимости официально задокументированы в бюллетенях CVE-2022-3266, CVE-2022-32939, CVE-2022-42846, CVE-2022-42850 и CVE-2022-22675.

Кодек H.264 работает, находя сходство между кадрами видео и кодируя эту информацию, а затем отправляя инструкции о том, как воссоздать изображение в конечной точке. Эти инструкции известны как элементы синтаксиса, а значения, которые они принимают, называются семантикой. Значения кодируются энтропией с помощью таких алгоритмов, как exp-Golomb, CABAC или CAVLC.

Вот некоторые элементы синтаксиса H.264:



С помощью изменения перечисленных параметрами можно манипулировать поведением декодера, чтобы он прочитал память за пределами выделенного массива. Например, на следующей иллюстрации схематично показан механизм эксплуатации уязвимости CVE-2022-22675.


Рис. 3. Эксплуатация уязвимости CVE-2022-22675. Слева правильно упорядоченный битовый поток H.264, считанный сверху вниз, а справа — декодированное содержимое в памяти по мере заполнения. Во второй строчке форсируется перезапись PPS значением num_ref_idx_ l0_active_minus1, что приводит в итоге к записи в память значения произвольной длины путём регулировки смещения в каждом последующем фрагменте, записывая значения в обратном порядке. Код для эксплуатации уязвимости опубликован в листинге 2 на стр. 17–18

Результаты работы оформлены в научной статье, а в более популярной форме опубликованы в мастодон-треде одного из авторов.

Новый канал распространения вирусов


Можно вспомнить, какой дырой для безопасности всей компьютерной инфраструктуры был Adobe Flash, через плагины интегрированный во все браузеры и операционные системы. По сути, один и тот же монолитный плагин стоял на всех компьютерах в мире — и это делало его привлекательной мишенью для взлома. Единственная 0day-уязвимость открывает перед злоумышленниками все двери.

Есть опасность, что такой же универсальной отмычкой для злоумышленников станут кодеки H.264, а распространение вирусов начнётся через YouTube и другие популярные видеоплатформы — и через уязвимости в декодерах браузеров.


Рис. 4. Уязвимость CVE-2022-3266 с чтением памяти из-за границ выделенного диапазона в процессе некорректного программного рендеринга видео в браузере Firefox (исправлено в версии 105). Реализовано путём уменьшения значения SPS во фрейме H.264, что сломало декодер

Хотя на практике таких массовых атак мы ещё не встречали, но экосистема видео в интернете уже созрела для вирусов нового типа, считают исследователи. На форумах HN говорят, что цветная рябь на видео как массовая инфекция — это самое близкое к классической «лавине» (snowcrash), которую мы помним по культовому киберпанковскому произведению Стивенсона, придумавшему понятие «Метавселенная» в 1992 году.



Примечание. Перечисленные в данной статье уязвимости исправлены в последних версиях iOS, VLC и Firefox. Производители аппаратных декодеров H.264 для смартфонов уведомлены о проблеме. Авторы отмечают, что аналогичные уязвимости также присутствуют также в декодерах H.265, но разработанный фреймворк H26Forge не способен сгенерировать видеопоследовательности для их эксплуатации.

Комментарии (10)


  1. VBKesha
    21.05.2023 23:07
    +4

    распространение вирусов начнётся через YouTube и другие популярные видеоплатформы

    За другие платформы не скажу, но ютуб пережимает заливаемые к нему видео. Так что без его прямого желания вряд-ли что-то получится. Другое дело если желание появится.


    1. Tarakanator
      21.05.2023 23:07

      Я не помню чем закончилось, но народ задавался вопросом как залить видео на ютуб так, чтобы оно не перекодироовалось. Точно помню что практические результаты были, но вот избавились ли от перекодирования вообще не знаю.


    1. Chupaka
      21.05.2023 23:07

      Значит, сам Ютуб для начала и взломают :)


  1. Stas911
    21.05.2023 23:07

    Выглядит как весьма заманчивая информация для спецслужб всвязи с распростаненностью устройств, проигрывающих видео.


  1. Helltraitor
    21.05.2023 23:07

    Кажется, мы приходим к вопросу, что нам более важно: скорость или безопасность. Пока нет опции "оба", но, надеюсь, вся языки и среды выполнения будут доработаны в соответствующем ключе

    Meltdown, Spectr, куча разных хаков производительности... Глаз Rust'ом замылен, хочется все переписать (чтобы хотя бы сравнить места, где что-то могло быть не учтено)


    1. unreal_undead2
      21.05.2023 23:07

      Программистов и разработчиков железа надо дорабатывать - выход за пределы массива можно проверить и в C, и в Verilog (вставив проверку так, чтобы она минимально влияла на производительность). Общее правило для любого кодека и подобного софта - надо корректно обрабатывать любой мусор на входе.


      1. Helltraitor
        21.05.2023 23:07

        Если писать код без ошибок, то ошибок и не будет. Жаль, что люди склонны их совершать


        1. unreal_undead2
          21.05.2023 23:07

          Вопрос ещё, конечно, в организации тестирования, code review и т.д. - без этого никакие языки не помогут.


  1. lexxsu
    21.05.2023 23:07
    -1

    Бред, там фиксированное колличество бит выделено в битстриме, т.е. он не может выйти за пределы памяти физически. 264й стар, с кучей различных реализаций кода, прогоните сами стримы на совместость со стандартом, думаю на этом проблема пропадет.


    1. lexxsu
      21.05.2023 23:07

      Давайте разъясню свое мнение, в статье все скинуто в кучу, так что пойдем по порядку:

      1. emulation prevention byte counter overflow:

        что такое prevention byte: заголовок каждого из NAL представляет собой синтаксис "0x000001", для того не ошибиться в стрим добавляют 0x3 (0x00000301), не понимаю для чего ведется из подсчет, когда их все нужно удалить из стрима для получения корректных данных, они не несут никаких данных

      2. подстановка типа IDR вместо и некего исходного значения

        Что это даст - некорректный парсинг и выход по ошибке (ну или идентично ошибке в битстриме, есть отдельные тесты для этого и что с этим делать - зависит от имплементации)

      3. переполнение cpb_cnt_minus1

        А ничего, что еще в коде 2015 года (я уж не знаю сколько оно существует на самом деле) есть проверка на это

        pMVCVUI->cpb_cnt_minus1 = (char) read_ue_v("cpb_cnt_minus1", s, &p_Dec->UsedBits);
        assert(pMVCVUI->cpb_cnt_minus1<=31);

        Даже если вы удалили её, до конца пакета около 24 бита + несколько байт, т.е. не соблюдается поверка на окончание NAL. т.е. максимально вы добавите еще пару фреймов, а не как сказано 224. Это надо специально поменять код, чтобы прийти к этому.

      В последнем примере красиво переведено, но не указано, что это происходит когда h/w вылетает из-за ошибки и приходится декодировать s/w (а так много говорилось про h/w реализацию).

      По факту мы видим, что есть некая попытка "испортить" данные для выхода за пределы размеров, но что-то мне говорит, что даже минимальный тест включает в себя проверку на такие элементарные стримы.