2018-01-16
We present a formal verification of HackerGold (HKG) ERC20 token contract.
The HackerGold (HKG) token is an ERC20 token written in Solidity, which became well-known when a serious security vulnerability was discovered that even survived a security audit performed by Zeppelin.
Specifically, the bug was due to a typographical error in the source code, using =+
instead of +=
for updating a receiver’s balance during a transfer, which would allow an attacker to reset an account balance. This security issue was resolved by deploying a new, fixed contract and reissuing the tokens.
We found that the HKG token implementation does not conform to the ERC20 standard, and deviates from the ERC20-K (and thus ERC20-EVM) specification as follows:
-
No
totalSupply
function: NototalSupply
function is provided in the HKG token, which is not compliant to the ERC20 standard. -
Returning
false
in failure: It returnsfalse
instead of throwing an exception in case of failure for bothtransfer
andtransferFrom
. It does not violate the standard, as throwing an exception is recommended but not mandatory according to the ERC20 standard:
"The function SHOULD throw if the from account balance does not have enough tokens to spend."
- Rejecting transfers of 0 value: It does not allow transferring 0 value (for both
transfer
andtransferFrom
), returningfalse
immediately without logging any event. It is not compliant to the standard, as the standard explicitly states that:
"transfers of 0 values MUST be treated as normal transfers and fire the Transfer event."
This is a potential security vulnerability for any API clients assuming the ERC20-compliant behavior.
- No overflow protection: It does not check arithmetic overflow, resulting in the receiver's balance wrapping around the 256-bit unsigned integer's maximum value in case of the overflow (for both
transfer
andtransferFrom
). It does not violate the standard, as the standard does not specify any requirement regarding it. However, it is questionable and potentially vulnerable, since it will result in loss of the funds in case of the overflow as the receiver's balance wraps around to a lower-than-expected value. We ask any API clients to be careful, having additional detection and protection mechanism for the arithmetic overflow, when using the HKG token.
The target contract of our formal verification has the following Solidity source code, taken from the Github repository https://github.com/ether-camp/virtual-accelerator at commit 42258200
:
We formally verified the full functional correctness of the following ERC20 functions:
totalSupply
: we proved the absence of the functionbalanceOf
allowance
approve
transfer
transferFrom
We took the source code, inlined the TokenInterface
contract, and compiled it to the EVM bytecode using Remix Solidity IDE (version soljson-v0.4.19+commit.c4cbbb05
).
The inlined source code of the contract is the following:
The EVM (runtime) bytecode generated by the Remix Solidity compiler is the following:
The Remix IDE-generated Gist link:
- StandardToken.inlined.gist: https://gist.github.com/anonymous/a57e87e7f735a656f166976641575fa1
The (annotated) EVM assembly disassembled from the bytecode is the following:
Due to its deviation from ERC20-K, we could not verify the HKG token against the original ERC20-EVM specification. In order to show that it is "correct" w.r.t. ERC20-K (thus ERC20-EVM) modulo the deviation, we modified the specification to capture the deviation and successfully verified it against the modified ERC20-EVM specification. Below are the changes made to the original ERC20-EVM specification:
-
To capture the absence of the
totalSupply
function, we wrote a new specification fortotalSupply
that throws an exception immediately when being called, as follows:[totalSupply] k: #execute => #halt callData: #abiCallData("totalSupply", .TypedArgs) localMem: .Map => _:Map gas: #gas({GASCAP}, 0, 0) => _ log: _ refund: _ storage: _:Map requires:
Note that the local memory could end up with some contents due to the function signature extraction process, for which some gas will be consumed. Other than that, nothing (
log
,refund
, andstorage
) will be updated. -
To capture the false return value, we changed the
k
andlocalMem
parameters of thetransfer-failure
section, from:k: #execute => #halt localMem: .Map => _:Map
to:
k: #execute => (RETURN RET_ADDR:Int 32 ~> _) localMem: .Map => .Map[ RET_ADDR := #buf(32, 0) ] _:Map
The modified parameter values specify that it returns
false
(denoted by 0) instead of throwing an exception. We changed thetransferFrom-failure
section similarly as the above. -
To capture the rejection of transferring 0 value, we added the following additional
requires
conditions, one for the success cases:andBool VALUE >Int 0
and its complement for the failure cases:
orBool VALUE <=Int 0
Note that the above complement, combined with the value range condition, implies
VALUE ==Int 0
. -
To capture the arithmetic overflow behavior, we changed the storage parameter values, from:
#hashedLocation({COMPILER}, {_BALANCES}, TO_ID) |-> (BAL_TO => BAL_TO +Int VALUE)
to:
#hashedLocation({COMPILER}, {_BALANCES}, TO_ID) |-> (BAL_TO => BAL_TO +Word VALUE)
Note the difference between
+Word
and+Int
. The+Word
operation wraps around2**256 − 1
, the maximum unsigned 256-bit integer value, while the+Int
operation is the mathematical integer addition (i.e., the arbitrary precision integer addition).Also, we dropped the
requires
conditions for the overflow, the one for the success cases:andBool BAL_TO +Int VALUE <Int (2 ^Int 256)
and its complement for the failure cases:
orBool BAL_TO +Int VALUE >=Int (2 ^Int 256)
The full changes made in ERC20-EVM are shown in here. The specifications of other functions except transfer
and transferFrom
are the same as the original ERC20-EVM.
We took the modified ERC20-EVM specification and instantiated it with the program-specific parameters shown below.
[pgm]
compiler: "Solidity"
_totalSupply: 0
_balances: 1
_allowances: 2
code: "0x60606040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063095ea7b31461007257806323b872dd146100cc57806370a0823114610145578063a9059cbb14610192578063dd62ed3e146101ec575b600080fd5b341561007d57600080fd5b6100b2600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610258565b604051808215151515815260200191505060405180910390f35b34156100d757600080fd5b61012b600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061034a565b604051808215151515815260200191505060405180910390f35b341561015057600080fd5b61017c600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506105c6565b6040518082815260200191505060405180910390f35b341561019d57600080fd5b6101d2600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061060f565b604051808215151515815260200191505060405180910390f35b34156101f757600080fd5b610242600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610778565b6040518082815260200191505060405180910390f35b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610417575081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b80156104235750600082115b156105ba5781600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190506105bf565b600090505b9392505050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156106605750600082115b1561076d5781600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610772565b600090505b92915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058206cb5284e8795f7d1c570318732bc1cb8add2222946156c0ba28c946531c4a2f50029"
gasCap: 100000
The resulting specification is the following:
The specification is written in eDSL, a domain-specific language for EVM specifications, which must be known in order to thoroughly understand our EVM-level specification. Refer to resources for background on our technology. The above file provides the eDSL specification template parameters. The full K reachability logic specification is automatically derived by instantiating a specification template with these template parameters.
Run the following command in the root directory of this repository to generate the full specification under the directory specs/hkg-erc20
:
$ make hkg-erc20
Run the EVM verifier to prove that the specification is satisfied by (the compiled EVM bytecode of) the target functions. See these instructions for more details of running the verifier.