- Deserialize error что это значит
- Введение
- Десериализация в PHP
- Десериализация в Python
- Десериализация в Java
- Десериализация YAML
- Заключение
- CA2329. Не выполняйте десериализацию с помощью JsonSerializer, используя небезопасную конфигурацию
- Причина
- Описание правила
- Устранение нарушений
- Условия для отключения предупреждений
- Настройка кода для анализа
- Исключение определенных символов
- Исключение определенных типов и их производных типов
Deserialize error что это значит
В рейтинге критических рисков веб-приложений OWASP Top Ten на восьмой позиции расположены недостатки десериализации недоверенных данных. Эта уязвимость имеет идентификатор CWE-502 и заключается в том, что приложение десериализует данные из недоверенного источника без достаточной их валидации. В результате атаки на механизмы десериализации злоумышленник нередко получает возможность удаленно исполнять команды в скомпрометированной системе.
В этой статье мы разберем самые распространенные случаи возникновения уязвимости небезопасной десериализации и примеры ее эксплуатации, а также поделимся рекомендациями по устранению таких угроз.
Введение
Что же такое десериализация?
Сериализация — это представление специфичных для языка программирования структур и объектов в едином формате, как правило, в виде строки определенного формата или последовательности байтов.
Десериализация — это обратный процесс: восстановление структур и объектов из сериализованной строки или последовательности байтов.
Сериализацию и десериализацию часто используют для сохранения состояния программы, например на диске или в базе данных, а также для обмена данными между различными приложениями.
Современные языки программирования предоставляют удобные механизмы для сериализации и десериализации своих структур. Поэтому разработчики нередко прибегают к ним: это просто, быстро, не требует дополнительных библиотек и позволяет не беспокоиться о проблемах совместимости сериализованных данных.
Вместе с тем гибкие механизмы сериализации и десериализации предоставляют гораздо больше возможностей, нежели просто представление объектов в едином формате. К сожалению, многие разработчики не уделяют должного внимания этим механизмам и допускают различные ошибки при написании кода, которые, в свою очередь, ведут к серьезным проблемам в безопасности приложения.
Десериализация в PHP
Для сериализации и десериализации в PHP используются функции serialize() и unserialize() соответственно.
serialize() принимает в качестве параметра объект и возвращает его сериализованное представление в виде строки.
unserialize() принимает в качестве параметра строку, содержащую сериализованный объект, и возвращает десериализованный объект, восстановленный из этой строки.
Рассмотрим на простом примере.
В этом примере присутствует класс Injection , реализующий магический метод __wakeup() . Данный метод будет выполнен сразу после десериализации объекта класса Injection и, как можно увидеть, исполнит код, хранящийся в переменной класса $some_data .
С помощью следующего кода сгенерируем полезную нагрузку для эксплуатации подобной конструкции:
В результате выполнения этого кода получим следующий сериализованный объект:
И обратимся к нашему уязвимому приложению, передав в качестве данных в параметре data этот сериализованный объект:
В результате исполнения данного кода и десериализации переданного нами объекта будет исполнена встроенная функция PHP phpinfo() . Таким образом злоумышленник получит возможность удаленного исполнения кода в уязвимой системе.
Стоит отметить, что не всегда эксплуатация уязвимости небезопасной десериализации в PHP ведет к удаленному исполнению кода. Иногда она приводит к чтению или записи произвольных файлов, SQL-инъекциям, отказу в обслуживании и так далее.
Для проведения успешной атаки необходимо, чтобы в приложении присутствовали классы, реализующие те или иные магические методы. Как правило, для целей эксплуатации наиболее полезны методы __destruct() , __wakeup() и __toString() . Кроме того, чтобы найти уязвимый класс или цепочку классов (так называемый гаджет), обычно нужен доступ к исходному коду приложения.
Вместе с тем приложения зачастую реализуются с использованием различных фреймворков, которые уже содержат подходящие гаджеты. В таком случае для генерации полезной нагрузки можно воспользоваться утилитой PHPGGC.
Десериализация в Python
Механизмы сериализации и десериализации в Python и в PHP во многом схожи. В Python для этих целей используется встроенная библиотека pickle .
pickle.dump() принимает в качестве параметров объект и имя файла и записывает переданный объект в файл с переданным именем в сериализованном виде.
pickle.load() принимает в качестве параметра имя файла, содержащего сериализованный объект, и возвращает десериализованный объект.
pickle.dumps() принимает в качестве параметра объект и возвращает его сериализованное представление в виде байтовой строки.
pickle.loads() принимает в качестве параметра байтовую строку, содержащую сериализованный объект, и возвращает десериализованный объект, восстановленный из этой строки.
Рассмотрим на простом примере.
С помощью следующего кода сгенерируем полезную нагрузку для эксплуатации подобной конструкции:
В результате выполнения этого кода получим следующий сериализованный объект:
И обратимся к нашему уязвимому приложению, передав в качестве данных в параметре data этот сериализованный объект, который представлен в URL-кодированном виде:
В результате исполнения данного кода и десериализации переданного нами объекта будет вызвана функция os.system() с параметром ls . Она осуществит вывод списка файлов в текущей рабочей директории приложения. Таким образом злоумышленник может получить возможность удаленного исполнения кода в уязвимой системе.
В случае с Python для успешного проведения атаки не требуется каких-либо дополнительных предпосылок. Поэтому для безопасности следует избегать использования pickle.loads() для десериализации данных, полученных из недоверенного источника.
Десериализация в Java
Десериализация в Java схожа с тем же процессом в PHP и Python.
Как правило, используются следующие конструкции:
- метод readObject() класса java.beans.XMLDecoder ;
- метод fromXML() класса com.thoughtworks.xstream.XStream ;
- методы readObject() , readObjectNodData() , readResolve() , readExternal() , readUnshared() класса java.io.ObjectInputStream .
Рассмотрим использование метода readObject() класса java.io.ObjectInputStream на простом примере.
С помощью следующего кода сгенерируем полезную нагрузку для эксплуатации подобной конструкции:
В результате выполнения этого кода получим следующий сериализованный объект:
И для удобства взаимодействия с бинарными данными — его же в base64-кодированном виде:
Обратимся к нашему уязвимому приложению, передав в качестве входного параметра этот сериализованный объект в base64-кодированном виде:
В результате исполнения данного кода и десериализации переданного нами объекта будет вызвана функция Runtime.getRuntime().exec() с параметром wget http://example.com:8080 , после чего будет получен «отстук» на контролируемом нами сервере example.com :
Таким образом злоумышленник может получить возможность удаленного исполнения кода в уязвимой системе.
В Java, как и в PHP, для получения удаленного исполнения кода необходимо наличие подходящего класса, реализующего интерфейс Serializable . В нашем примере таким классом был Injection . И, как и в PHP, поиск подходящего гаджета практически не осуществим без доступа к исходному коду приложения. В случае, если приложение реализовано с использованием некоторых фреймворков и библиотек классов, для генерации полезной нагрузки можно прибегнуть к помощи утилиты ysoserial.
Десериализация YAML
В различных языках и фреймворках есть возможность получить удаленное исполнение кода в ходе десериализации YAML.
Так, например, исполнение подобного кода на Python приведет к выводу листинга текущей директории:
Это довольно распространенная проблема, но, поскольку в разных языках функциональность обработки YAML-файлов реализуется по-разному, мы не будем подробно останавливаться на конкретных примерах.
Как можно заметить в коде выше, в вызов функции yaml.load() был явно передан аргумент Loader=yaml.UnsafeLoader . Это важно: в актуальных версиях библиотеки разработчики позаботились о том, чтобы по умолчанию не использовались уязвимые методы.
Сейчас при вызове yaml.load() без дополнительных параметров мы получим сообщение об ошибке:
Вместе с тем в ранних версиях функция yaml.load() не ограничивала возможность исполнения управляющих конструкций, а чтобы безопасно десериализовать недоверенный YAML, нужно было прибегнуть к функции yaml.safe_load() . Подобную уязвимость все еще можно встретить во многих приложениях, которые используют более старые версии библиотек для работы с YAML.
Поэтому мы рекомендуем не полагаться на предусмотрительность поставщика библиотеки, а выбирать заведомо безопасные конструкции по типу yaml.safe_load() .
Заключение
Сериализация и десериализация, несомненно, мощные и гибкие инструменты, которые позволяют разработчикам легче манипулировать данными, сохранять их на диск или в базу данных, передавать их по сети. Но, как и в реальной жизни, в работе с каким-либо инструментом важно делать это правильно и соблюдать технику безопасности.
Простого и универсального метода защиты приложения от атак на десериализацию, к сожалению, не существует (разве что полный отказ от использования этого механизма). Поэтому вот несколько наших рекомендаций по безопасному использованию механизма десериализации:
- По возможности использовать безопасные методы десериализации, например yaml.safe_load() вместо yaml.load() .
- Пользоваться более простыми форматами (например, JSON) для передачи данных и для их сохранения на диск или в базу данных. Они, как правило, менее функциональные, но и не несут таких угроз, как встроенные механизмы сериализации.
- Вести белый список допустимых классов. Разработчик может переопределить стандартную функциональность, которая используется для десериализации, и при загрузке объекта убедиться, что десериализация переданного объекта разрешена и используемые в сериализованном объекте конструкции безопасны.
- Осуществлять подпись передаваемых сериализованных данных. Этот вариант хорошо подходит для сетевого обмена данными между приложениями. Без знания секретного ключа, использованного для подписи передаваемых данных, злоумышленник не сможет внести в них изменения. Однако стоит помнить, что приложение или секретный ключ могут быть скомпрометированы другим способом, и это может негативно сказаться на безопасности связанных приложений.
- Использовать сторонние библиотеки и фреймворки, спроектированные специально для повышения безопасности процедур десериализации. Например, SerialKiller или NotSoSerial для Java.
Соблюдать эти рекомендации не всегда просто, особенно когда перед разработчиком стоит задача поддержки уже имеющегося кода. Но, учитывая, что атаки на механизмы десериализации могут привести к удаленному исполнению кода и полной компрометации системы, такие трудо- и времязатраты зачастую жизненно необходимы.
Источник
CA2329. Не выполняйте десериализацию с помощью JsonSerializer, используя небезопасную конфигурацию
—>
Значение | |
---|---|
Идентификатор правила | CA2329 |
Категория | Безопасность |
Исправление является критическим или не критическим | Не критическое |
Причина
Это правило срабатывает, если для экземпляра Newtonsoft.Json.Jsonserialize, который передается в метод десериализации или инициализируется как поле или свойство, справедливы оба следующих условия:
- Свойство TypeNameHandling имеет значение, отличное от None .
- Свойство SerializationBinder имеет значение NULL.
По умолчанию это правило анализирует всю базу кода, но такое поведение можно настроить.
Описание правила
Небезопасные десериализаторы уязвимы при десериализации ненадежных данных. Злоумышленник может изменить сериализованные данные и включить в них непредвиденные типы для внедрения объектов с вредоносными побочными эффектами. Атака против небезопасного десериализатора может, например, выполнять команды в базовой операционной системе, отсылать сообщения по сети или удалять файлы.
Это правило находит экземпляры Newtonsoft.Json.JsonSerializer, настроенные для десериализации типов, указанных из входных данных, но не настроенные для ограничения десериализованных типов с помощью интерфейса Newtonsoft.Json.Serialization.ISerializationBinder. Чтобы полностью запретить десериализацию типов, указанных из входных данных, отключите правила CA2327, CA2328, CA2329 и CA2330 и включите вместо них правило CA2326.
Устранение нарушений
- По возможности используйте для параметра TypeNameHandling значение None .
- Примените к сериализованным данным защиту от несанкционированных изменений. После сериализации криптографически подпишите сериализованные данные. Перед десериализацией проверьте криптографическую подпись. Защитите криптографический ключ от раскрытия и реализуйте регулярную смену ключей.
- Ограничьте десериализованные типы. Реализуйте пользовательский интерфейс Newtonsoft.Json.Serialization.ISerializationBinder. Перед десериализацией с помощью Json.NET убедитесь, что пользовательский интерфейс ISerializationBinder указан в свойстве Newtonsoft.Json.JsonSerializer.SerializationBinder. Если в переопределенном методе Newtonsoft.Json.Serialization.ISerializationBinder.BindToType тип является непредвиденным, возвращается null или вызывается исключение для остановки десериализации.
Условия для отключения предупреждений
Можно отключить вывод предупреждений для этого правила в следующих случаях:
- Вам известно, что входные данные являются доверенными. Учитывайте, что со временем могут измениться как границы доверия, так и потоки данных приложения.
- Вы выполнили одну из мер предосторожности из раздела Устранение нарушений.
Настройка кода для анализа
Используйте следующие параметры, чтобы указать части базы кода, к которым будет применяться это правило.
Эти параметры можно настроить только для указанного правила, для всех правил или для всех правил в этой категории (безопасность). Дополнительные сведения см. в статье Параметры конфигурации правила качества кода.
Исключение определенных символов
Вы можете исключить из анализа определенные символы, например типы и методы. Например, чтобы указать, что правило не должно выполняться для какого-либо кода в типах с именем MyType , добавьте следующую пару «ключ-значение» в файл EDITORCONFIG в своем проекте:
Допустимые форматы имени символа в значении параметра (разделенные | ):
- Только имя символа (включает все символы с этим именем, любого типа и в любом пространстве имен).
- Полные имена в формате идентификатора документации для символа. Для каждого имени символа требуется префикс в виде символа, например M: для методов, T: для типов и N: для пространств имен.
- .ctor используется для конструкторов, а .cctor — для статических конструкторов.
Значение параметра | Итоги |
---|---|
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType | Соответствует всем символам с именем MyType . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 | Соответствует всем символам с именем MyType1 или MyType2 . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) | Соответствует конкретному методу MyMethod с заданной полной сигнатурой. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) | Соответствует конкретным методам MyMethod1 и MyMethod2 с соответствующими полными сигнатурами. |
Исключение определенных типов и их производных типов
Из анализа можно исключать определенные типы и их производные типы. Например, чтобы указать, что правило не должно выполняться в каких-либо методах типов MyType и их производных типов, добавьте следующую пару «ключ-значение» в файл .editorconfig своего проекта:
Допустимые форматы имени символа в значении параметра (разделенные | ):
- Только имя типа (включает все типы с этим именем, любого типа и в любом пространстве имен).
- Полные имена в формате идентификатора документации для символа с необязательным префиксом T: .
Источник