Категория OWASP: MASVS-CODE: Качество кода
Обзор
При хранении или передаче больших объемов данных Java-объектов часто эффективнее сначала сериализовать данные. Затем данные проходят процесс десериализации в принимающем приложении, активности или поставщике, который в конечном итоге обрабатывает данные. В обычных условиях данные сериализуются, а затем десериализуются без какого-либо вмешательства пользователя. Однако доверительные отношения между процессом десериализации и целевым объектом могут быть использованы злоумышленником, который может, например, перехватить и изменить сериализованные объекты. Это позволит злоумышленнику совершать такие атаки, как отказ в обслуживании (DoS), повышение привилегий и удаленное выполнение кода (RCE).
Хотя класс Serializable является распространенным методом управления сериализацией, в Android есть собственный класс для обработки сериализации, называемый Parcel . Используя класс Parcel , данные объекта могут быть сериализованы в поток байтов и упакованы в Parcel с помощью интерфейса Parcelable . Это позволяет более эффективно передавать или хранить Parcel .
Тем не менее, следует проявлять осторожность при использовании класса Parcel , поскольку он предназначен для высокоэффективной передачи данных по межпроцессному взаимодействию, но не должен использоваться для хранения сериализованных объектов в локальном постоянном хранилище, так как это может привести к проблемам совместимости данных или их потере. Когда данные необходимо прочитать, можно использовать интерфейс Parcelable для десериализации Parcel и преобразования его обратно в объектные данные.
В Android существует три основных способа использования уязвимости десериализации:
- Использование ошибочного предположения разработчика о безопасности десериализации объектов, созданных на основе пользовательского класса. В действительности, любой объект, созданный любым классом, может быть потенциально заменен вредоносным содержимым, которое в худшем случае может помешать работе загрузчиков классов этого или других приложений. Это вмешательство принимает форму внедрения опасных значений, которые, в зависимости от назначения класса, могут привести, например, к утечке данных или захвату учетной записи.
- Использование уязвимостей в методах десериализации, которые по своей природе считаются небезопасными (например, CVE-2023-35669 , уязвимость локального повышения привилегий, позволявшая внедрять произвольный код JavaScript через вектор десериализации с помощью глубоких ссылок).
- Использование уязвимостей в логике приложения (например, CVE-2023-20963 , локальная уязвимость повышения привилегий, которая позволяла приложению загружать и выполнять код в привилегированной среде из-за уязвимости в логике пакетов WorkSource Android).
Влияние
Любое приложение, десериализующее ненадежные или вредоносные сериализованные данные, может быть уязвимо для удаленного выполнения кода или атак типа «отказ в обслуживании».
Риск: Десериализация ненадежных входных данных.
Злоумышленник может использовать отсутствие проверки пакетов в логике приложения для внедрения произвольных объектов, которые после десериализации могут заставить приложение выполнить вредоносный код, что может привести к отказу в обслуживании (DoS), повышению привилегий и удаленному выполнению кода (RCE).
Подобные атаки могут быть скрытными. Например, приложение может содержать интенты, ожидающие только один параметр, который после проверки будет десериализован. Если злоумышленник отправит второй, неожиданный вредоносный дополнительный параметр вместе с ожидаемым, это приведет к десериализации всех внедренных объектов данных, поскольку интент рассматривает дополнительные параметры как Bundle . Злоумышленник может использовать это поведение для внедрения данных объектов, которые после десериализации могут привести к удаленному выполнению кода (RCE), компрометации данных или их потере.
Меры по смягчению последствий
В качестве оптимальной практики следует исходить из того, что все сериализованные данные являются ненадежными и потенциально вредоносными. Для обеспечения целостности сериализованных данных необходимо проводить проверки, чтобы убедиться, что они соответствуют ожидаемому приложению классу и формату.
Возможным решением может стать реализация шаблона предварительного просмотра для библиотеки java.io.ObjectInputStream . Изменив код, отвечающий за десериализацию, можно гарантировать, что в рамках намерения будет десериализован только явно указанный набор классов .
Начиная с Android 13 (уровень API 33), в классе Intent были обновлены несколько методов, которые считаются более безопасными альтернативами старым и теперь устаревшим методам обработки пакетов. Эти новые методы, обеспечивающие более безопасную типизацию, такие как getParcelableExtra(java.lang.String, java.lang.Class) и getParcelableArrayListExtra(java.lang.String, java.lang.Class) выполняют проверку типов данных для выявления уязвимостей несоответствия, которые могут привести к сбоям приложений и потенциально могут быть использованы для проведения атак с повышением привилегий, таких как CVE-2021-0928 .
Следующий пример демонстрирует, как можно реализовать безопасную версию класса Parcel :
Предположим, класс UserParcelable реализует Parcelable и создает экземпляр пользовательских данных, которые затем записываются в объект Parcel . В этом случае для чтения сериализованного объекта Parcel можно использовать следующий типобезопасный метод класса readParcelable :
Котлин
val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)
Java
Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);
Обратите внимание на использование UserParcelable.CREATOR в приведенном выше примере на Java внутри метода. Этот обязательный параметр указывает методу readParcelable какой тип данных следует ожидать, и он более производительен, чем устаревшая версия метода readParcelable .
Специфические риски
В этом разделе собраны риски, требующие нестандартных стратегий смягчения или которые были смягчены на определенном уровне SDK, и они приведены здесь для полноты информации.
Риск: Десериализация нежелательных объектов
Реализация интерфейса Serializable внутри класса автоматически приведет к тому, что все подтипы данного класса будут реализовывать этот интерфейс. В этом сценарии некоторые объекты могут наследовать указанный интерфейс, что означает, что определенные объекты, которые не предназначены для десериализации, все равно будут обрабатываться. Это может непреднамеренно увеличить поверхность атаки.
Меры по смягчению последствий
Согласно рекомендациям OWASP , если класс наследует интерфейс Serializable , метод readObject должен быть реализован следующим образом, чтобы предотвратить десериализацию набора объектов класса:
Котлин
@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
throw IOException("Cannot be deserialized")
}
Java
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Ресурсы
- Посылки
- Посылка
- Сериализуемый
- Намерение
- Уязвимости десериализации в Android: краткая история.
- Android Parcels: Плохое, хорошее и улучшенное (видео)
- Android Parcels: плохое, хорошее и лучшее (слайды презентации)
- CVE-2014-7911: Повышение привилегий в Android <5.0 с использованием ObjectInputStream
- CVE-CVE-2017-0412
- CVE-2021-0928: Несоответствие сериализации/десериализации посылок
- Руководство OWASP