The vulnerability allows bypassing the Twig sandbox's restrictions on calling the __toString() method. The analysis of the patches reveals two distinct vectors for this bypass.
-
Traversable objects in filters: The function Twig\Extension\SandboxExtension::ensureToStringAllowed is the central security check for string coercion. The patches (cc1e21a2a29, e9e818cbfc0) show that this function initially lacked logic to inspect the contents of Traversable objects. This oversight meant that if a Traversable object containing an object with a restricted __toString() method was passed to filters like join or replace, the sandbox would not prevent the implicit __toString() call that occurs when these filters process the iterable's elements. The vulnerable function is ensureToStringAllowed due to its incomplete validation.
-
in and not in operators: The in and not in operators are handled by the Twig\Node\Expression\Binary\InBinary and Twig\Node\Expression\Binary\NotInBinary classes, respectively. The patch 8d6af0707b7 modifies these classes to implement the CoercesChildrenToStringInterface. This interface signals to the SandboxNodeVisitor that the operands of these operators must be checked for safe string coercion. Before this change, the operands were passed directly to CoreExtension::inFilter, which uses PHP's native comparison, triggering __toString() without consulting the security policy. The compile methods of InBinary and NotInBinary are identified as vulnerable because they are responsible for generating the code that performs this unsafe operation.
The root cause is the failure to apply the __toString() security policy in all cases where PHP might implicitly perform string coercion. The identified functions are the specific locations in the codebase where these checks were missing. An attacker exploiting this would cause a call to Twig\Extension\SandboxExtension::ensureToStringAllowed which would fail to find the malicious object, and then a subsequent call within Twig\Extension\CoreExtension::join, Twig\Extension\CoreExtension::replace, or Twig\Extension\CoreExtension::inFilter would trigger the un-sandboxed __toString() call.