torch.load() unless weights_only parameter is set to True, uses pickle module implicitly, which is known to be insecure. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling.
Из этого неявно следует, что режим weights_only=True должен был secure. Тем не менее, задача на то, чтобы прочитать флаг, контролируя содержимое файла, которое подаётся в torch.load(..., weights_only=True).
sploent.py [URL] распечатает флаг на stdout, в предположении, что сервис запущен на URL (по умолчанию http://localhost:20022/). Цепочка примерно такая:
- Этот баг позволяет обойти проверку на валидность
tupleчуть ниже и пропихнуть вpersistent_load(), например,list. - Сервис запущен с отключенными ассертами, поэтому можно заехать сюда с контролируемым объектом.
- Чтобы получить из этого что-то полезное, нужно, чтобы контролируемый аргумент для
get_source_lines_and_file()был чем-то, для чего можно получить исходники. Для этого в сервисе в список разрешённых типов добавленtypes.ModuleType. - У объекта типа
types.ModuleTypeможно выставить__file__, и так получить чтение произвольного файла. В итоге этот файл преобразуется в дифф (для этого нужно на объекте проставитьdump_patches=Trueиcontainer.__name__), но поскольку содержимое второй стороны диффа мы контролируем, то это неважно. - Дальше надо как-то слить дифф с диска. В записи диффа на диск есть два досадных ограничения: 1) файл с диффом должен либо не существовать, либо быть пустым, 2) его имя должно заканчиваться на
.patch. Чтобы это нейтрализовать, сервис позволяет контролировать имя загружаемой вtorch.load()модельки, и дополнительно открывать на запись все файлы моделек сразу. - В таких условиях можно сначала первой моделькой сдампить дифф во вторую модельку (имя которой заканчивается на
.patch), а потом содержимым второй модельки частично его переехать так, чтобы флаг внутри диффа интерпретировался как часть пикл-стрима и был в итоге слит через текст исключения.