Overview
Table of Contents
On December 7, a substantial exploit occurred on the TIME token. TIME token serves as the inherent token of Chrono.tech, a decentralized finance (DeFi) platform functioning on the Ethereum blockchain.
This incident resulted in a loss of ~89.5 ETH, out of which the attacker gained 84.6 ETH ($188K). The attack revolved around the susceptible Forwarder contract, enabling the attacker to consume a significant portion of TIME tokens through the execution of a transaction from an arbitrary sender address.
About Chronotech (TIME)
Chrono.tech, founded in 2016, offers blockchain-driven solutions for recruitment, HR, and payment procedures, diminishing obstacles to entry for employment and ensuring secure fund transfers within the worldwide labor market. The project’s market cap is $14,962,202.
Chrono.tech includes various services, accompanied by its native governance token TIME, all deployed on diverse blockchains. These offerings include LaborX.com, a peer-to-peer freelancing marketplace; TimeX.io, a crypto exchange leveraging Plasma technology; AUDT, a stablecoin pegged to the Australian dollar; and PaymentX, a cryptocurrency invoicing and payroll solution.
The TIME token serves as the intrinsic token within the Chrono.tech ecosystem and finds utility across its products. It can be employed, for example, to obtain premium account status and for staking on TimeWarp.finance, leading to the reception of revenues generated by all services.
In November 2020, the initial TIME token underwent an upgrade to the new ERC677 standard, facilitating integration with other DeFi protocols, reducing transaction costs, and offering additional benefits, all while maintaining full compatibility with the ERC20 standard.
Root Cause of the Exploit
This attack was a vulnerability at the framework level, impacting the TIME project and proving to be a threat to any other project that utilizes it.
A mistake in the integration of standard ERC-2771 with Multicall, coupled with variations in how calldata is handled between them, led to this specific attack incident.
Exploit Transaction: https://etherscan.io/tx/0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6
Attacker’s Address: 0xfde0d1575ed8e06fbf36256bcdfa1f359281455a
Attack Contract: 0x6980a47bee930a4584b09ee79ebe46484fbdbdd0
Attacked Contract: 0x4b0e9a7da8bab813efae92a6651019b8bd6c0a29
Some other attacks that have leveraged this vulnerability are mentioned below:
- https://polygonscan.com/tx/0x1b0e27f10542996ab2046bc5fb47297bcb1915df5ca79d7f81ccacc83e5fe5e4
- https://etherscan.io/tx/0x6bf60f1667c20f705fed4617ebe4aa7e915c05b3fcc050f5cc676f7f01a18b28
Detailed Technical Analysis
Following is the course of action that took place during the hack:
1. The attacker first changed 5 ETH to 5 WETH.
2. Then the attacker swapped 5 WETH to 3,455,399,346 TIME on dex 0x760d…af84.
3. The attacker called the “execute()” function in the Forwarder contract:
Let us break down this process:
- Construction of ‘req’ (calldata): The attacker initiated the process by creating a specific data set called ‘req’ (calldata) that met certain requirements. Alongside this, the attacker provided the corresponding signature.
- Verification using “verify()”: The “verify()” function played a crucial role. It utilized the provided signature to confirm whether the entire ‘req’ was signed by the specified address, req.from, as declared within the ‘req.’
- Arbitrary use of a controlled address: The attacker chose the address 0xa16a5f37774309710711a8b4e83b068306b21724, which was under their control and had a guaranteed eligible nonce. The attacker signed the ‘req’ data with the private key of this address, successfully passing the verification process.
- Execution using “execute()” and “call()”: The “execute()” function then called the “call()” function, packaging ‘req.data’ and ‘req.from’ as parameters for the “call()” function. The ‘req.to’ was specified as 0x4b0e9a7da8bab813efae92a6651019b8bd6c0a29 (TIME contract), and the selector of the multicall(bytes[]) function was 0xac9650d8.
- Delegate invocation to TIME contract: The “call()” function was tantamount to invoking the “multicall(bytes[])” function of the TIME contract. The multicall function directly passed the calldata parameter to delegatecall().
- Parsing problem with calldata: An issue arose during the parsing process. The initial element of the bytes array (data[0]) had a length of only 0x38, signifying the absence of the req.from value (0000000000000000a16a5f37774309710711a8b4e83b068306b21724). The “execute()” function in the Forward contract added this value following req.data.
- Misinterpretation of parameter passing: The developer aimed to include the verified req.from value in the calldata sent to the TIME contract for permission control. However, there was a misunderstanding in the parsing logic. The developer mistakenly believed that merely combining req.data and req.from through splicing was sufficient to include req.from in the calldata sent to the TIME contract.
- Truncation by “multicall()”: The “multicall()” function, however, directly truncated req.from because it did not adhere to the calldata parsing logic. This led to a significant error since the verified req.from value was not transmitted to the TIME contract as anticipated.
- Incorrect execution by TIME contract: The TIME contract mistakenly received an address controlled by the attacker, which had not been verified. As a result, the TIME contract executed the burn logic on the incorrect target due to this unexpected and erroneous address.
4. The burn() function within the TIME contract when invoked, the _msgSender function plays a crucial role in determining whether the entity initiating the call (msg.sender) is a Forwarder contract.
If the msg.sender is identified as a Forwarder contract, a specific action takes place. The function extracts the last 20 bytes of the calldata (the data provided when calling the function), and these 20 bytes are then returned as the first parameter for the _burn() function.
Based on the earlier examination, the address effectively sent to the TIME contract is 760dc1e043d99394a10605b2fa08f123d60faf84 (comprising the last 20 bytes of calldata). This particular address falls under the control of the attacker and serves as the target pool address for the intended attack. The TIME contract executed the burning of 62 billion TIME tokens on 0x760d…af84.
Subsequently, only 9,999,999 Time tokens remained in the pool, resulting in an exceptionally high destruction ratio of 99.9%. The attacker invoked the synchronization function of the pool to align reserves, intending to manipulate the price. Concurrently, they exchanged 3,455,399,346 TIME for 94.5 WETH within the pool.
Consequently, the TIME contract mistakenly consumed a substantial quantity of tokens from the attacker-controlled target pool instead of the intended address.
Stolen Fund Details
The TIME token hack incident resulted in the theft of about 89.5 ETH, estimated to a value of approximately $188,000.
Subsequently, these tokens were traded for a considerable quantity of WETH, eventually being converted back to ETH, with a portion allocated for a bribe Flashbots during the transaction.
Hack Aftermath
The attacker incinerated more than 62 billion TIME tokens, causing a significant depletion in the token pool. The TVL, at the time of writing, stands at $210,184.
Mitigation Steps
Addressing such an attack requires the implementation of security measures to both prevent and identify such vulnerabilities. Here are a few mitigation steps:
Enhance Calldata Validation
- Incorporate comprehensive validation checks for calldata to verify that only valid and anticipated data is accepted.
- Include robust parsing mechanisms that accurately interpret and process calldata parameters, avoiding vulnerabilities stemming from misinterpretations.
Implement Strict Permission Controls
- Enforce strict permission controls on critical functions, such as the burn() function, ensuring that only authorized addresses or contracts can execute them.
- Utilize access control mechanisms to verify the legitimacy of the caller and prevent unauthorized access to sensitive functions.
Audit and Testing
- Conduct comprehensive security audits of smart contracts, especially those handling critical operations like token burning and fund transfers.
- Implement extensive testing, including both unit testing and scenario testing, to identify and address potential vulnerabilities before deploying contracts to the mainnet.
By incorporating these mitigation steps, developers can fortify smart contracts against the described attack process, enhancing overall security and minimizing the risk of exploitation.
Conclusion
The incident involving the TIME token underscores the inherent vulnerabilities in smart contracts, demonstrating how even a minor mistake can result in significant financial consequences.
The primary reason for the exploit was the alteration of the Forwarder contract, which was designed to execute transactions from any given address. Projects should be careful about the frameworks employed in a contract and thoroughly analyze all such external contracts.
Moving forward, emphasis should be placed on strengthening security protocols and educating the blockchain community to avert comparable incidents.