The root cause of the vulnerability is a prototype pollution issue in the Liquid class constructor. The filters and tags objects were initialized as plain JavaScript objects, inheriting from Object.prototype. This allowed an attacker to invoke methods from Object.prototype (like valueOf) as if they were registered filters in a Liquid template.
The exploitation starts when Liquid.parseAndRender is called with a malicious template. A filter like | valueOf triggers the vulnerability. Object.prototype.valueOf returns this, which in the filter's context is an internal object containing references to the entire Liquid engine instance, including the parser, file system access, and rendering context. This information leak is the first step of the exploit.
With access to these internal objects, the attacker can then use other gadgets within the LiquidJS library to escalate their privileges. The proof-of-concept demonstrates chaining this leak with another gadget involving Parser._parseFile. The attacker overwrites a comparison operator to point to _parseFile and then triggers a comparison. This allows them to call _parseFile with controlled arguments, and by also overwriting loader.lookup and readFile methods, they can trick the parser into executing arbitrary code, resulting in Remote Code Execution.