CRC1 API: An Attack Vector on Approve/TransferFrom Methods

CRC1 API: An Attack Vector on Approve/TransferFrom Methods

0. Abstract

In this article we describe a possible attack vector on standard CRC1 Core Token API. This is attack on API itself, not on any particular implementations, so all conformant implementations are potentially vulnerable. The method uses methods approve and transferFrom defined by CRC1. We also give some thoughts about how described attack could be prevented or at least mitigated using current version of CRC1 API. We also suggest changes to CRC1 API that would make the described attack impossible.

1. Introduction

CRC1, defines a standard API for Core Tokens smart contracts. Tokens are defined by Core Foundation as the following:

Tokens in the Core ecosystem can represent any fungible tradable good: coins, loyalty points, gold certificates, IOUs, in game items, etc. Since all tokens implement some basic features in a standard way, this also means that your token will be instantly compatible with the Core wallet and any other client or contract that uses the same standards.

So CRC1 is supposed to be the standard way to implement basic features of all tokens to make them compatible with common Core software such as Core Wallet.

2. Approve/TransferFrom Methods

Among other things, CRC1 defines the following two methods to be implemented by every Core Token smart contract:

function transferFrom(address _from, address _to, uint256 _value)
returns (bool success)

Send _value amount of tokens from address _from to address _to

The transferFrom method is used for a withdraw workflow, allowing contracts to send tokens on your behalf, for example to “deposit” to a contract address and/or to charge fees in sub-currencies; the command should fail unless the _from account has deliberately authorized the sender of the message via some mechanism; we propose these standardized APIs for approval:

function approve(address _spender, uint256 _value)
returns (bool success)

Allow _spender to withdraw from your account, multiple times, up to the _value amount. If this function is called again it overwrites the current allowance with _value.

Apart from updating the allowance, the CRC-1 proposal does not specify the intended semantics of multiple calls to approve.

In the following sections we will show how these two methods, as defined in CRC1, could be used in an attack that allows a spender to transfer more tokens than the owner of the tokens ever wanted to allow the spender to transfer.

3. Attack Scenario

Here is possible attack scenario:

  1. Alice allows Bob to transfer N of Alice’s tokens (N>0) by calling approve method on Token smart contract passing Bob’s address and N as method arguments
  2. After some time, Alice decides to change from N to M (M>0) the number of Alice’s tokens Bob is allowed to transfer, so she calls approve method again, this time passing Bob’s address and M as method arguments
  3. Bob notices Alice’s second transaction before it was mined and quickly sends another transaction that calls transferFrom method to transfer N Alice’s tokens somewhere
  4. If Bob’s transaction will be executed before Alice’s transaction, then Bob will successfully transfer N Alice’s tokens and will gain an ability to transfer another M tokens
  5. Before Alice noticed that something went wrong, Bob calls transferFrom method again, this time to transfer M Alice’s tokens.

So, Alice’s attempt to change Bob’s allowance from N to M (N>0 and M>0) made it possible for Bob to transfer N+M of Alice’s tokens, while Alice never wanted to allow so many of her tokens to be transferred by Bob.

4. Attack Analysis

The attack described above is possible because approve method overrides current allowance regardless of whether spender already used it or not, so there is no way to increase or decrease allowance by certain value atomically, unless token owner is a smart contract, not an account.

5. Workaround

Because the described attack allows an attacker to transfer at most N + M tokens when allowance is being changed from N to M, then changing allowance from N to 0 and then from 0 to M seems quite safe. Token owner just needs to make sure that first transaction actually changed allowance from N to 0, i.e. that the spender didn’t manage to transfer some of N allowed tokens before first transaction was mined. Unfortunately, such checking does not seem to be possible via standard Web3 API, because to do the check one needs to be able to analyze changes in the storage of smart contract made by particular transactions, including internal transactions. Though, such checking is still possible using advanced blockchain explorers.

Another way to mitigate the threat is to approve token transfers only to smart contracts with verified source code that does not contain logic for performing attacks like described above, and to accounts owned by the people you may trust.

6. Suggested CRC1 API Changes

This section suggests changes to CRC1 API that are supposed to make the attack described above impossible.

6.1. Atomic “Compare And Set” Approve Method

We suggest the following method to be added to CRC1 API:

function approve(
address _spender,
uint256 _currentValue,
uint256 _value)
returns (bool success)

If current allowance for _spender is equal to_currentValue, then overwrite it with _value and return true, otherwise return false.

This change alone is enough to address the attack vector described above. Suggestions given below are not required, but are supposed to make usage of approve and transferFrom methods more convenient and less error-prone.

6.2. Separate Log Message for “TransferFrom” Transfers

We suggest the following event to be added to CRC1 API:

event Transfer(
address indexed _spender,
address indexed _from,
address indexed _to,
uint256 _value)

Triggered when tokens are transferred via transferFrom method.

Note, that for backward compatibility reasons, token contracts will probably have to log both, old three-args and new four-args Transfer events in transferFrom method.

6.3. Four-Args Approval Event

We suggest the following event to be added to CRC1 API:

event Approval(
address indexed _owner,
address indexed _spender,
uint256 _oldValue,
uint256 _value)

Triggered whenever either approve method is called.

Note, that for backward compatibility reasons, token contract will probably have to log both, old three-args and new four-args Approval events in approve method.

7. Authors