Skip to content

ERC721_transferFrom and ERC721_safeTransferFrom allow improper transfer of tokens #148

@smonicas

Description

@smonicas

Hi,

I work at Trail of Bits and I noticed a critical bug recently introduced in the ERC721 support: an incorrect handling of the zero address in ERC721_transferFrom / ERC721_safeTransferFrom allows anyone to steal the tokens.

func ERC721_transferFrom{
pedersen_ptr: HashBuiltin*,
syscall_ptr: felt*,
range_check_ptr
}(_from: felt, to: felt, token_id: Uint256):
let (caller) = get_caller_address()
let (is_approved) = _is_approved_or_owner(caller, token_id)
assert is_approved = 1
_transfer(_from, to, token_id)
return ()
end
func ERC721_safeTransferFrom{
pedersen_ptr: HashBuiltin*,
syscall_ptr: felt*,
range_check_ptr
}(
_from: felt,
to: felt,
token_id: Uint256,
data_len: felt,
data: felt*
):
let (caller) = get_caller_address()
let (is_approved) = _is_approved_or_owner(caller, token_id)
assert is_approved = 1
_safe_transfer(_from, to, token_id, data_len, data)
return ()
end

In cairo, calling a function directly (i.e, without going through an account contract) lead the message’s sender to be zero.

ERC721_getApproved returns the address approved:

func ERC721_getApproved{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr
}(token_id: Uint256) -> (approved: felt):
let (exists) = _exists(token_id)
assert exists = 1
let (approved) = ERC721_token_approvals.read(token_id)
return (approved)
end

However if no address was approved, this will return the zero address (as storage is initialized to 0).

As a result, anyone calling ERC721_transferFrom / ERC721_safeTransferFrom directly will be approved, and can steal any tokens.

A short-term fix is for ERC721_transferFrom and ERC721_safeTransferFrom to not accept the zero address as caller. However, this will prevent anyone from using these functionalities without going through a contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions