Сегодня мы разберем возможность выполнения своего кода в Linux и в Windows. В опенсорсной ОС провинился загрузчик GRUB: в нем нашли способ обойти авторизацию и добраться до консоли восстановления, причем уязвимость существует еще с 2009 года. В Windows «недокументированные возможности» нашли в стандартном медиаплеере: он при определенных условиях позволяет исполнять JavaScript.

 

Проблема с аутентификацией в GRUB 2

 

CVSSv2

N/A

 

BRIEF

Дата релиза: 14 декабря 2015 года
Автор: Hector Marco, Ismael Ripoll
CVE: 2015-8370

GRUB 2 — самая важная часть большинства систем *nix, она позволяет управлять загрузкой ОС, а также может защитить твое устройство дополнительным паролем. Найденная уязвимость позволяет обойти этот запрос пароля. Впервые она была раскрыта 10 декабря 2015 года на конференции IX Jornadas STIC CCN-CERT, а 14 декабря появилась публичная информация. В качестве тестового стенда авторами был выбран Debian 7.5, запущенный внутри QEMU.

Для проверки системы на уязвимость просто нажми клавишу Backspace 28 раз в тот момент, когда GRUB спросит имя пользователя. Если в итоге система перезагрузится или появится rescue shell, то поздравляю — устройство может быть успешно атаковано.

GRUB rescue shell — это очень мощное средство. Оно позволяет атакующему сделать следующее:

  • повысить привилегии. Атакующему не нужно знать ни логин, ни пароль существующего пользователя;
  • раскрыть важную информацию. Атакующий может загрузить свои собственные ядро и initframs (к примеру, с USB-устройства) и затем, работая из более удобной среды, скопировать весь диск или установить руткит;
  • вызвать DoS. Атакующий может уничтожить все данные внутри GRUB. Или, если диск зашифрован, перезаписать, вызвав DoS.

Эта уязвимость появилась в GRUB начиная с версии 1.98 после патча с хешем b391bdb2f2c5ccf29da66cecdbfb7566656a704d, который затрагивает функцию grub_password_get(). На деле проблемных функций оказывается две: grub_username_get() и grub_password_get(), они находятся в grub-core/normal/auth.c и lib/crypto.c соответственно. Обе функции похожи, за исключением вызова printf() в grub_username_get(). Пример эксплоита, который мы рассмотрим дальше, основан на эксплуатации ошибки в функции grub_username_get() для получения доступа к GRUB rescue shell.

Вот содержимое уязвимой функции grub_username_get() из grub-core/normal/auth.c.

static int
grub_username_get (char buf[], unsigned buf_size)
{
  unsigned cur_len = 0;
  int key;

  while (1)
    {
      key = grub_getkey ();
      if (key == '\n' || key == '\r')
        break;

      if (key == '\e')
        {
          cur_len = 0;
          break;
        }

      if (key == '\b')  // Не проверяет на целочисленное опустошение
        {
          cur_len--; // Целочисленное опустошение (integer underflow)
          grub_printf ("\b");
          continue;
        }

      if (!grub_isprint (key))
        continue;

      if (cur_len + 2 < buf_size)
        {
          buf[cur_len++] = key; // Сдвиг на два
          grub_printf ("%c", key);
        }
    }

  grub_memset( buf + cur_len, 0, buf_size - cur_len); // Выход за границы массива

  grub_xputs ("\n");
  grub_refresh ();

  return (key != '\e');
}

Ошибка вызывается из-за уменьшения значения переменной cur_len без соответствующей проверки.

 

EXPLOIT

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

Первая ошибка перезаписывает два байта ниже буфера с именем пользователя. Локальная переменная называется login в функции grub_auth_check_authentication(), но эта область не содержит полезной информации для проведения атаки, поэтому пропустим ее.

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

grub_memset (buf + cur_len, 0, buf_size - cur_len);

К примеру, напишем в качестве имени пользователя root. Длина (cur_len) будет равна пяти, и функция grub_memset() очистит (установит 0) байты с пятого по 1024 – 5 (размер буфера для имени пользователя и пароля равен 1024) в имени пользователя. Это надежный путь программирования: к примеру, если набранное имя пользователя сохранилось в очищенном 1024-байтовом массиве, то затем мы можем сравнить эти 1024 байта с правильным именем пользователя вместо сравнения обеих строк. Это защищает от некоторых атак по сторонним каналам, к примеру от атаки по времени.

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

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

Популярные оружейные сейфы Vaultek можно взломать и открыть удаленно

Специалисты компании Two Six Labs обнаружили ряд проблем в оружейных сейфах производства V…