
Почти все программисты на Python хотя бы раз применяли модуль pickle. Он входит в стандартную библиотеку языка, требует минимум кода для работы и способен сохранять практически любые объекты без дополнительных настроек.
Благодаря этим преимуществам pickle часто применяют для хранения состояния программы, обмена данными между процессами, кэширования, работы с ML-моделями и внутренними сервисами.
Однако у такой простоты есть серьезный недостаток. В отличие от JSON, pickle воссоздает не просто данные, а полноценные объекты Python. Если злоумышленник получает контроль над данными, которые десериализует приложение, это может привести к выполнению произвольного кода на сервере. Неслучайно уязвимости десериализации годами входят в топ наиболее опасных веб-уязвимостей.
Далее мы рассмотрим причины такой уязвимости, принципы работы Pickle, как пентестеры выявляют подобные проблемы и какие меры защиты должны предпринимать разработчики.
В завершении статьи вы сможете перейти к практическому заданию из бесплатного курса «Профессия Белый Хакер» и увидеть работу уязвимости в учебном примере.
Сериализация: назначение и применение
Объекты Python невозможно напрямую сохранить в файл или передать по сети. Для этого их сначала преобразуют в последовательность байтов — этот процесс называется сериализацией.
Обратное преобразование — восстановление объекта из байтов — именуется десериализацией. Именно этот этап представляет наибольший риск с точки зрения безопасности.
Сериализация применяется практически в каждом приложении:
- сохранение состояния программы;
- кэширование;
- обмен данными между сервисами;
- передача объектов между процессами;
- хранение результатов вычислений.
Для большинства задач используют универсальные форматы вроде JSON, XML или YAML. Однако они поддерживают только простые структуры данных. Когда требуется сохранить сложный объект Python со всеми атрибутами, многие выбирают Pickle.
Ключевые отличия Pickle от JSON
На первый взгляд JSON и Pickle решают схожие задачи — позволяют сохранять данные. Однако их принципы работы кардинально различаются.
После обработки JSON приложение получает лишь стандартные структуры: словари, списки, строки, числа и булевы значения. При этом никакие функции не выполняются.
Pickle предназначен для полного восстановления объектов Python. Интерпретатор читает сериализованный поток и выполняет все необходимые действия для реконструкции: создает экземпляры классов, восстанавливает их состояние и при необходимости вызывает специальные методы.

Именно поэтому десериализация через Pickle принципиально отличается от разбора JSON. Она не просто читает данные, а активно участвует в восстановлении объектов. Если злоумышленник контролирует данные, передаваемые в pickle.loads(), механизм восстановления может быть использован для выполнения произвольных команд.
По этой причине разработчики Python настоятельно рекомендуют никогда не десериализовать через Pickle данные из ненадежных источников.
Метод __reduce__: ключевой механизм для RCE
Основную роль в большинстве атак играет специальный метод __reduce__.
Большинство разработчиков никогда не используют его напрямую. Однако именно он определяет, как объект должен быть восстановлен после десериализации.
Упрощенно, __reduce__ отвечает на вопрос:
«Какие действия нужны для воссоздания этого объекта?»
Вместо готового объекта метод возвращает инструкции по его восстановлению.
Например:
- какую функцию вызвать;
- какие аргументы передать;
- какое состояние установить.
В обычных условиях это абсолютно легальный механизм.
Проблема возникает, когда описание объекта формирует злоумышленник.
В учебных материалах часто приводят классический пример: метод __reduce__ возвращает вызов системной команды с заданными параметрами. При десериализации эта команда автоматически выполняется.

Именно поэтому говорят, что Pickle способен выполнять код при десериализации.
На самом деле код не «встроен» в файл.
Он вызывается самим механизмом восстановления объекта.
Почему это считается удаленным выполнением кода
Представим приложение, которое получает сериализованный объект через HTTP-запрос и без проверок передает его в pickle.loads():
obj = pickle.loads(request.data)
Если злоумышленник контролирует содержимое запроса, он может подменить объект на вредоносный. При десериализации приложение выполнит инструкции по его восстановлению, что может привести к RCE.
Именно поэтому уязвимости десериализации считаются крайне опасными и могут приводить к удаленному выполнению кода.
Почему Base64 не обеспечивает защиту
Многие приложения дополнительно кодируют сериализованные данные в Base64.
Например:
gASVOAAAAAAAAACM…
Это создает ложное впечатление безопасности. На самом деле Base64 лишь преобразует бинарные данные в текстовый формат. После декодирования приложение получает тот же поток байтов Pickle. Для атакующего это не представляет серьезного препятствия.
Типичные сценарии использования Pickle
Некоторые считают, что Pickle применяют только новички.
В реальности это не так.
Он широко используется внутри доверенной инфраструктуры, где риски считаются приемлемыми.
Например:
- сохранение обученных ML-моделей;
- обмен объектами между процессами Python;
- внутренние очереди задач;
- кэширование сложных объектов;
- сохранение состояния приложений;
- служебные файлы.
Сам по себе Pickle не является уязвимостью.
Проблемы начинаются, когда сериализованные данные попадают под контроль пользователя.
Именно этот момент обычно становится отправной точкой при аудите безопасности.
Как выявляют небезопасную десериализацию
На практике поиск уязвимости начинают не с изучения эксплойтов. Сначала исследователь замечает подозрительные признаки, а затем проверяет использование Pickle.
Первый источник информации — исходный код.
При аудите следует обращать внимание на вызовы:
pickle.loads(…)
pickle.load(…)
Если аргументы этих функций полностью или частично формируются пользователем, это повод для детального анализа.
Однако исходный код не всегда доступен.
При внешнем тестировании ищут косвенные признаки.
Например, приложение может получать длинные строки в параметрах запроса, куках или теле POST-запроса. Иногда они выглядят как случайный набор символов, но после декодирования оказываются сериализованными объектами Pickle.
Опытный исследователь всегда задает ключевые вопросы:
- откуда поступают данные;
- проходят ли они проверку;
- действительно ли сформированы приложением;
- можно ли изменить их содержимое.
Именно цепочка «получение данных от пользователя → pickle.loads()» представляет наибольший интерес.
Косвенные признаки использования Pickle
В реальных приложениях редко встречаются явные указания на уязвимость.
Чаще приходится анализировать косвенные признаки.
Вот что обычно привлекает внимание при аудите:
- использование модулей pickle, dill или joblib;
- данные в Base64;
- файлы с расширениями .pkl, .pickle или .joblib;
- API, принимающие сериализованные объекты;
- кэширование сложных структур.
Ни один из этих признаков сам по себе не означает уязвимость.
Например, сохранение ML-модели в Pickle — нормальная практика.
Опасность возникает, когда пользователь получает контроль над сериализованными данными.
Поэтому задача пентестера — найти место, где ненадежные данные попадают в десериализацию.
Почему фильтрация малоэффективна
Иногда разработчики пытаются защититься проверками.
Например:
- блокируют определенные строки;
- удаляют символы;
- проверяют размер;
- фильтруют содержимое после Base64.
К сожалению, такие меры редко обеспечивают надежную защиту.
Причина проста.
Проблема не в конкретной строке внутри данных.
Опасен сам механизм восстановления объекта.
Если приложение передает данные в pickle.loads(), оно соглашается выполнить все инструкции по восстановлению.
Поэтому фильтрация превращается в бесконечную гонку.
Официальная рекомендация Python остается неизменной: не десериализуйте через Pickle данные из ненадежных источников. Если злоумышленник контролирует данные, он может добиться выполнения произвольного кода.
Где чаще всего встречаются уязвимости
Большинство проблем возникает не в публичных API.
Обычно разработчики уверены, что компонент используется только внутри доверенной инфраструктуры.
Например:
- обмен объектами между внутренними сервисами;
- сохранение состояния в Redis;
- передача объектов между процессами;
- загрузка ML-моделей из файлов.
Со временем инфраструктура меняется.
Появляются новые интеграции.
Добавляется загрузка пользовательских файлов.
Возникает возможность импорта резервных копий.
Именно тогда границы доверия могут незаметно измениться.
Код при этом остается прежним.
В результате функция, работавшая только с доверенными данными, начинает принимать объекты от пользователя.
Так возникает классическая уязвимость десериализации.
Меры защиты
Главное правило простое.
Не используйте Pickle для данных, происхождение которых нельзя полностью контролировать.

Для обмена данными между клиентом и сервером лучше выбрать форматы, которые описывают только данные, а не процесс восстановления.
Чаще всего подходят:
- JSON;
- MessagePack;
- Protocol Buffers;
- другие безопасные форматы.
Если отказаться от Pickle невозможно, соблюдайте правила.
Во-первых, данные должны поступать только из доверенных источников.
Во-вторых, проверяйте их целостность с помощью цифровой подписи или HMAC. Это не делает Pickle безопасным, но помогает обнаружить подмену.
В-третьих, четко определяйте границы доверия. Даже если сегодня объекты создаются только вашим приложением, через год архитектура может измениться.
Ключевые выводы
Подведем итоги.
Pickle сам по себе не является уязвимостью.
Это удобный инструмент сериализации, отлично подходящий для доверенной среды.
Проблемы начинаются, когда приложение доверяет внешним данным.
Поэтому при анализе кода важно проверять не только использование pickle, но и происхождение данных.
Перед практикой убедитесь, что можете ответить на вопросы:
- Чем Pickle принципиально отличается от JSON?
- Почему десериализация может привести к RCE?
- Какие функции отвечают за сериализацию?
- Какие признаки указывают на использование Pickle?
- Почему Base64 не решает проблему?
- Когда безопасный механизм становится уязвимостью?
Если ответы есть, вы готовы к поиску уязвимости в учебном приложении. Скорее всего, искать придется не «магический эксплойт», а нарушение границ доверия — ключевую идею большинства задач на небезопасную десериализацию.
Практическое задание
Теория помогает понять принципы, но настоящие знания приходят с практикой.
Для этого подойдет задача «Элитный сомелье»* из бесплатного курса «Профессия Белый Хакер».
По сюжету ваш знакомый сомелье Володя запустил сайт о вине, но забыл о безопасности. Известно, что сайт использует Pickle для сериализации. Ваша задача — исследовать приложение, выполнить произвольные команды и получить флаг из корня файловой системы.
Эта задача позволяет на практике увидеть, как небезопасная десериализация превращается в уязвимость, определить нарушение границ доверия и применить полученные знания.
Разборы на вебинарах
ONE TASK включает не только самостоятельную практику, но и регулярные разборы. Участники бесплатного курса «Профессия Белый Хакер» могут разбирать задачи с экспертами.
Обычно студенты сначала пробуют решить задачу самостоятельно, а затем участвуют в вебинаре, где разбирают ход атаки: анализ условий, поиск зацепок, проверку гипотез.
Такой формат показывает не просто решение, а мышление специалиста.
Следите за анонсами вебинаров, регистрируйтесь на курс и используйте ONE TASK для роста в пентесте.
*Для доступа к заданию «Элитный сомелье» требуется регистрация на бесплатном курсе «Профессия Белый Хакер». После регистрации задание станет доступно в личном кабинете.
Источник: http://www.securitylab.ru/analytics/574694.php
