Overview
Table of Contents
Short Address Attack is a vulnerability where attackers exploit the characteristic of address encoding algorithms to ignore the trailing characters of the encoded string.
By constructing a specific encoded string that completely matches the prefix portion of a legitimate address, attackers deceive users into using the address controlled by the attacker when sending funds or performing operations.
Although Ethereum addresses typically consist of 20 bytes, if an attacker employs an address shorter than 20 bytes, Solidity automatically fills the difference on the right side with zeros to maintain the address’s expected length.
For instance, if a function anticipates two arguments, such as an address and a token amount, the contract may inadvertently consider a portion of the second argument (the token amount) as part of the first (the address) due to this automatic padding.
This unintended interpretation effectively enables the attacker to transmit a greater quantity of tokens than originally intended.
An Example of a Short Address Attack
Consider a Solidity function transfer(address _to, uint256 _value)
that is used to transfer tokens from the sender’s account to another account. Here’s a simplified version:
function transfer(address _to, uint256 _value) public {
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
}
This function works correctly in normal circumstances. However, an attacker could send a transaction from an Ethereum address only 19 bytes long instead of 20 bytes. If the transaction was sent in raw form, it might look like this:
0xa9059cbb000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000064
The first part (0xa9059cbb) is the function selector. The next part is the 19-byte address, followed by the 32-byte representation of the number of tokens to send (100 in this case).
The contract would read the 20-byte address by combining the 19-byte address with the first byte of the 32-byte number of tokens. This effectively causes the transfer of 256 times more tokens than intended because the first byte of the 32-byte gets multiplied by 256.
Remediation
Numerous strategies can be employed to mitigate Short Address Attacks:
- Input Validation: One of the simplest yet effective methods is to validate the address length within the contract. This can be accomplished by incorporating a
require()
statement to confirm that the address comprises exactly 20 bytes:function transfer(address _to, uint256 _value) public { require(_to.length == 20); require(balances[msg.sender] >= _value); balances[msg.sender] -= _value; balances[_to] += _value; emit Transfer(msg.sender, _to, _value); }
- Safe Math Libraries: Leveraging safe math libraries such as OpenZeppelin’s SafeMath can also fortify defenses against such attacks. These libraries offer functions that execute mathematical operations while implementing safety checks, triggering errors when overflows or underflows are detected. For instance:
using SafeMath for uint256; function transfer(address _to, uint256 _value) public { require(_to.length == 20); require(balances[msg.sender] >= _value); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); }
- Secure Programming Practices: Ensure that all function arguments have a predefined length. Avoid reliance on
msg.data.length
in your code, as it could be manipulated. - Off-Chain Verification: Wallets and user-facing applications can also implement checks to confirm that users transmit transactions to addresses with the correct length.
- Auditing and Testing: Regularly auditing smart contracts and conducting thorough testing can uncover security vulnerabilities. The use of automated auditing tools and manual code reviews can be instrumental in averting such attacks.