Attackathon _ Fuel Network 32924 - [Smart Contract - Insight] sways legacy storage namespacing is br
Submitted on Sun Jul 07 2024 15:22:13 GMT-0400 (Atlantic Standard Time) by @cyberthirst for Attackathon | Fuel Network
Report ID: #32924
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/FuelLabs/sway/tree/v0.61.2
Impacts:
Permanent freezing of funds
Description
Brief/Intro
Sway allows to namespace storage to avoid collisions. It does so by adding salt to the computation of the storage slot of the given storage variable. However, at least in certain cases, the salt is not used in the computation, and thus, the contracts can be vulnerable to storage collisions, especially when the proxy pattern is employed.
Vulnerability Details
In the compiler, the storage key is calculated as:
The storage_field_names
includes the respective namespaces and the fields. However, in the case of namespacing via:
the namespace name is not provided at all in the vec
. Therefore, the storage slot is computed without salt.
This is most likely a regression introduced in the recent PR, which added a new namespacing variant (see the link in References).
In the PoC, the proxy uses the foo
namespace to avoid storage collisions with important storage variables, and the implementation doesn't use any namespace. Yet, both end up having the same storage addresses.
If the alternative variant:
is used, the salts are propagated correctly.
Impact Details
The vulnerable namespacing is said to be deprecated by the compiler. However, it is still accessible without any flags, and it is also the only
namespacing variant documented in the Sway docs.
The PoC demonstrated that both Proxy and Implementation use the same storage addresses. As such, the implementation can unintentionally rewrite the address of the implementation contract and also the owner. As such, the proxy can become unusable because it might point to an invalid implementation contract (and it can't be updated because the owner was also rewritten). Therefore, the funds are blocked forever because the proxy doesn't have implementation.
But because this is a compiler bug, any user contract that relies on namespacing to avoid storage collision is potentially vulnerable.
References
Sway namespacing docs: https://docs.fuel.network/docs/sway/advanced/advanced_storage/#storage-namespace Namespacing PR: https://github.com/FuelLabs/sway/commit/e30497caaae37683011245d45ec4fc84b368f3ae#diff-bfae318752d54fae537a316de96038bab0ff0e1eebe44643d63ce9bcca784de7
Proof of concept
Proof of Concept
The whole project is accessible as .zip from the link at the bottom. Here, we describe the main points.
The main things to pay attention to are the storage declarations in proxy.sw and impl.sw.
In proxy we have
and in impl we have the same declarations but without the namespace:
Now, if the poc.sh
bash script is run, it loads the storage_slots.json
of both the contracts and compares their contents. If the contents are identical, it outputs COLLISION FOUND
. And this, indeed, is the case.
The script just builds and loads json files:
We attach the whole project through Google Drive. Once downloaded and unzipped, only run chmod +x poc.sh
and ./poc.sh
.
Google Drive link: https://drive.google.com/file/d/1lPRe5eQKxULZkzHBhNWcFo-_DrVdGkPv/view?usp=sharing
Last updated