- Ошибка heap-use-after-free (У вызывающей стороны нет прав на запись для ресурса: [subscriptions/]) Error: heap-use-after-free
- Пример — malloc Example — malloc
- Итоговая ошибка Resulting error
- Пример — operator new Example — operator new
- Итоговая ошибка-оператор New Resulting error — operator new
- Пример — realloc Example — realloc
- Итоговая ошибка-перераспределения Resulting error — realloc
- Пример — volatile Example — volatile
- Статья CVE-2019-0888: Уязвимость Use-After-Free в Windows ActiveX (ADO)
- NokZKH
Ошибка heap-use-after-free (У вызывающей стороны нет прав на запись для ресурса: [subscriptions/]) Error: heap-use-after-free
Ошибка очистки адреса: использование освобожденной памяти Address Sanitizer Error: Use of deallocated memory
Мы покажем три примера, где хранилище в куче можно выделить malloc с помощью, realloc (C) и new (C++), а также с ошибочным использованием volatile . We show three examples where storage in the heap can be allocated via malloc , realloc (C), and new (C++), along with a mistaken use of volatile .
Пример — malloc Example — malloc
Чтобы выполнить сборку и тестирование этого примера, выполните следующие команды в командной строке разработчикаVisual Studio 2019 версии 16,9 или более поздней: To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:
Итоговая ошибка Resulting error
Пример — operator new Example — operator new
Чтобы выполнить сборку и тестирование этого примера, выполните следующие команды в командной строке разработчикаVisual Studio 2019 версии 16,9 или более поздней: To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:
Итоговая ошибка-оператор New Resulting error — operator new
Пример — realloc Example — realloc
Чтобы выполнить сборку и тестирование этого примера, выполните следующие команды в командной строке разработчикаVisual Studio 2019 версии 16,9 или более поздней: To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:
Итоговая ошибка-перераспределения Resulting error — realloc
Пример — volatile Example — volatile
Чтобы выполнить сборку и тестирование этого примера, выполните следующие команды в командной строке разработчикаVisual Studio 2019 версии 16,9 или более поздней: To build and test this example, run these commands in a Visual Studio 2019 version 16.9 or later developer command prompt:
Статья CVE-2019-0888: Уязвимость Use-After-Free в Windows ActiveX (ADO)
NokZKH
Переводчик
Исследовательская группа 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 :
Установка соединения с помощью 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 :
Переменные объекта 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 , будет влиять на программу:
Проще говоря, 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 , правильно преобразуя его тип и предотвращая его передачи обратно в программу.
«Глупая» эксплуатация
Простейшим способом достижения некоторого типа примитива эксплойта с этой ошибкой было бы заставить объект быть освобожденным, а затем немедленно “распылить” кучу с выделением памяти для контролируемых данных того же размера, что и освобожденный объект, чтобы используемая память, которую хранил объект теперь содержал наши собственные произвольные данные.
В строке 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 года. Было написано множество статей о рассылке захваченного эксплойта и лежащей в основе ошибки, поэтому для получения дополнительной информации мы обратимся сюда:
CVE-2018-8174 — это ошибка использования после освобождения в VBScript, связанная с обработкой функции обратного вызова Class_Terminate . По сути, это дало возможность произвольно освобождать объект VBScript, но сохранять его ссылочным, подобно свойствам ошибки ADO.
Захваченный эксплойт реализовал сложную технику, которая использует атаку типа «путаница», чтобы превратить возможность использования после освобождения в обход ASLR и примитива «чтение-запись-везде». Сам метод сам по себе бесполезен (без ошибки для его включения) и технически не является ошибкой, поэтому он никогда не был «исправлен» и остается в базе кода. Техника, вероятно, лучше всего объясняется в статье Петра Флорчика .
Учитывая сходство между двумя ошибками, должна быть возможность взять закомментированный код эксплойта для CVE-2018-8174 из записи Florczyk , заменить части кода, специфичные для ошибок, чтобы использовать ошибку ADO, и заставить ее успешно работать так же путь. И действительно, применяя этот простой патч .
… Создается рабочий эксплойт для ошибки ADO.
Оказывается, этот эксплойт работает на системах под управлением Windows 7, но не на Windows 8 или более поздних версиях. Это относится и к первоначальному захваченному эксплойту. Эксплойт ломается из-за «рандомизации порядка размещения с низкой кучей кучи (LFH)», меры безопасности для кучи, представленной в Windows 8, которая нарушает простые сценарии использования «после освобождения» .
Обход рандомизации порядка распределения LFH
Вот один пример того, как поведение кучи изменилось после того, как Microsoft ввела рандомизацию порядка размещения LFH:
Введение рандомизации порядка распределения изменило результат выполнения malloc-> free-> malloc с логики LIFO (Last In First Out) на недетерминированность.
Почему это ломает все? Рассмотрим следующую выдержку из закомментированного кода эксплойта:
В 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 для выполнения такого назначения. Это упрощенный псевдокод, объясняющий, как он работает:
Функция 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 будет в конечном итоге назначено одному из них.
Вот как код может быть преобразован:
Вот визуализация вышеуказанной логики кода в действии:
UafArrayA (38) получает такое же выделение 0x108 (Vval), что и reuseObjectA_arr
После того , как массив UafArrayA был заполненнен новыми объектами FakeReuseClass и mem = value завершается, мы можем перебрать массив и найти объект, mem , где переменная была успешно повреждена стать массивом:
Поврежденный объект будет единственным, который не вызовет исключения в строке 3. Как только мы находим его, на него можно ссылаться с любым индексом, позволяющим читать и записывать все адреса в области памяти процесса.
С этим исправлением к оригинальному эксплойту, теперь оно работает и в системах Windows 10.