ASLR — это Address Space Layout Randomization, рандомизация адресного пространства. Это механизм обеспечения безопасности, который включает в себя рандомизацию виртуальных адресов памяти различных структур данных, чувствительных к атакам. Расположение в памяти целевой структуры сложно предугадать, поэтому шансы атакующего на успех малы.
Авторы: Артем Шишкин и Илья Смит, исследовательский центр Positive Research.
ASLR — это Address Space Layout Randomization, рандомизация адресного пространства. Это механизм обеспечения безопасности, который включает в себя рандомизацию виртуальных адресов памяти различных структур данных, чувствительных к атакам. Расположение в памяти целевой структуры сложно предугадать, поэтому шансы атакующего на успех малы.
Реализация ASLR в Windows тесно связана с механизмом релокации (relocation) исполняемых образов. Релокация позволяет PE-файлу загружаться не только по фиксированной предпочитаемой базе. Секция релокаций в PE-файле является ключевой структурой при перемещении образа. Она описывает, какие необходимо внести изменения в определенные элементы кода и данных для обеспечения корректного функционирования приложения по другому базовому адресу.
Ключевую роль в работе ASLR играют генератор случайных чисел и пара функций, модифицирующих базовый адрес загружаемого PE-файла.
Windows 8 полагается на генератор случайных чисел, который по сути является генератором Фибоначчи с задержкой с параметрами j=24 и k=55. Его зерно инициализируется в модуле winload.exe при старте системы. Winload.exe собирает энтропию из различных источников: ключи реестра, TPM, текущее время, ACPI, а также при помощи новой инструкции rdrand. Инициализация ядерного генератора случайных чисел детально описана в источнике [1].
Рассмотрим подробнее новую инструкцию rdrand. В процессорах, основанных на архитектуре Ivy Bridge, была представлена технология Intel Secure Key для генерации высококачественных псевдослучайных чисел. Она реализована при помощи аппаратного цифрового генератора случайных чисел (DRNG) и инструкции rdrand для программного извлечения значений из него.
С аппаратной точки зрения DRNG представляет собой отдельный модуль на процессорном чипе. Он работает асинхронно с ядрами процессора на частоте 3 ГГц. DRNG использует тепловой шум в качестве источника энтропии; кроме того, в нем присутствует встроенная система тестирования, выполняющая серию проверок качества извлекаемых случайных значений. В случае неудовлетворительного результата тестирования, DRNG перестает генерировать случайные значения.
Для извлечения случайных чисел из DRNG используется инструкция rdrand. В документации к ней отмечено, что теоретически DRNG может возвращать нулевые значения в случае неудовлетворительного результата тестирования или опустошения внутренней очереди случайных значений. Однако на практике опустошить DRNG не удалось.
Intel Secure Key является мощным генератором случайных чисел, производящим высококачественные случайные значения с очень высокой скоростью. Практически невозможно предугадать изначальное состояние генератора, инициализированного при помощи инструкции rdrand (в отличие от прочих источников энтропии).
Внутренней интерфейсной функцией ядерного ГПСЧ является ExGenRandom(). Она также имеет экспортируемую функцию-обертку RtlRandomEx(). ASLR в Windows 8 использует ExGenRandom() — в отличие от предыдущих версий, которые полагались на инструкцию rdtsc. Последняя используется для получения счетчика времени на ЦПУ, который изменяется линейно и, следовательно, не может обеспечить должного качества генерируемых значений, что небезопасно.
Основной функцией механизма ASLR является MiSelectImageBase(). В Windows 8 она может быть описана следующим псевдокодом.
#define MI_64K_ALIGN(x) (x + 0x0F) >> 4
#define MmHighsetUserAddress 0x7FFFFFEFFFF
typedef PIMAGE_BASE ULONG_PTR;
typedef enum _MI_MEMORY_HIGHLOW
{
MiMemoryHigh = 0,
MiMemoryLow = 1,
MiMemoryHighLow = 2
} MI_MEMORY_HIGHLOW, *PMI_MEMORY_HIGHLOW;
MI_MEMORY_HIGHLOW MiSelectBitMapForImage(PSEGMENT pSeg)
{
if (!(pSeg->SegmentFlags & FLAG_BINARY32)) // WOW binary
{
if (!(pSeg->ImageInformation->ImageFlags & FLAG_BASE_BELOW_4GB))
{
if (pSeg->BasedAddress > 0x100000000)
{
return MiMemoryHighLow;
}
else
{
return MiMemoryLow;
}
}
}
return MiMemoryHigh;
}
PIMAGE_BASE MiSelectImageBase(void* a1<rcx>, PSEGMENT pSeg)
{
MI_MEMORY_HIGHLOW ImageBitmapType;
ULONG ImageBias;
RTL_BITMAP *pImageBitMap;
ULONG_PTR ImageTopAddress;
ULONG RelocationSizein64k;
MI_SECTION_IMAGE_INFORMATION *pImageInformation;
ULONG_PTR RelocDelta;
PIMAGE_BASE Result = NULL;
// rsi = rcx
// rcx = rdx
// rdi = rdx
pImageInformation = pSeg->ImageInformation;
ImageBitmapType = MiSelectBitMapForImage(pSeg);
a1->off_40h = ImageBitmapType;
if (ImageBitmapType == MiMemoryLow)
{
// 64-bit executable with image base below 4 GB
ImageBias = MiImageBias64Low;
pImageBitMap = MiImageBitMap64Low;
ImageTopAddress = 0x78000000;
}
else
{
if (ImageBitmapType == MiMemoryHighLow)
{
// 64-bit executable with image base above 4 GB
ImageBias = MiImageBias64High;
pImageBitMap = MiImageBitMap64High;
ImageTopAddress = 0x7FFFFFE0000;
}
else
{
// MiMemoryHigh 32-bit executable image
ImageBias = MiImageBias;
pImageBitMap = MiImageBitMap;
ImageTopAddress = 0x78000000;
}
}
// pSeg->ControlArea->BitMap ^= (pSeg->ControlArea->BitMap ^ (ImageBitmapType << 29)) & 0x60000000;
// or bitfield form
pSeg->ControlArea.BitMap = ImageBitmapType;
RelocationSizein64k = MI_64K_ALIGN(pSeg->TotalNumberOfPtes);
if (pSeg->ImageInformation->ImageCharacteristics & IMAGE_FILE_DLL)
{
ULONG StartBit = 0;
ULONG GlobalRelocStartBit = 0;
StartBit = RtlFindClearBits(pImageBitMap, RelocationSizein64k, ImageBias);
if (StartBit != 0xFFFFFFFF)
{
StartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 0);
if (StartBit != 0xFFFFFFFF)
{
Result = ImageTopAddress - (((RelocationSizein64k) + StartBit) << 0x10);
if (Result == (pSeg->BasedAddress - a1->SelectedBase))
{
GlobalRelocStartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 1);
StartBit = (GlobalRelocStartBit != 0xFFFFFFFF) ? GlobalRelocStartBit : StartBit;
Result = ImageTopAddress - (RelocationSizein64k + StartBit) << 0x10;
}
a1->RelocStartBit = StartBit;
a1->RelocationSizein64k = RelocationSizein64k;
pSeg->ControlArea->ImageRelocationStartBit = StartBit;
pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k;
return Result;
}
}
}
else
{
// EXE image
if (a1->SelectedBase != NULL)
{
return pSeg->BasedAddress;
}
if (ImageBitmapType == MiMemoryHighLow)
{
a1->RelocStartBit = 0xFFFFFFFF;
a1->RelocationSizein64k = (WORD)RelocationSizein64k;
pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF;
pSeg->ControlArea->ImageRelocationSizeIn64k = (WORD)RelocationSizein64k;
return ((DWORD)(ExGenRandom(1) % (0x20001 - RelocationSizein64k)) + 0x7F60000) << 16;
}
}
ULONG RandomVal = ExGenRandom(1);
RandomVal = (RandomVal % 0xFE + 1) << 0x10;
RelocDelta = pSeg->BasedAddress - a1->SelectedBase;
if (RelocDelta > MmHighsetUserAddress)
{
return 0;
}
if ((RelocationSizein64k << 0x10) > MmHighsetUserAddress)
{
return 0;
}
if (RelocDelta + (RelocationSizein64k << 0x10) <= RelocDelta)
{
return 0;
}
if (RelocDelta + (RelocationSizein64k << 0x10) > MmHighsetUserAddress)
{
return 0;
}
if (a1->SelectedBase + RandomVal == 0)
{
Result = pSeg->BasedAddress;
}
else
{
if (RelocDelta > RandomVal)
{
Result = RelocDelta - RandomVal;
}
else
{
Result = RelocDelta + RandomVal;
if (Result < RelocDelta)
{
return 0;
}
if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) > 0x7FFFFFDFFFF)
{
return 0;
}
if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) < (RelocDelta + (RelocationSizein64k << 0x10))))
{
return 0;
}
}
}
//random_epilog
a1->RelocStartBit = 0xFFFFFFFF;
a1->RelocationSizein64k = RelocationSizein64k;
pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF;
pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k;
return Result;
}
Как можно заметить, существуют три различные битовые карты образов. Первая предназначена для 32-разрядных исполняемых приложений, вторая — для 64-разрядных, третья — для 64-разрядных приложений с базой выше 4 ГБ, что дает им виртуальный адрес с высокой энтропией.
Рандомизация исполняемых образов влияет непосредственно на их базовый адрес. В случае загрузки библиотек, ASLR является частью процесса релокации модуля: случайной величиной при выборе нового базового адреса является переменная ImageBias, которая инициализируется при старте системы.
VOID MiInitializeRelocations()
{
MiImageBias = ExGenRandom(1) % 256;
MiImageBias64Low = ExGenRandom(1) % MiImageBitMap64Low.SizeOfBitMap;
MiImageBias64High = ExGenRandom(1) % MiImageBitMap64High.SizeOfBitMap;
return;
}
Битовые карты исполняемых образов отображают адресное пространство текущих пользовательских процессов. Исполняемый образ получает базовый адрес во время загрузки, его повторная загрузка в другие процессы будет производиться по тому же базовому адресу. Такое поведение загрузчика является наиболее эффективным в смысле быстродействия и экономии памяти благодаря использованию исполняемыми образами механизма копирования при записи (copy-on-write).
Текущая реализация ASLR в Windows 8 позволяет рандомизировать базовый адрес приложений, не поддерживающих рандомизацию. Ниже приведена таблица, демонстрирующая поведение загрузчика в зависимости от комбинаций флагов линкера, связанных с ASLR.
* Невозможно собрать образ при помощи MSVS, поскольку флаг /DYNAMICBASE требует флаг /FIXED:NO, который генерирует секцию релокаций
Можно заметить изменение в поведении загрузчика, характерное для Windows 8: если исполняемый образ имеет секцию релокаций, то он будет загружен в любом случае. Это еще одно свидетельство взаимосвязи между ASLR и механизмом релокации
В целом можно сказать, что реализация новых функций ASLR в Windows 8 не оказывает значительного влияния на логику кода, поэтому достаточно сложно обнаружить в ней полезные уязвимости. Увеличение энтропии для рандомизации различных объектов по сути является заменой константного выражения в коде. Кроме того, по графам кода можно отметить проведение инспекции кода.
Источники
[1] Valasek Ch., Mandt T. Windows 8 Heap Internals. 2012.
[2] Johnson K., Miller M. Exploit Mitigation Improvements in Windows 8. Slides, Black Hat USA, 2012.
[3] Intel. Intel®Digital Random Number Generator (DRNG): Software Implementation Guide. Intel Corporation, 2012.
[4] Whitehouse O. An Analysis of Address Space Layout Randomization on Windows Vista. Symantec Advances Threat Research, 2007.
[5] Sotirov A., Dowd M. Bypassing Browser Memory Protections. 2008.