-
Notifications
You must be signed in to change notification settings - Fork 740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistencies of XCM NonFungible adapters. And an easy-to-introduce vulnerability #4073
Comments
Hi @franciscoaguirre! Could you look into this please? I'd propose an ad-hoc fix. We can check the NFT's owner via the As for the custom things, I believe new abstract NFT traits are needed. Decoupled from the metadata things and more granular. This will make them more general (currently, they repeat the pallet-uniques and pallet-nfts interface). |
@paritytech/frame-coders PTAL |
cc @muharem |
I'm looking into this |
@mrshiposha Nice catch! Seems weird that the nonfungible trait doesn't take in the source location in the |
There's also the nonfungible v2 traits, which I don't know if they improve the situation in any way |
They have the same flaw as v1. Also, v2 traits repeat the interface of I'd be happy to describe all the concerns and an alternative set of general NFT / Asset Metadata traits in a draft PR. I could add such a trait set (not replacing the existing traits so that I won't break anything existing). Would you be interested in this? Note: I view these traits not only as an interface for use in XCM but also as an interface to use in pallet configs. NFTs (or their classes) may represent functional objects (like CoreTime Region), and these objects can be utilized by pallets in a decoupled way, so having a good granular interface is important for such things as well |
I think a truly granular set of traits for NFTs would be greatly appreciated. I'll review the draft PR when it's open. |
Description of bug
1. Different behavior of
T
and(T,)
Both the NonFungiblesAdapter and the NonFungibleAdapter implement the
transfer_asset
function of theTransactAsset
trait. This leads to the inconsistency between the implementations ofTransactAsset
forT
and for a tuple(T,)
.Why this happens:
internal_transfer_asset
function, so the default implementation is used that returns theErr(XcmError::Unimplemented)
. The non-fungible adapters implement thetransfer_asset
directly instead. Thetransfer_asset
function has a non-trivial default implementation which will attempt to call theinternal_transfer_asset
first, and if failed, will resort to the combination of thewithdraw_asset
+deposit_asset
.In NonFungible adapters, this default logic is substituted. So, if we call the
transfer_asset
directly on an adapter type, the adapter's implementation will be used.transfer_asset
untouched. So, if we call thetransfer_asset
on a tuple of an adapter type (e.g.,(T,)
), thewithdraw_asset
+deposit_asset
combination will be used (because the adapter'sinternal_transfer_asset
will return an error).The actual implementation of the transfer by the adapter is completely ignored.
Different behavior of
T
and(T,)
is bad by itself, but also the NonFungible adapters'TransactAsset
implementations contradict the Fungible adapters that implement the internal_transfer_assetWARNING: Implementing
internal_transfer_asset
instead of thetransfer_asset
in the NFT adapters is NOT enough, as it will strengthen the adapters' vulnerability described below. Moreover, fixing this will INTRODUCE the vulnerability to AssetHub.2. Both NFT adapters don't check the
from
account in thetransfer_asset
In the implementation of the
transfer_asset
, neither of the adapters checks thefrom
account. They transfer the asset to theto
account without any permission checks.If the
transfer_asset
of the NFT adapters is used unwary in a chain's runtime, an NFT could be stolen.This vulnerability is relatively easy to introduce (see the reasons below).
3. The conjunction of the above issues
Suppose a chain has the following setting in its XCM config:
type AssetTransactors = NonFungiblesAdapter<...>;
. Notice that theAssetTransactors
is NOT a tuple but just a NonFungiblesAdapter.Then, suppose the pallet-xcm's config allows a user to execute an arbitrary XCM message (as the AssetHub does). In that case, a user might execute a simple program containing just the
TransferAsset
XCM instruction (via thepallet-xcm.execute
extrinsic).This will trigger the adapter's
transfer_asset
implementation, which doesn't include the ownership checks.IF a NonFungible adapter is used directly, not in a tuple, it produces the described vulnerability.
Fortunately, at the moment, the NonFungible adapters are never used alone, as in this example, so the AssetHubs of Westend and Rococo aren't vulnerable to this (checked locally for Westend; see
steps to reproduce
for the details).Polkadot's and Kusama's AssetHub don't include NonFungible adapters.
Steps to reproduce
Exposing NonFungible adapters vulnerability
AssetTransactors
in Westend to the following:polkadot-parachain-bin
packageuniques.create
by Alice. Set the collection's admin to Alice, too. SetCOLLECTION_ID = 42
.uniques.mint
. Make Alice the owner of this NFT. UseNFT_ID = 256
.uniques.asset(42, 256)
polkadotXcm.execute
Submit it using Bob's account. After it executes, recheck the storage:
uniques.asset(42, 256)
. The owner will be the following account:5EZfMTyug6vyWCAcSBdpBVA8zkErY2veBZyXg7yzG97unHZw
.Bob doesn't have any right to transfer Alice's NFT elsewhere. However, it can do this if the chain uses the adapter as in the first step.
Proving that the actual Westend AssetHub isn't vulnerable
AssetTransactors
made aboveinternal_transfer_asset
fallbacks towithdraw_asset
+deposit_asset
, and Bob can't withdraw the NFT owned by Alice.Search for the following message in the logs:
xcm::TransactAsset::internal_transfer_asset: [Parachain] did not transfer asset
and examine the surroundings.The text was updated successfully, but these errors were encountered: