Небезопасная десериализация

Категория OWASP: MASVS-CODE: Качество кода

Обзор

При хранении или передаче больших объемов данных объектов Java зачастую более эффективно сначала сериализовать данные. Затем данные подвергаются процессу десериализации принимающим приложением, действием или поставщиком, который в конечном итоге обрабатывает данные. В обычных обстоятельствах данные сериализуются, а затем десериализуются без какого-либо вмешательства пользователя. Однако доверительные отношения между процессом десериализации и его предполагаемым объектом могут быть злоупотреблены злоумышленником, который может, например, перехватить и изменить сериализованные объекты. Это позволит злоумышленнику выполнять такие атаки, как отказ в обслуживании (DoS), повышение привилегий и удаленное выполнение кода (RCE).

Хотя класс Serializable является распространенным методом управления сериализацией, в Android есть собственный класс для обработки сериализации, который называется Parcel . Используя класс Parcel , данные объекта можно сериализовать в данные потока байтов и упаковать в Parcel с помощью интерфейса Parcelable . Это позволяет более эффективно транспортировать или хранить Parcel .

Тем не менее, при использовании класса Parcel следует уделить особое внимание, поскольку он предназначен для высокоэффективного транспортного механизма IPC, но его не следует использовать для хранения сериализованных объектов в локальном постоянном хранилище, поскольку это может привести к проблемам совместимости данных. или потеря. Когда данные необходимо прочитать, интерфейс Parcelable можно использовать для десериализации Parcel и превращения его обратно в данные объекта.

Существует три основных направления использования десериализации в Android:

  • Использование неверного предположения разработчика о том, что десериализация объектов, происходящих из пользовательского типа класса, безопасна. В действительности любой объект, полученный из любого класса, потенциально может быть заменен вредоносным содержимым, которое в худшем случае может помешать работе загрузчиков классов того же или других приложений. Это вмешательство принимает форму введения опасных значений, которые, в зависимости от цели класса, могут привести, например, к краже данных или захвату учетной записи.
  • Использование методов десериализации, которые по своей конструкции считаются небезопасными (например, CVE-2023-35669 , уязвимость локального повышения привилегий, которая позволяла внедрять произвольный код JavaScript через вектор десериализации глубоких ссылок).
  • Использование недостатков в логике приложения (например, CVE-2023-20963 , локальная уязвимость повышения привилегий, которая позволяла приложению загружать и выполнять код в привилегированной среде из-за уязвимости в логике пакетов Android WorkSource).

Влияние

Любое приложение, которое десериализует ненадежные или вредоносные сериализованные данные, может быть уязвимо для удаленного выполнения кода или атак типа «отказ в обслуживании».

Риск: десериализация ненадежных входных данных.

Злоумышленник может воспользоваться отсутствием проверки посылок в логике приложения, чтобы внедрить произвольные объекты, которые после десериализации могут заставить приложение выполнить вредоносный код, что может привести к отказу в обслуживании (DoS), повышению привилегий и удаленному выполнению кода. (РЦЭ).

Эти типы атак могут быть незаметными. Например, приложение может содержать намерение, ожидающее только один параметр, который после проверки будет десериализован. Если злоумышленник отправит второй, неожиданный вредоносный дополнительный параметр вместе с ожидаемым, это приведет к десериализации всех введенных объектов данных, поскольку намерение рассматривает дополнительные параметры как 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 . Затем для чтения сериализованной посылки можно использовать следующий более типобезопасный метод readParcelable :

Котлин

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Ява

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

Обратите внимание, что в приведенном выше примере Java используется UserParcelable.CREATOR внутри метода. Этот обязательный параметр сообщает методу readParcelable , какой тип ожидать, и является более производительным, чем устаревшая версия метода readParcelable .

Конкретные риски

В этом разделе собраны риски, которые требуют нестандартных стратегий смягчения или были устранены на определенном уровне SDK и приведены здесь для полноты картины.

Риск: нежелательная десериализация объектов

Реализация интерфейса Serializable внутри класса автоматически приведет к тому, что все подтипы данного класса реализуют этот интерфейс. В этом сценарии некоторые объекты могут наследовать вышеупомянутый интерфейс, а это означает, что определенные объекты, которые не предназначены для десериализации, все равно будут обрабатываться. Это может непреднамеренно увеличить поверхность атаки.

Смягчения

Если класс наследует интерфейс Serializable , согласно руководству OWASP , метод readObject должен быть реализован следующим образом, чтобы избежать десериализации набора объектов в классе:

Котлин

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Ява

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

Ресурсы