Between the unexpected and the unknown. ¡Here Comes 2021!
When passing parameters to a smart contract, the parameters are encoded according to the ABI specification. It is possible to send encoded parameters that are shorter than the expected parameter length. For example, sending an address that is only 38 hex chars (19 bytes) instead of the standard 40 hex chars (20 bytes). In such a scenario, the EVM will pad 0’s to the end of the encoded parameters to make up the expected length.
This becomes an issue when third party applications do not validate inputs. The clearest example is an exchange which doesn’t verify the address of an ERC20 token when an user requests a withdrawal.
Consider, the standard ERC20 transfer function interface and noting the order of the parameters:
function transfer(address to, uint tokens) public returns (bool success);
Now consider an exchange, holding a large amount of a token. Let’s say TRX and an user wishes to withdraw their share of 100 tokens. The user would submit their address,
0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead and the number of tokens,
100. The exchange would encode these parameters in the order specified by the
transfer() function, i.e.
address then at last the
tokens. By which the encoded result would be
The first four bytes
(a9059cbb) are the
transfer() function signature/selector, the second 32 bytes are the address, followed by the final 32 bytes which represent the
uint256 number of tokens. Notice that the hex
56bc75e2d63100000 at the end corresponds to 100 tokens with 18 decimal places, as specified by the TRX token contract.
Alright, so now lets look at what happens if we were to send an address that was missing 1 byte (2 hex digits). Specifically, let’s say an attacker sends now this string
0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde as an address (which is deliberately missing the last two digits) and the same
100 tokens to withdraw. If the exchange actually doesn't validate this unexpectedly truncated input, it eventually would get encoded as
The difference is subtle. However, note that
00 has been padded to the end of the encoding, in an attempt to make up for the short
address that was sent. When this actually gets sent to the intended smart contract, there is no doubt that the addressparameters will be read as
0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde00 and the value will be read as
56bc75e2d6310000000 (notice the two extra
0's). This value is now,
25600 tokens (the value has been multiplied by
In this example, if the exchange held this many tokens, the user would withdraw
25600 tokens (whilst the exchange thinks the user is only withdrawing
100) to the modified address. Obviously, the attacker won't posses the modified address in this example. But if the attacker were to generate any address which ended in
0's (which can be easily brute forced) and used this generated address, they could easily steal tokens from the unsuspecting exchange just like that.