In February 2022, Superfluid, an Ethereum-based money streaming protocol, was the victim of an attack.  The attacker took advantage of vulnerabilities in how Superfluid tracked context to steal $13 million in cryptocurrency.

Inside the Attack

Superfluid “agreements” are the rules that Super Tokens operate under.  A Super App can support multiple composable agreements within a single transaction.  For this to work, Superfluid is designed to track the current context across different agreements.

The Superfluid attacker took advantage of a serialization vulnerability in the Superfluid smart contract that allowed them to create a specially forged context.

The callAgreement function in Superfluid is designed to have a placeholder argument, ctx, that will store this context information.  The value of ctx should be zero, and the callAgreement function will create it and store a hash of it in a state variable.  This way, the value of ctx can be easily retrieved using the argument variable and its integrity can be validated by checking it against the hash.

Instead of leaving ctx as a placeholder, the attacker injected a malicious value of ctx into its call to callAgreement.  When the calldata is packaged to be sent to an agreement, it contains two versions of ctx; the real and fake ones.  When an agreement deserialized the values, it kept the first version of ctx (the malicious one) and discarded the other.

While the agreement had the ability to verify the legitimacy of the context using the stored hash value, it did not do so for the trusted Host contract.  As a result, the fake version of ctx was accepted even though it did not match this hash.

The attacker’s fake ctx was designed to have a source address belonging to another party.  When the agreement was executed, it was believed that the transaction was a legitimate transfer from that spoofed address to the attacker’s account.  This allowed the attacker to drain tokens from other accounts via Superfluid.

Lessons Learned From the Attack

The Superfluid smart contract had the ability to detect injected ctx values using the Superfluid.isCtxValid function, which checks against the stored hash value.  However, this check was not performed on calldata provided by a trusted Host contract.  As a result, the attacker was able to inject a fake context containing spoofed source addresses into the call data.

Zero trust security principles emphasize the importance of “trust but verify” for security, assuming that anyone is a potential threat.  If this principle was applied here, the malicious context would have been identified and discarded by the exploited agreements, preventing the $13 million theft.

Rob Behnke
02.14.2022