SlowMist: A Detailed Explanation of Ethereum Short Address Attacks (Released in 2018)
Overview
When it comes to smart contract vulnerabilities, the first things that might come to mind are algorithm overflows, call() function abuse, and fake deposit vulnerabilities. After all, these are common issues found in many smart contracts and have high levels of risk. However, there is a vulnerability that many people might have seen or heard about but not many are concerned with. It is like the Atlantis of the human world — many people know about it, but there are few research reports on it. What can be found on Google are mostly explanations of the principle but no practical reproductions, giving it a somewhat armchair strategist vibe. It is widely known that this is a flaw at the EVM (Ethereum Virtual Machine) level, not at the smart contract level, and in our default thinking, it is a vulnerability that has already been fixed. Recently, I tried to find the official solution to the short address attack but couldn’t find any relevant information through my searches. I also went through Github and couldn’t see any historical releases with relevant fixes. So, I guess, and it’s really just a guess, that the EVM level might not have been fixed.
Basic Knowledge of Short Address Attacks
Short address attacks actually involve a function found in every ERC20 contract, as shown below:
When invoking this function on the contract, from the perspective of the EVM layer, a series of bytecode is passed in, as shown in the following image. You can view the entire function invocation data in the input data section of each transaction on Etherscan.
The bytecode passed to the ERC20 contract’s transfer function contains 136 bytes (normally), including the function signature (first 4 bytes) and the input parameter values (data portion, including the address and amount data passed in, both of which are 32 bytes and zero-padded from the high bit). The above figure only shows 134 bytes, which has been specially processed by the author, as will be explained in detail below.
From the information above, we can see that EVM recognizes the input parameters based on the bytecode, and does not perform any verification. This creates a vulnerability where attackers can intentionally use invalid addresses, such as addresses with a few digits missing at the end, to see how EVM will handle them. To the attacker’s delight, EVM not only does not report an error, but also helpfully completes the address by padding the missing digits with the amount data portion’s length. For example, if your address was:
As a result, due to your cunning plan, intentionally leaving out two zeros, it becomes like this:
In this case, the “thoughtful” EVM will take two digits from the amount data part in the bytecode to complete the address, and since the first two digits of the amount data happen to be 0, your address will remain the same, but the data part will be two bytes shorter. However, this violates the byte length of the abi. But don’t worry, the “thoughtful” EVM will fill the amount data part with 0 from the end until it reaches the normal 136 bytes. Now, you may ask, what if my address has six zeros and I intentionally write six less zeros in the address, will the data part increase by six zeros? Wouldn’t that make me rich?
The answer is: you are right.
This means that the originally filled data was 1, and after being converted into bytecode, it is 0x1. If the address is missing 6 bytes, then your data will automatically become 0x1000000! Surprised or not!
First attempt
My first attempt to reproduce the exploit was using Remix, but as expected it failed, as shown in the screenshot below. Why did it fail? Because this is a vulnerability that was discovered back in 2017 and there have been numerous patches since then. It’s naive to think that it would be so easy to succeed, as the front-end will definitely filter and check your input address.
But as a troublemaker, how could one give up easily? Since no official fix was found, there must still be a problem at the lowest level. Therefore, I had to start operating from the very bottom, and I thought of using web3 to reproduce the vulnerability.
Environment preparation
1. Operating system: macOS
2. Node: v8.11.0
3. Web3: 1.0.0-beta.36
4. rpc: Infura
5. abi of the contract for testing
6. Sample contract:
7. Deploy the example contract on the testnet using remix and obtain the contract address, which will be used later.
Method to obtain abi:
Second Attempt
Note: (All the following operations are performed in the command line)
1. Type “node” to enter the command line prompt.
2. Import web3 into the project and set the provider. (We will not go into details on how to download web3 here, but it is a simple operation that you can look up yourself.)
3. Creating contract instance:
4. Constructing method abi means constructing the input data of the transaction we mentioned above. Since we want to carry out a short address attack, our address should have fewer digits than a normal address, so that the EVM can automatically fill in the missing address digits with zeros. However, a normal construction attempt will fail, as shown in the following figure.
However, as a troublemaker, we cannot give up so easily! Therefore, we need a special method, which consists of two steps.
Step one: first construct a normal abi with a valid address, such as ‘0xdfca6234eb09125632f8f3c71bf8733073b7cd00’
As shown in the image, at this point, the abi, also the input data, is 136 bytes.
Step two: use a little trick to secretly remove the last two zeros from the address in the abi. Originally, the ABI was 136 bytes, but now it’s only 134 bytes, which is the abnormal input data I mentioned earlier. This is constructed at this point.
The abi with the zeros removed is shown above.
OK, after everything is prepared, we can get to the most exciting moment.
5. Import ethereumjs-tx into the project (we will not elaborate on how to download it here).
6. Import your private key and do some processing.
7. Construct the raw transaction data, which is a very raw Ethereum transaction data. Each contract call actually constructs the following data. We won’t go into detail about this, but except for nonce, everything else is what we encounter when we call the contract on remix. Regarding nonce, it’s the number of transactions sent by the account. For example, if your account has processed 5 transactions, then the nonce is 4 (nonce starts from 0). You can see the last transaction nonce of your account on Etherscan. Below is the specific transaction data:
8. Sign and process the transaction.
9. Send the transaction.
The miracle is happening again
We can see from the event we set in advance that the EVM successfully adds zeros. The input data was originally 123, but the result extracted by the EVM is 31488. The original hexadecimal 123 is 0x7b, but now it is 0x7b00! Exciting!
Epilogue
I have deployed a contract on the test network and verified that it can be queried on the chain. Seeing is believing.
https://kovan.etherscan.io/address/0x6e2f32497a7da13907831544093d3aa335ecbf33#code
This operation reminds me of a quote from “One Flew Over the Cuckoo’s Nest” — “But I tried, didn’t I? Goddamn it, at least I did that.”
References
Web3js 1.0 API method (Chinese manual)
http://cw.hubwiz.com/card/c/web3.js-1.0/1/2/21/
A detailed description of short address attacks
https://zhuanlan.zhihu.com/p/34470071
Details on ABI can be found here
https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html
About SlowMist
SlowMist is a blockchain security firm established in January 2018. The firm was started by a team with over ten years of network security experience to become a global force. Our goal is to make the blockchain ecosystem as secure as possible for everyone. We are now a renowned international blockchain security firm that has worked on various well-known projects such as Huobi, OKX, Binance, imToken, Crypto.com, Amber Group, Klaytn, EOS, 1inch, PancakeSwap, TUSD, Alpaca Finance, MultiChain, O3Swap, etc.
Website:
https://www.slowmist.com
Twitter:
https://twitter.com/SlowMist_Team
Github:
https://github.com/slowmist/