Пожалуйста, обратите внимание, что пользователь заблокирован
Введение
В Windows 10 1607 (RS1) была добавлена рандомизация PTE, что усложнило манипуляции с их свойствами, описываемыми структурами
Ранее база
В современных техниках эксплуатации ядра используется утечка этой константы при наличии примитива на чтение минимум 5 байт. Почему достаточно лишь пяти, ведь адрес в 64-битной системе имеет размер 64 бита? В действительности для большинства систем будет справедливо использование модели памяти PML4, которая описывает виртуальный адрес 48 битами.
В различных блогах при разработке эксплойта используется чтение значения вышеупомянутой константы
Сама функция состоит из одного базового блока и вычисляет
На python эта функция могла бы выглядеть следующим образом:
Цель
В какой-то момент мне стало интересно, а сколько таких смещений с
До секции
Пишем скрипт для IDAPython
Для поиска
Алгоритм был намечен следующий:
1. Объявить начало и конец файла
2. Объявить байтовый паттерн c учетом little-endian
3. Найти первое вхождение и вывести на экран
Запустив скрипт, в окне вывода мы можем видеть
Теперь сверимся с отладчиком.
Сравниваем разные билды ntoskrnl.exe
У меня есть несколько версий баз с
К сожалению, результат совсем не утешительный. С другой стороны, это не такая уж и большая проблема. Для некоторых ядерных эксплойтов придется реализовать поддержку различных версий ядер. Для поиска
В Windows 10 1607 (RS1) была добавлена рандомизация PTE, что усложнило манипуляции с их свойствами, описываемыми структурами
_MMPTE и _MMPTE_HARDWARE для обхода SMEP через обнуление 63 бита - NoExecute.
Код:
3: kd> dt nt!_mmpte u.Hard.
+0x000 u :
+0x000 Hard :
+0x000 Valid : Pos 0, 1 Bit
+0x000 Dirty1 : Pos 1, 1 Bit
+0x000 Owner : Pos 2, 1 Bit
+0x000 WriteThrough : Pos 3, 1 Bit
+0x000 CacheDisable : Pos 4, 1 Bit
+0x000 Accessed : Pos 5, 1 Bit
+0x000 Dirty : Pos 6, 1 Bit
+0x000 LargePage : Pos 7, 1 Bit
+0x000 Global : Pos 8, 1 Bit
+0x000 CopyOnWrite : Pos 9, 1 Bit
+0x000 Unused : Pos 10, 1 Bit
+0x000 Write : Pos 11, 1 Bit
+0x000 PageFrameNumber : Pos 12, 36 Bits
+0x000 ReservedForHardware : Pos 48, 4 Bits
+0x000 ReservedForSoftware : Pos 52, 4 Bits
+0x000 WsleAge : Pos 56, 4 Bits
+0x000 WsleProtection : Pos 60, 3 Bits
+0x000 NoExecute : Pos 63, 1 Bit
Ранее база
PTE была статичной и хранилась в константе MmPteBase. Собственно, она и сейчас используется во множестве функций ядра с той лишь разницей, что ее значение изменяется при каждом запуске ОС.
Код:
3: kd> x nt!MmPteBase
fffff803`2cefb358 nt!MmPteBase = <no type information>
3: kd> dps fffff803`2cefb358
fffff803`2cefb358 fffff880`00000000
В современных техниках эксплуатации ядра используется утечка этой константы при наличии примитива на чтение минимум 5 байт. Почему достаточно лишь пяти, ведь адрес в 64-битной системе имеет размер 64 бита? В действительности для большинства систем будет справедливо использование модели памяти PML4, которая описывает виртуальный адрес 48 битами.
В различных блогах при разработке эксплойта используется чтение значения вышеупомянутой константы
MmPteBase по смещению MiGetPteAddress+0x13.
Код:
3: kd> dq nt!MiGetPteAddress+0x13
fffff803`2c51e4c3 fffff880`00000000 cccccccc`c3c10348
fffff803`2c51e4d3 cccccccc`cccccccc ca8b4ccc`cccccccc
fffff803`2c51e4e3 000fffff`ffffb848 d18b4cc8`234c0000
Сама функция состоит из одного базового блока и вычисляет
PTE, где в качестве параметра передается VA.
На python эта функция могла бы выглядеть следующим образом:
Python:
def MiGetPteAddress(va):
MmPteBase = 0xFFFFF88000000000
return((va >> 9) & 0x7FFFFFFFF8) + MmPteBase
MiGetPteAddress(0xfffff8032c200000)
Цель
В какой-то момент мне стало интересно, а сколько таких смещений с
MmPteBase присутствует в ядре? Зачем? Я предположил, что поиск таких смещений может быть полезен для написания стабильного эксплойта, т.к. в зависимости от версии ядра RVA могут различаться. Функция MiGetPteAddress находится в секции .text, достаточно близко к началу файла. Моя задача найти смещения как можно ближе к началу т.к. логично предположить, что в таком случае это и будут наиболее стабильные смещения.До секции
.text есть еще несколько, где потенциально могут находиться нужные данные. В конце мы проверим гипотезу, запустив скрипт на разных версиях ядра.
Пишем скрипт для IDAPython
Для поиска
RVA я буду использовать IDA Pro и IDAPython.Алгоритм был намечен следующий:
1. Объявить начало и конец файла
2. Объявить байтовый паттерн c учетом little-endian
3. Найти первое вхождение и вывести на экран
VA и RVA.
Python:
pattern = '00 00 00 00 80 F6 FF FF' # little-endian
NtBase = ida_nalt.get_imagebase()
addr = idc.get_inf_attr(INF_MIN_EA)
for x in range(0, 1):
addr = ida_search.find_binary(addr, idc.BADADDR, pattern, 16, ida_search.SEARCH_NEXT | ida_search.SEARCH_DOWN)
if addr != idc.BADADDR:
print("VA: 0x%x\t RVA: 0x%x\t" % (addr, (addr - NtBase)))
Запустив скрипт, в окне вывода мы можем видеть
VA и RVA первого вхождения, которое используется функцией MiTrimWorkingSetBuildup.
Код:
VA: 0x140204ce7 RVA: 0x204ce7
Теперь сверимся с отладчиком.
MmPteBase, которое в моем случае равно 0xfffff88000000000 находится там, где и должно. Напомню, из-за рандома при каждом новом запуске ОС это значение будет отличаться.
Код:
3: kd> dq fffff803`2c200000 + 0x204ce7
fffff803`2c404ce7 fffff880`00000000 48c93345`40668b4d
fffff803`2c404cf7 f000e481`4919e0c1 b94810e4`c149ffff
Сравниваем разные билды ntoskrnl.exe
У меня есть несколько версий баз с
ntoskrnl.exe. Давайте запустим скрипт для каждой и проверим, имеют ли они общее RVA для MmPteBase.
Код:
ntoskrnl.exe_20h2_19042_572 VA: 0x140204ce7 RVA: 0x204ce7
ntoskrnl.exe_20h2_19042_685 VA: 0x1402038cd RVA: 0x2038cd
ntoskrnl.exe_20h2_19042_746 VA: 0x1402038cd RVA: 0x2038cd
ntoskrnl.exe_20h2_19042_867 VA: 0x1402038cd RVA: 0x2038cd
ntoskrnl.exe_20h2_19042_906 VA: 0x1402038cd RVA: 0x2038cd
ntoskrnl.exe_20h2_19042_928 VA: 0x1402038cd RVA: 0x2038cd
ntoskrnl.exe_20h2_19042_985 VA: 0x140204c14 RVA: 0x204c14
К сожалению, результат совсем не утешительный. С другой стороны, это не такая уж и большая проблема. Для некоторых ядерных эксплойтов придется реализовать поддержку различных версий ядер. Для поиска
RVA по байтовому паттерну вы можете использовать вышенаписанный скрипт.
Последнее редактирование: