Security Week 25: В *NIX реанимировали древнюю уязвимость, WannaCry оказался не доделан, ЦРУ прослушивает наши роутеры

    Земля, 2005 год. По всей планете происходят загадочные события: Nokia выводит на рынок планшет на Linux, в глубокой тайне идет разработка игры с участниками группы Metallica в главных ролях, Джобс объявил о переходе Маков на платформу Intel.

    Тем временем на конференции CancSecWest Гаэль Делалло из Beijaflore представил фундаментальный доклад об уязвимостях системы управления памятью в разнообразных NIX-ах, и проиллюстрировал свои находки эксплойтами для Apache. Все запатчились. Прошло несколько лет.

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

    2016 год. Гуглевский Project Zero разродился исследованием эксплуатации уязвимостей стека ядра под Ubuntu. Оберайде передает в комментах привет. Убунта запатчилась.

    2017 год. Никогда такого не было, и вот опять. Qualys научилась мухлевать со стеком юзермода в любых никсах, согласно идеям Делалло.

    Эксплойты стека юзермода основаны на простом вопросе – если динамическая область (heap) и стек будут расти навстречу друг другу, то что будет, когда они встретятся? Однако не надо думать, что ядра никсов разрабатывают дураки! Они, конечно же, подумали о таком развитии событий (ну, к нашему времени точно уже додумались). И во всех современных юниксоподобных операционках стек юзермода огорожен специальной страницей памяти, попытка доступа к которой вызывает исключение или завершение процесса.

    Но вот что странно. Внезапно оказалось, что эту сторожевую страницу можно успешно обойти, для чего есть много способов. И об этом рассказывал еще Делалло в 2005 году. Все опять патчатся, весело и с песней.

    Принцип эксплуатации этого бородатого бага основан на увеличении объема стека без записи в него. Это делается по-разному в разных ОС, например, с помощью рекурсивного вызова процедуры, или многомегабайтными аргументами командной строки. Указатель стека перемещается на его начало (нижний адрес), стек резко наращивается, и – хоп, – указатель стека оказывается уже за сторожевой страницей. Доступа к самой сторожевой странице не происходит, ошибки нет. Получается, что область стека перекрывается с динамической областью.



    Это позволяет подменить в стеке адрес возврата из функции и таким образом запустить произвольный код с повышенными правами.

    Qualys испытали эту методику лишь локально, но теоретически подобный трюк можно провернуть удаленно, ну или, как минимум, это будет полезно троянцам для повышения собственных привилегий. Исследователи видят два пути исправления этой уязвимости: фантастический и реалистический. Фантастический заключается в раздувании сторожевой страницы как минимум до 1 Мб, а лучше больше. А реалистический – это всего-то перекомпилировать весь код пространства пользователя с опцией -fstack-check в GCC, после чего указатель стека уже не сможет перепрыгивать сторожевую страницу без записи в нее. А в FreeBSD еще стоит хотя бы включить эту самую сторожевую страницу – по умолчанию она там не задействуется.

    Исследователь предположил преждевременные роды WannaCry

    Роковой для многих троянец WannaCry то ли сделан кривыми руками, то ли вырвался на волю до того, как его доделали. Такую любопытную теорию высказал Джейк Вильямс из Rendition InfoSec, после глубокого анализа новостей кода EternalBlue и WannaCry. По его мнению, создатели рансомвары допустили несколько «ошеломляющих ошибок».

    Для начала, очень странным выглядит хардкодинг биткойн-адреса для выплаты выкупа. Мало того, что это позволило кому-то предприимчивому урвать себе малую толику, перебив в хекс-редакторе адрес, так еще и контролировать выплату становится невозможно – как справедливо подметил Вильямс, попробуй разберись, кому именно из жертв принадлежат падающие в кошелек транзакции. И еще, проследить дальнейшую судьбу денег с одного адреса не так уж сложно (миксеры не всегда спасают), то есть проблемы с выводом суммы обеспечены.

    Гораздо практичнее генерить собственный адрес для каждого заражения, или хотя бы для нескольких заражений, это сразу сняло бы большинство проблем. Собственно, опытные рансомварщики так и делают. Но не авторы WannaCry. Всего в пойманных семплах насчитывается три варианта адреса, и вполне вероятно, что изначальным злоумышленникам принадлежит лишь один из них.

    Туда же, в копилку ляпов – знаменитый «рубильник» WannaCry, домен, при обнаружении которого в Сети троянец прекращает работу. Само по себе это вполне общепринятая практика, но, помилуйте, что мешало сделать чуть более сложную проверку, чем код состояния 200? В случае многих других ботов обмен с сервером управления шифруется и троянец выключается только по команде.

    С гипотезой, что вонакрай – детище криворуких «скрипт-киддис», не вяжется ряд признаков, указывающих на северокорейское происхождение этой эпидемии. Группа Lazarus, которую подозревают в связи с WannaCry, не давала повода обвинить ее в непрофессионализме. Поэтому Вильямс предложил другое объяснение. По его мнению, недоделанный WannaCry попросту случайно вырвался из тестовой среды и дальше пошел распространяться неконтролируемо.

    Это чертовски похоже на правду, хотя не очень понятно, что помешало создателям вовремя дернуть за рубильник, зарегав стоп-домен, пока распространение троянца не успело принять характер эпидемии. И еще – забавно будет, если окажется, что захардкоденный биткойн-адрес представляет собой лишь плейсхолдер, и не соответствует никакому реальному кошельку. По крайней мере, списаний с известных кошельков WannaCry пока не было.

    ЦРУ годами следит за нашими роутерами

    На Wikileaks опубликованы подробности программы ЦРУ по мониторингу трафика, проходящего через роутеры D-Link, Linksys, 3Com и Panet Tec. Поэтично названная Cherry Blossom, программа включает в себя создание особых прошивок, которые прошиваются на роутеры удаленно. Прошивка выглядит как настоящая, но только выдирает из трафика и передает на свой сервер имейл-адреса, имена, встречающиеся в чатах, MAC-адреса, номера VoIP. А в случае необходимости может даже перенаправлять трафик «куда надо».



    Единственным более-менее сложным этапом операции выглядит внедрение прошивки на ничего не подозревающий роутер. Однако, что получается у Mirai, точно по плечу детищу ЦРУ – для роутеров агентство разработало эксплойты Tomato и Surfside. Работают они не везде, и для прочих случаев в документации рекомендовано внедрять прошивку оперативным путем. То есть внедриться в компанию-поставщика и втихаря прошить все роутеры на складе. Романтика шпионской работы, как она есть. Все-таки жив в ЦРУ дух старой школы!

    Древности


    «Justice»

    Очень опасен, поражает COM-файлы при обращении к ним функциями DOS 43h, 4Bh, 3Dh, 56h. Записывается в конец файлов и изменяет 5 байт их начала (NOP; NOP; JMP Loc_Virus). COMMAND.COM заражается по алгоритму вируса «Lehigh». Периодически направляет записываемую на диск информацию в сектор с другим номером. Содержит текст «AND JUSTICE FOR ALL». Перехватывает int 13h и int 21h.

    Цитата по книге «Компьютерные вирусы в MS-DOS» Евгения Касперского. 1992 год. Страницa 72.

    Disclaimer: Данная колонка отражает лишь частное мнение ее автора. Оно может совпадать с позицией компании «Лаборатория Касперского», а может и не совпадать. Тут уж как повезет.
    «Лаборатория Касперского» 304,78
    Ловим вирусы, исследуем угрозы, спасаем мир
    Поделиться публикацией
    Комментарии 11
    • +4
      Принцип эксплуатации этого бородатого бага основан на увеличении объема стека без записи в него. Это делается по-разному в разных ОС, например, с помощью рекурсивного вызова процедуры, или многомегабайтными аргументами командной строки. Указатель стека перемещается на его начало (нижний адрес), стек резко наращивается, и – хоп, – указатель стека оказывается уже за сторожевой страницей. Доступа к самой сторожевой странице не происходит, ошибки нет. Получается, что область стека перекрывается с динамической областью какого-то процесса.

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


      У вас здесь написана ерунда, потому что вы смешали в кучу пользовательский и ядерный стек.
      Смотрите: сторожевая страница находится на границе пользовательского стека.

      Указатель стека перемещается на его начало (нижний адрес), стек резко наращивается, и – хоп, – указатель стека оказывается уже за сторожевой страницей. Доступа к самой сторожевой странице не происходит, ошибки нет. Получается, что область стека перекрывается с динамической областью какого-то процесса.


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

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


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

      В статьях на которые приведены ссылки идёт речь о переполнении ядерного стека.
      • +2
        Вы правы. Исправил путаницу, насколько возможно.
      • 0
        Честно говоря, странно видеть, что уязвимость перезаписи кучи стеком (или наоборот) до сих пор существует на 64-битных системах. Что мешает использовать для кучи и для стека адреса, разнесенные на сто тысяч гигабайт (условно)? Ядро же живет себе в верхней половине адресного пространства и его это не беспокоит.
        • 0
          В Windows изначально под esp нет физических страниц памяти, и чтобы туда замапилась очередная страница, нужно сделать чтение по адресу под последней выделенной страницей (и не ниже).

          Например, если на стеке 10кб локальных переменных, MSVC в начале функции выполняет два холостых чтения из [esp-0x1000] и [esp-0x2000], и это не для защиты от уязвимостей, а так работает расширение стека в Windows.

          Поэтому в Windows компиляторы вынуждены увеличивать стек постранично, не имея возможность пропустить guard page. Интересно, как работает стек в Linux.
          • 0

            Так же.

            • 0
              Тогда почему esp проскакивает guard page?
              Для ядра компилятор генерирует код по-другому?
              • 0

                Насколько я понимаю, в стеке ядра нет защитных страниц.

                • +1
                  mayorovp не так же. Вот кусок обработчика страничного сбоя для linux x86:

                          vma = find_vma(mm, address);
                          if (unlikely(!vma)) {
                                  bad_area(regs, error_code, address);
                                  return;
                          }
                          if (likely(vma->vm_start <= address))
                                  goto good_area;
                          if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
                                  bad_area(regs, error_code, address);
                                  return;
                          }
                          if (error_code & PF_USER) {
                                  /*
                                   * Accessing the stack below %sp is always a bug.
                                   * The large cushion allows instructions like enter
                                   * and pusha to work. ("enter $65535, $31" pushes
                                   * 32 pointers and then decrements %sp by 65535.)
                                   */
                                  if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
                                          bad_area(regs, error_code, address);
                                          return;
                                  }
                          }
                          if (unlikely(expand_stack(vma, address))) {
                                  bad_area(regs, error_code, address);
                                  return;
                          }
                  

                  В переводе на русский: для сбойного адреса ищется первая VMA, конечный адрес которой больше чем адрес сбоя. Если такая VMA найдена, и она расширяется вниз (VM_GROWSDOWN) — то это область стека. Попытаться её расширить функцией expand_stack.

                  Т.е. сбой по любому адресу в пустом месте перед стековой VMA вызовет попытку расширения стека.

                  Для ядра компилятор генерирует код по-другому?

                  Это не о ядерном коде вообще. Ядерные стеки имеют фиксированный размер и никогда не расширяются.
                  • 0
                    Это не о ядерном коде вообще. Ядерные стеки имеют фиксированный размер и никогда не расширяются.

                    Вот именно, об этом я и говорил.

                    • 0
                      jcmvbkbc, теперь понятно.

                      Linux в usermode может расширять стек не на 1 страницу, а сразу намного (но не более 64KB за 1 раз, наверное чтобы случайное чтение на 15 гигов ниже стека не скушало всю физическую память под стек).
                      • 0
                        но не более 64KB за 1 раз

                        Это вы из проверки внутри if (error_code & PF_USER) сделали вывод? Но стек необязательно двигать командой entry. Можно тупо sub esp, 100500. Эта проверка о том, что обращаться к стеку ниже текущего указателя стека вообще-то нельзя, но если надо — то можно, но недалеко (64К).

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.