CVE-2021-23727:celery: Stored OS command / code injection via untrusted backend res
A reconstruction of the flaw, and the evidence path Lachesis follows to make it visible.
Tampered task-failure metadata triggers unsafe exception reconstruction when the result is read.
That is the shape of the failure. The rest of this case file follows the evidence behind it.
What was known.
What Lachesis established.
Known before Lachesis ran
The CVE and vulnerable release were selected from public history. We are not claiming novel discovery.
- CVE
- CVE-2021-23727
- Vulnerable target
- v5.2.1 (affected: all versions < 5.2.2) · vulnerable
- Prior knowledge
- CVE-2021-23727 · v5.2.1 (affected: all versions < 5.2.2) is vulnerable
Independent re-detection
The experiment graph was built around the celery package at the vulnerable release v5.2.1, not pointed at the CVE. Blind enumeration ran over the whole candidate registry (31 constructors, every family) with the pass-3 guard-differential ranker ordering leads. The expected injection.exec family surfaced on its own (19 injection.exec.escaping obligations) alongside lifecycle, navigation, filesystem and deserialize families. Everything under sast_output is the enumerator's own emission (candidate capsule plus sources_of value-flow cone); everything under adjudication and trace narrative is my reading of the verbatim source and the official fix and is labelled as such. Honesty note carried through: the family fired but the highest-ranked sink is os.execv in the worker self-reload path, which is not the CVE mechanism -- see what_the_tool_did_NOT_prove.
- Seeded inputs
- None
- Run timestamp
- 2026-09-02T13:27:48Z
- Evidence artifact
- celery_v5.2.1.kuzu
The commands and outputs below come from this recorded Lachesis run.
What Lachesis reconstructed.
One historical repository. Four captured queries. A complete source-to-sink argument.
Lachesis follows the same evidence path through unfamiliar repositories.
Here is how the path becomes visible.
These are the recorded questions Lachesis asked of the historical vulnerable code, followed by the raw result and source location each query returned.
Captured runReal replay of Lachesis over the graph built for the celery v5.2.1 case. Every command was run against that graph and every result is the captured output. The tool was not pointed at a file or a family; the hunt enumerated the whole taxonomy first and the injection.exec family came out of that. No message or backend payload was seeded, so the mode is independent-redetection. The reconstruction faithfully records that the top exec sink is the worker self-reload os.execv, not the CVE's exception-reconstruction path.
Load the graph and list every bug family
Load the graph and list every family with nothing chosen ahead of time. All 8 domains and 31 constructors are reported. injection.exec is one row among many, not a target.
loaded graph (celery v5.2.1, 103075 nodes / 309704 edges); taxonomy: 8 domains, 31 constructors; census complete for observable graph; 776 candidates enumerated across families.A small line with a large consequence.
the pre-5.2.2 path trusts backend content and reconstructs whatever class the metadata names, so a poisoned backend yields code execution on the reader.
exception class from backend metadata imported and instantiated with no trust/type check
// validate that only genuine Exception subclasses are reconstructed and reject untrusted class references (fixed in 5.2.2, commit 1f7ad7e6).
The original record.
Read the historical advisory and vulnerability record behind this reconstruction.