Недостаточно памяти для asn1
Обновлено: 21.11.2024
Доступна более новая предварительная версия этого пакета.
Подробности см. в списке версий ниже.
Для проектов, поддерживающих PackageReference, скопируйте этот XML-узел в файл проекта для ссылки на пакет.
Предоставляет классы, которые могут читать и записывать форматы данных ASN.1 BER, CER и DER.
Часто используемые типы:
System.Formats.Asn1.AsnReader
System.Formats.Asn1.AsnWriter
Продукт | Версии |
---|---|
.NET | net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0 -macos net6.0-tvos net6.0-windows |
.NET Core | netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1 |
.NET Standard | netstandard2.0 netstandard2.1 | .NET Framework | net461 net462 net463 net47 net471 net472 net48 |
MonoAndroid | monoandroid |
MonoMac | monomac |
MonoTouch | monotouch |
Tizen | tizen40 tizen60 |
Xamarin.iOS | xamarinios |
Xamarin.Mac | xamarinmac |
Xamarin.TVOS | xamarinwatchos |
Xamarin.WatchOS | xamarinwatchos |
net6.0
Пакеты NuGet (11)
Показаны 5 лучших пакетов NuGet, зависящих от System.Formats.Asn1:
Предоставляет реализации криптографических алгоритмов и управление ключами с помощью Windows Cryptographic Next Generation API (CNG). Часто используемые типы: System.Security.Cryptography.RSACng System.Security.Cryptography.ECDsaCng System.Security.Cryptography.CngKey
Предоставляет реализации криптографических алгоритмов и управление ключами для систем, отличных от Windows, с OpenSSL. Часто используемые типы: System.Security.Cryptography.RSAOpenSsl
Обеспечивает поддержку алгоритмов PKCS и CMS. Часто используемые типы: System.Security.Cryptography.Pkcs.EnvelopedCms
Библиотека классов сертификатов OPC UA Security X509
Библиотека классов сертификатов OPC UA Security X509
Репозитории GitHub (4)
Показаны 4 самых популярных репозитория GitHub, которые зависят от System.Formats.Asn1:
Windows 7 Enterprise Windows 7 Home Basic Windows 7 Home Premium Windows 7 Professional Windows 7 Starter Windows 7 Ultimate Windows Server 2008 R2 Datacenter Windows Server 2008 R2 Enterprise Windows Server 2008 R2 для систем на базе Itanium Windows Server 2008 R2 Foundation Windows Server 2008 R2 Standard Windows Server 2008 R2 Web Edition Подробнее. Меньше
Симптомы
Рассмотрите следующий сценарий:
У вас есть компьютер под управлением Windows 7 или Windows Server 2008 R2.
В этом случае приложение не может декодировать сообщение. Кроме того появляется сообщение об ошибке следующего вида:
Необработанное исключение: System.Security.Cryptography.CryptographicException: ASN1 не хватает памяти.
в System.Security.Cryptography.Pkcs.EnvelopedCms.OpenToDecode(Byte[] encodedMessage)
в System.Security. Cryptography.Pkcs.EnvelopedCms.Decode(Byte[] encodedMessage)
Причина
Эта проблема возникает из-за неправильного увеличения размера буфера в функции CryptMsgUpdate, когда метод EnvelopedCms.Decode добавляет декодированное содержимое в буфер в процессе декодирования. Поэтому для большого файла возникает целочисленное переполнение.
Разрешение
Информация об исправлении
Поддерживаемое исправление доступно в Microsoft. Однако это исправление предназначено для устранения только проблемы, описанной в этой статье. Применяйте это исправление только к системам, в которых возникла проблема, описанная в этой статье. Это исправление может пройти дополнительное тестирование. Поэтому, если эта проблема не сильно повлияла на вас, мы рекомендуем дождаться следующего обновления программного обеспечения, содержащего это исправление.
Если исправление доступно для загрузки, в верхней части этой статьи базы знаний есть раздел «Исправление доступно для загрузки». Если этот раздел не отображается, обратитесь в службу поддержки клиентов Майкрософт, чтобы получить исправление.
Примечание. Если возникают дополнительные проблемы или требуется их устранение, вам может потребоваться создать отдельный запрос на обслуживание. Обычная стоимость поддержки будет применяться к дополнительным вопросам поддержки и проблемам, которые не подходят для этого конкретного исправления. Чтобы получить полный список номеров телефонов службы поддержки и обслуживания клиентов Майкрософт или создать отдельный запрос на обслуживание, посетите следующий веб-сайт Майкрософт:
http://support.microsoft.com/contactus/?ws=supportПримечание В форме «Исправление доступно для загрузки» отображаются языки, для которых доступно исправление. Если вы не видите свой язык, это означает, что для него недоступно исправление.
Предпосылки
Чтобы применить это исправление, необходимо использовать одну из следующих операционных систем:
Пакет обновления 1 (SP1) для Windows 7
Windows Server 2008 R2
Windows Server 2008 R2 с пакетом обновления 1 (SP1)
Информация о реестре
Чтобы использовать исправление в этом пакете, вам не нужно вносить какие-либо изменения в реестр.
Требование перезапустить
После установки этого исправления необходимо перезагрузить компьютер.
Информация о замене исправлений
Это исправление не заменяет ранее выпущенное исправление.
Информация о файле
Глобальная версия этого исправления устанавливает файлы с атрибутами, перечисленными в следующих таблицах. Даты и время для этих файлов указаны в формате всемирного координированного времени (UTC). Даты и время для этих файлов на вашем локальном компьютере отображаются в вашем местном времени вместе с вашим текущим смещением летнего времени (DST). Кроме того, даты и время могут меняться при выполнении определенных операций с файлами.
Информация о файлах для Windows 7 и Windows Server 2008 R2
Важные исправления для Windows 7 и исправления для Windows Server 2008 R2 включены в одни и те же пакеты. Однако исправления на странице запроса исправления перечислены для обеих операционных систем. Чтобы запросить пакет исправлений для одной или обеих операционных систем, выберите исправление, указанное в разделе «Windows 7/Windows Server 2008 R2» на странице. Всегда обращайтесь к разделу «Применимо к» в статьях, чтобы определить фактическую операционную систему, к которой относится каждое исправление.
Файлы, относящиеся к определенному продукту, вехе (окончательная первоначальная версия, SPn) и ветви обслуживания (LDR, GDR), можно определить, изучив номера версий файлов, как показано в следующей таблице:
В этой книге мы рассмотрели ряд интересных тем компьютерных сетей. Однако этот раздел по ASN.1 может не войти в десятку самых интересных тем. Как и овощи, знание ASN.1 и более широкой проблемы презентационных сервисов — это то, что «полезно для вас». ASN.1 — это стандарт, разработанный ISO, который используется в ряде протоколов, связанных с Интернетом, особенно в области управления сетью. Например, в разделе 8.2 мы видели, что переменные MIB в SNMP неразрывно связаны с ASN.1. Таким образом, хотя материал по ASN.1 в этом разделе может быть довольно сухим, читатель, надеюсь, примет на веру, что материал важный.
Чтобы мотивировать наше обсуждение, рассмотрим следующий мысленный эксперимент. Предположим, можно надежно скопировать данные из памяти одного компьютера непосредственно в память другого удаленного компьютера. Если бы можно было это сделать, была бы проблема коммуникации «решена»? Ответ на вопрос зависит от определения «коммуникативной проблемы». Конечно, идеальная копия из памяти в память точно передаст биты и байты с одной машины на другую. Но означает ли такая точная копия битов и байтов, что при доступе к этим данным программного обеспечения, работающего на компьютере-получателе, оно увидит те же значения, которые были сохранены в памяти компьютера-отправителя? Ответ на этот вопрос «не обязательно»! Суть проблемы в том, что разные компьютерные архитектуры, разные операционные системы и компиляторы имеют разные правила хранения и представления данных. Если данные должны передаваться и храниться между несколькими компьютерами (как это происходит в любой коммуникационной сети!), эта проблема представления данных должна быть четко решена.
В качестве примера этой проблемы рассмотрим приведенный ниже простой фрагмент кода C. Как эта структура может быть размещена в памяти?
Проблема различных архитектур, имеющих разный внутренний формат данных, является реальной и распространенной проблемой. Конкретная проблема целочисленного хранения в разных форматах в разных архитектурах настолько распространена, что у нее есть название. Порядок хранения целых чисел с обратным порядком байтов имеет старшие значащие байты целого числа, сохраненные первыми (по наименьшему адресу хранения). В порядке "с прямым порядком байтов" сначала сохраняются младшие значащие байты. Процессоры Sun SPARC и Motorola имеют обратный порядок байтов, а процессоры Intel и DEC Alpha — обратный порядок байтов. Между прочим, термины «прямой порядок байтов» и «прямой порядок байтов» взяты из книги Джонатана Смита «Путешествие Гулливера», в которой две группы людей догматически настаивают на том, чтобы делать простую вещь двумя разными способами (надеюсь, аналогия с сообществом компьютерной архитектуры очевидна). Одна группа в стране лилипутов настаивает на том, чтобы разбивать яйца с большей стороны («большой конец»), в то время как другая настаивает на том, чтобы разбивать их с меньшей стороны. Это различие стало причиной больших междоусобиц и восстаний.
Учитывая, что разные компьютеры хранят и представляют данные по-разному, как с этим справиться с сетевыми протоколами? Например, если агент SNMP собирается отправить ответное сообщение, содержащее целочисленное количество полученных дейтаграмм UDP, как он должен представлять целочисленное значение, которое должно быть отправлено управляющему объекту — в прямом или обратном порядке? Одним из вариантов может быть отправка агентом байтов целого числа в том же порядке, в котором они будут храниться в управляющем объекте. Другим вариантом может быть отправка агентом своего собственного порядка хранения, а принимающий объект переупорядочивает байты по мере необходимости. Любой вариант потребует, чтобы отправитель или получатель узнали формат другого для целочисленного представления.
Третий вариант заключается в том, чтобы иметь независимый от машины, ОС и языка метод описания целых чисел и других типов данных (например, язык описания данных) и правила, определяющие способ, которым каждый из типов данных должен быть передаваться по сети. Когда данные данного типа получены, они принимаются в известном формате и затем могут быть сохранены в любом требуемом машинно-зависимом формате. Оба SMI, которые мы изучали в разделе 8.3, и ASN.1 используют этот третий вариант. На языке ISO эти два стандарта описывают службу представления — службу передачи и перевода информации из одного машинно-специфического формата в другой. На рис. 8.4-2 показана реальная проблема презентации; ни один из получателей не понимает основной передаваемой идеи — что говорящему что-то нравится. Как показано на рис. 8.4-3, служба представления может решить эту проблему, переводя идею на общепонятный (службой представления), независимый от человека язык, отправляя эту информацию получателю, а затем переводя на язык, понятный пользователю. получатель.
Рисунок 8.4-2: Проблема презентации
Рисунок 8.4-3: Проблема представления решена
Помимо предоставления языка описания данных, ASN.1 также предоставляет основные правила кодирования (BER), которые определяют, как экземпляры объектов, которые были определены с использованием языка описания данных ASN.1, должны отправляться по сети. BER использует так называемый подход TLV (тип, длина, значение) к кодированию данных для передачи. Для каждого элемента данных, который должен быть отправлен, тип данных, длина элемента данных, а затем фактическое значение элемента данных отправляются в указанном порядке. Благодаря этому простому соглашению получаемые данные по существу являются самоидентифицируемыми.
На рис. 8.4.4 показано, как будут отправлены два элемента данных в нашем простом примере с языком C выше. В этом примере отправитель хочет отправить букву «a», за которой следует десятичное значение 259 (которое равно 00000001 00000011 в двоичном формате или байтовое значение 1, за которым следует байтовое значение 3) с обратным порядком байтов. Первый байт в переданном потоке имеет значение 4, что указывает на то, что тип следующего элемента данных — СТРОКА ОКТЕТОВ; это буква «Т» в кодировке TLV. Второй байт в потоке содержит длину ОКТЕТНОЙ СТРОКИ, в данном случае 1. Третий байт в передаваемом потоке начинает (и заканчивает) ОКТЕТНУЮ СТРОКУ длиной один; он содержит ASCII-представление буквы «а». Значения T, L и V следующего элемента данных равны 2 (значение тега типа INTEGER), 2 (т. е. целое число длиной 2 байта) и двухбайтовое представление значения с обратным порядком байтов 259 в десятичном порядке) .
Рис. 8.4-4: Пример кодирования BER
В нашем обсуждении выше мы затронули только небольшое и простое подмножество ASN.1. Ресурсы для получения дополнительной информации о ASN.1 включают документ стандартов ASN.1 [ISO 1987, ISOX.680], домашнюю страницу Philipp Hoschka ASN.1 [Hoschka 1997] и [Larmouth 1996].
Этот пакет находится не в последней версии модуля.
Подробнее
Система модулей Go была представлена в Go 1.11 и является официальным решением для управления зависимостями для Go.
Распространяемые лицензии накладывают минимальные ограничения на использование, изменение и распространение программного обеспечения.
Модули с помеченными версиями позволяют импортерам создавать более предсказуемые сборки.
Когда проект достигает основной версии v1, он считается стабильным.
Репозиторий
Документация ¶
Обзор ¶
Пакет asn1 реализует синтаксический анализ структур данных ASN.1 в кодировке DER, как определено в Рекомендации МСЭ-Т X.690.
Индекс ¶
- func (b BitString) At(i int) int
- func (b BitString) RightAlign() []byte
- func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool
- func (oi ObjectIdentifier) String() string
- func (e StructuralError) Строка Error()
- func (e SyntaxError) Строка Error()
Константы ¶
Теги ASN.1 представляют тип следующего объекта.
Типы классов ASN.1 представляют пространство имен тега.
Переменные ¶
NullBytes содержит байты, представляющие тип ASN.1 NULL в кодировке DER.
NullRawValue – это RawValue, для которого в качестве тега задан тег типа ASN.1 NULL (5).
Функции ¶
Функ Маршал ¶
Marshal возвращает значение в кодировке ASN.1.
Помимо тегов структуры, распознаваемых Unmarshal, можно использовать следующие теги:
функция MarshalWithParams ¶ добавлена в версии go1.10
MarshalWithParams позволяет указать параметры поля для элемента верхнего уровня. Форма параметров такая же, как и у тегов полей.
функция Unmarshal ¶
Unmarshal анализирует закодированную DER структуру данных ASN.1 b и использует пакет Reflect для заполнения произвольного значения, на которое указывает val. Поскольку Unmarshal использует пакет Reflect, записываемые структуры должны использовать имена полей в верхнем регистре. Если val равно нулю или не является указателем, Unmarshal возвращает ошибку.
После синтаксического анализа b любые оставшиеся байты, не использованные для заполнения val, будут возвращены в состоянии покоя. При синтаксическом анализе SEQUENCE в структуру любые конечные элементы SEQUENCE, для которых нет соответствующих полей в val, не будут включены в rest, поскольку они считаются допустимыми элементами SEQUENCE, а не конечными данными.
Целое число ASN.1 может быть записано в int, int32, int64 или *big.Int (из пакета math/big). Если закодированное значение не соответствует типу Go, Unmarshal возвращает ошибку синтаксического анализа.
BIT STRING ASN.1 может быть записан в BitString.
СТРИНКА ОКТЕТОВ ASN.1 может быть записана в [] байт.
ИДЕНТИФИКАТОР ОБЪЕКТА ASN.1 может быть записан в ObjectIdentifier.
ASN.1 ENUMERATED можно записать в Enumerated.
В time.Time можно записать ASN.1 UTCTIME или GENERALIZEDTIME.
В строку можно записать PrintableString, IA5String или NumericString ASN.1.
Любое из приведенных выше значений ASN.1 может быть записано в интерфейс<>. Значение, хранящееся в интерфейсе, имеет соответствующий тип Go. Для целых чисел этим типом является int64.
Последовательность ASN.1 SEQUENCE OF x или SET OF x может быть записана в слайс, если x может быть записан в тип элемента слайса.
ПОСЛЕДОВАТЕЛЬНОСТЬ или НАБОР ASN.1 могут быть записаны в структуру, если каждый из элементов в последовательности может быть записан в соответствующий элемент в структуре.
Следующие теги в полях структуры имеют особое значение для Unmarshal:
При декодировании значения ASN.1 с тегом IMPLICIT в строковое поле Unmarshal по умолчанию использует PrintableString, который не поддерживает такие символы, как '@' и '&'. Чтобы принудительно использовать другие кодировки, используйте следующие теги:
Если тип первого поля структуры — RawContent, то в нем будет сохранено необработанное содержимое ASN1 структуры.
Если имя типа фрагмента заканчивается на "SET", то он обрабатывается так, как если бы для него был установлен тег "set". В результате тип интерпретируется как НАБОР x, а не как SEQUENCE OF x. Это можно использовать с вложенными фрагментами, где нельзя указать тег структуры.
Другие типы ASN.1 не поддерживаются; если они встречаются, Unmarshal возвращает ошибку синтаксического анализа.
функция UnmarshalWithParams ¶
UnmarshalWithParams позволяет указать параметры поля для элемента верхнего уровня. Форма параметров такая же, как и у тегов полей.
Типы ¶
введите BitString ¶
BitString — это структура, которую следует использовать, если вам нужен тип ASN.1 BIT STRING. Строка битов дополняется до ближайшего байта в памяти и записывается количество допустимых битов. Биты заполнения будут равны нулю.
func (BitString) At ¶
At возвращает бит по заданному индексу. Если индекс выходит за пределы допустимого диапазона, он возвращает false.
func (BitString) RightAlign ¶
RightAlign возвращает фрагмент, в котором биты заполнения находятся в начале. Слайс может совместно использовать память с BitString.
тип перечисляемый ¶
Enumerated представлен как простое целое число.
тип Флаг ¶
Флаг принимает любые данные и устанавливается в значение true, если он присутствует.
введите ObjectIdentifier ¶
Идентификатор ObjectIdentifier представляет ИДЕНТИФИКАТОР ОБЪЕКТА ASN.1.
func (ObjectIdentifier) Равно ¶
Equal сообщает, представляют ли oi и other один и тот же идентификатор.
Строкаfunc (ObjectIdentifier) ¶ добавлена в версии go1.3
введите RawContent ¶
RawContent используется, чтобы сигнализировать о том, что некодированные данные DER необходимо сохранить для структуры. Чтобы его использовать, первое поле структуры должно иметь этот тип. Использование этого типа в любом другом поле является ошибкой.
введите RawValue ¶
RawValue представляет собой незакодированный объект ASN.1.
введите StructuralError ¶
Ошибка StructuralError предполагает, что данные ASN.1 допустимы, но тип Go, который их получает, не совпадает.
Это эксплойт для уязвимости повреждения кучи в библиотеке Microsoft ASN.1. Эта уязвимость отличается от описанной в бюллетене eEye, но она также была исправлена в бюллетене MS04-007. АСН.1 эксплойты в фреймворках CANVAS и Metasploit основаны на этом коде.
Загрузки
Скриншот
Декодирование битовой строки в MSASN1.DLL
Уязвимость, связанная с повреждением кучи битовой строки в библиотеке Microsoft ASN.1, была обнаружена компанией eEye 10 февраля 2004 г. Эта уязвимость возникает при обработке битовой строки длиной 1 с 7 неиспользуемыми битами. Дополнительные сведения см. в бюллетене eEye AD20040210-2.
Битовые строки декодируются функцией BERDecBitString() в MSASN1.DLL. Эта функция выделяет новый буфер, копирует в него битовую строку и возвращает указатель на данные и их длину в битах. Сконструированные битовые строки (тег 0x23) обрабатываются путем рекурсивного вызова BERDecBitString() для каждого элемента и объединения результатов. Псевдокод BERDecBitString() приведен ниже:
Давайте посмотрим на пример битовой строки:
Для ясности мы будем использовать представление, аналогичное коду Perl, используемому для построения битовых строк в нашем эксплойте:
Когда эта сконструированная битовая строка, BERDecBitString() будет вызываться дважды для декодирования битовых строк "AA" и "B". Каждый раз размер буфера B будет увеличиваться путем вызова realloc, и к нему будет добавляться новая битовая строка.
Уязвимость realloc()
В MSASN1.DLL есть проблема, которую гораздо проще использовать, чем уязвимость eEye. Функция BERDecBitString неправильно обрабатывает сконструированные битовые строки, которые содержат сконструированные битовые строки.
Следующий пример иллюстрирует проблему:
Когда BERDecBitString вызывается для этой битовой строки, указатель bitbuf->ptr изначально имеет значение NULL. Поскольку это сконструированный элемент, снова вызывается BERDecBitString для декодирования битовой строки "AAAAAAAA". Он выделяет новый буфер размером 8 и копирует в него "AAAAAAAA". Новый буфер возвращается в bitbuf2->ptr. Мы вызываем realloc(bitbuf->ptr, 8), чтобы выделить место для 8 байтов данных. Так как bitbuf->ptr имеет значение NULL, этот вызов эквивалентен alloc(8). Копируем данные из bitbuf2->ptr в bitbuf->ptr и освобождаем bitbuf2->ptr.
Теперь bitbuf->ptr является указателем на 8-байтовый буфер, содержащий "AAAAAAAA". Исходный буфер был освобожден, и bitbuf2->ptr указывает на освобожденную память.
Мы возвращаемся к началу цикла while и вызываем BERDecBitString для декодирования следующего элемента, который является второй сконструированной битовой строкой. Прежде чем приступить к его декодированию, функция инициализирует bitbuf->length_in_bits равным 0, но bitbuf->ptr по-прежнему содержит значение, переданное вызывающей стороной, указатель на освобожденную память. Если бы bitbuf->ptr был инициализирован со значением NULL, как в версии MSASN1.DLL, включенной в патч MS04-007, уязвимости не существовало бы.
Первым элементом созданной строки битов является строка битов "BBBBBBBB", которая декодируется вызовом BERDecBitString. Он выделяет 8 байт, копирует "BBBBBBBB" в 8-байтовый буфер и возвращает указатель на него как bitbuf2->ptr.
Затем мы вычисляем новый размер буфера bitbuf->ptr и вызываем realloc(bitbuf->ptr, 8).
В этот момент bitbuf->ptr по-прежнему указывает на освобожденный фрагмент памяти, который раньше содержал «AAAAAAAA». Что происходит, когда мы передаем указатель на свободный фрагмент в функцию NtReallocateHeap()? Он должен возвращать NULL, чтобы указать на ошибку, но из-за недостатка дизайна он возвращает свой аргумент без изменений. Теперь программа считает, что выделила новый буфер размером 8, но на самом деле она собирается перезаписать первые 8 байт освободившегося фрагмента памяти битовой строкой "BBBBBBBB".
Обзор кучи NT
Распределитель кучи в Windows NT аналогичен реализации malloc в Linux. Вся доступная память разбита на куски, которые могут быть либо свободны, либо заняты. Каждый фрагмент имеет 8-байтовый заголовок, содержащий размер текущего фрагмента, размер предыдущего фрагмента и флаги. Пользовательские данные начинаются с блока +8.
Когда блок освобождается, он объединяется с предыдущим и следующим блоками, если они также свободны. Затем этот фрагмент вставляется в двусвязный список свободных фрагментов. Существует 127 таких списков, называемых выделенными списками, для фрагментов размером 8, 16, 24, 32 и до 1016. Фрагменты размером более 1024 байт хранятся в невыделенном списке, отсортированном в порядке возрастания их размера. р>
Когда приложение пытается выделить некоторый объем памяти, сначала выполняется поиск точного размера в выделенном списке. Если он пуст, ищется список для следующего размера блока и так далее, пока не будет найден свободный блок или мы не пройдемся по всем выделенным спискам, не найдя ни одного. В этом случае мы должны искать в невыделенном списке. Распределитель кучи начинается с начала списка и следует прямому указателю, пока не найдет достаточно большой фрагмент. Поскольку неспециализированный список отсортирован по размеру фрагмента, мы гарантированно найдем наименьший фрагмент, удовлетворяющий размеру нашего запроса. Если свободных фрагментов нет, мы должны запросить у ядра больше памяти.
Найденный свободный блок удаляется из списка свободных и помечается как "используется".Приложению возвращается указатель на пользовательские данные в чанке.
Важно отметить, что прямые и обратные указатели, которые связывают фрагменты в свободном списке, хранятся в первых 8 байтах пользовательских данных фрагмента. Это означает, что если мы сможем записать 8 байтов в уже освобожденный буфер, мы перезапишем указатели связанного списка. Как только это будет сделано, операции добавления фрагмента в список свободных и удаления фрагмента из списка можно использовать для записи в произвольное место в памяти.
Создание эксплойта
Битовая строка, используемая в эксплойте, сложна из-за того, что нам нужна функция декодера ASN.1 для выполнения определенной последовательности выделений памяти, чтобы повредить кучу, но не привести к сбою приложения.
Этот эксплойт отличается от большинства эксплойтов кучи Windows, поскольку мы можем управлять шаблоном распределения памяти декодера, предоставляя ему битовые строки разной длины. Это позволяет нам делать несколько произвольных перезаписей по 4 байта и использовать уязвимость с высокой степенью надежности.
Желаемая последовательность операций с памятью приведена ниже:
После того как мы перезаписываем указатели FW и BK освобожденного блока, мы выделяем блок того же размера. Функция NtHeapAllocate() проходит по связанному списку свободных блоков и достигает блока с перезаписанными указателями. Поскольку он удовлетворяет размеру запроса, он удаляется из связанного списка, помечается как используемый и возвращается в приложение. Если перезаписанные указатели FW и BK указывают на доступную для записи память, операция разъединения завершится успешно, но блок останется в списке свободных.
Когда блок освобождается, функция NtFreeHeap() просматривает связанный список свободных блоков, пока не найдет блок такого же или большего размера. Затем освобожденный блок вставляется в связанный список перед этим блоком. В нашем случае блок, который мы освобождаем, все еще будет в списке свободных, поэтому это будет блок, который мы найдем.
Когда NtFreeHeap() вставляет блок в список свободных, она манипулирует тремя блоками, помеченными A, B и C. Блок B должен быть вставлен между A и C. Код для этого приведен ниже:
В нашем случае блоки B и C одинаковы, поэтому код эквивалентен
Предполагая, что адрес B находится в ebx, выполняемые инструкции будут иметь тот же эффект, что и
Этот код записывает адрес блока, который мы освобождаем, в место, указанное значением в блоке +4. Возможность записи адреса нашего блока в произвольный адрес в памяти позволяет очень легко написать надежный эксплойт.
В нашем эксплойте мы перезаписываем указатель FastPebLockRoutine на PEB+0x20. Если мы позволим программе продолжить выполнение, буфер нашего шелл-кода может быть освобожден или перезаписан до того, как программа вызовет FastPebLockRoutine. Вот почему мы делаем окончательный запрос на выделение блока того же размера, что и наш блок шеллкода. Во время поиска подходящего свободного блока NtAllocateHeap() находит блок шеллкода (который все еще находится в свободном списке) и пытается отвязать его. Если мы убедимся, что первые два двойных слова в шелл-коде указывают на недопустимое место в памяти (например, 0x90909090), возникнет исключение.
Исключение обрабатывается обработчиком исключений в LSASRV.DLL, который регистрирует следующее сообщение в журнале событий:
"Согласование пакета безопасности вызвало исключение. Теперь пакет отключен."
Одна из функций, участвующих в записи в журнал событий, блокирует PEB, вызывая FastPebLockRoutine, которая теперь указывает на наш шелл-код.
После отключения протокола Negotiate система продолжает работать, но интерактивный вход невозможен.
Шелл-код
Эта уязвимость предъявляет особые требования к нашему шеллкоду, что делает невозможным использование общей полезной нагрузки. Во время выполнения обработчика исключений процесс LSASS.EXE блокируется и не может обслуживать какие-либо запросы проверки подлинности. Если наш шелл-код выполняет процесс или использует системную функцию, которая отправляет запрос в локальный орган безопасности, произойдет взаимоблокировка. Если LSASS.EXE будет уничтожен, система перезагрузится. Очень важно, чтобы обработчик исключений в LSASS мог продолжать свое выполнение.
Для этого наш шеллкод разделен на два этапа. Этап 0 восстанавливает указатель FastPebLockRoutine и восстанавливает кучу. Затем шелл-код этапа 1 выполняется в новом потоке. В родительском потоке шелл-код стадии 0 переходит к RtlEnterCriticalSection, и выполнение обработчика исключений продолжается без каких-либо следов эксплуатации.
Нет практически никаких ограничений на то, что может делать шелл-код стадии 1, если он не приводит к сбою процесса, в котором он запущен. После завершения шелл-кода он должен завершить свой поток, вызвав ExitThread, или просто выполнить инструкцию возврата.
Примечания
Эт эксплойт был протестирован и, как известно, работает в Windows 2000 SP2, SP3, SP4 и Windows XP SP0 и SP1.
В Windows 2000 SP0 и SP1 указатель FastPebLockRoutine перезаписывается, но никогда не вызывается LSASS.EXE. Необходимо использовать альтернативный метод эксплуатации, такой как перезапись обработчика необработанных исключений.
Читайте также: