Dexible Finance is a decentralized finance (DeFi) aggregator. It allows users to trade tokens seamlessly across multiple DEXes in a single interface, optimizing for the best rates and lowest fees. However, on February 17, 2023, a security vulnerability was exploited in the platform's recently introduced v2 smart contracts, resulting in the loss of approximately $2 million worth of tokens. In this hack analysis, we will discuss the nature of the vulnerability, the attackers' exploitation method, and the overall impact of the hack on the platform and its users.
The Dexible Finance hack had significant consequences for the platform and its users. The attackers managed to exploit a vulnerability in the selfSwap function of the platform's v2 smart contracts, which allowed them to siphon tokens from users who had granted permission to Dexible to manage their tokens. The hack affected a total of 17 user accounts, with the majority of losses coming from a single address belonging to BlockTower Capital, a prominent investment firm.
In total, approximately $1.5 million was stolen on the Ethereum network and an additional $450k on other platforms (Arbitrum and BSC). The stolen funds were sent to Tornado Cash, a privacy-focused mixer service, to obfuscate the attackers' tracks and make it difficult to trace the origin of the stolen funds.
Vulnerable Functions Overview
The selfSwap function
The selfSwap function is a part of Dexible's smart contract and is responsible for enabling users to perform token swaps without the involvement of an affiliate or automatic discounts. The function takes a SwapTypes.SelfSwap request as an input, which contains information about the tokens to be swapped, the fees, and the routing details.
It creates a SwapTypes.SwapRequest object with the provided data and initializes a SwapMeta object that stores various metadata about the swap, including fee details, gas amounts, and balances.Once the SwapMeta object is created, the selfSwap function calls the fill function, passing the SwapRequest and SwapMeta objects as arguments. After the fill function returns the updated SwapMeta object, the selfSwap function proceeds with the postFill method to finalize the token swap.
The fill function
The fill function is called by the selfSwap function and is responsible for executing the token swap based on the routing information provided in the SwapRequest object. It first performs a preCheck to ensure that the request and metadata are valid. Then, it iterates through the specified routes and approves each route amount for the respective spender. The function then calls the router specified in each route using the routerData provided.
If the router call is unsuccessful, the function reverts with an error message. Otherwise, the fill function calculates the output amount of the token swap and checks if it is sufficient compared to the requested amount. If the output is insufficient, the function reverts with an error message. Finally, the fill function returns the updated SwapMeta object to the selfSwap function.
Security Flaw in the Functions
The vulnerability in the selfSwap and fill functions lies in the lack of validation and verification for the router address (routerID) provided in the SwapRequest object. This omission allows an attacker to pass their own contract address as the router, which is not verified on-chain. As a result, the fill function performs a delegate call to the attacker's provided routerData, allowing the attacker to transfer the victim's approved tokens directly to their account.
The absence of routerID validation and an on-chain verification mechanism in these two functions leaves the smart contract susceptible to manipulation by malicious actors, leading to the loss of tokens for the affected users.
Attacker's Playbook: Exploiting the Vulnerability
Step 1: Crafting the malicious contract
The attacker creates their own malicious contract that mimics a legitimate DEX. This contract contains a "transferFrom" function, which can transfer tokens from an approved account to the attacker's address.
Step 2: Preparing the selfSwap request
The attacker prepares a SwapTypes.SelfSwap request containing the details of the tokens they want to swap. They provide the address of their malicious contract as the router in the routing information, bypassing any on-chain verification.
Analogy: It's like a thief providing false bank details to a victim, tricking them into sending money to the thief's account instead of a legitimate bank.
Step 3: Calling the selfSwap function
The attacker calls the selfSwap function on the Dexible contract, passing their crafted selfSwap request. The Dexible contract processes the request and calls the fill function with the SwapRequest and SwapMeta objects.
Step 4: Executing the fill function
During the execution of the fill function, the malicious router address provided by the attacker is used to perform a delegate call to the routerData. Since there is no validation for the routerID, the fill function ends up calling the attacker's malicious contract instead of a legitimate DEX.
Step 5: Profiting from the vulnerability
The malicious contract's "transferFrom" function is executed, transferring the victim's approved tokens directly to the attacker's address. The attacker then converts these tokens to other cryptocurrencies or sends them to privacy-focused services like Tornado Cash to obfuscate their tracks.
Analogy: The thief withdraws the funds from the victim's account and moves them through various accounts or services to hide the source of the stolen funds.
By following these steps, the attacker can exploit the vulnerability in the selfSwap and fill functions to steal tokens from unsuspecting users who have granted the Dexible contract permission to manage their tokens.
Recommendations for Enhanced Security
Verify the router address on-chain
Implement a mechanism within the selfSwap function to ensure that the provided router address is a legitimate DEX. An on-chain allowlist or a registry of approved DEX addresses can be used to validate the router address before proceeding with the swap.
Analogy: A bank verifying the authenticity of a recipient bank account before allowing a funds transfer.
Whitelist legitimate routers
Include a whitelist of approved router addresses in the Dexible contract. By doing so, you can prevent users from interacting with malicious contracts, as only approved router addresses will be allowed to process swaps.
Analogy: A bank only allowing transfers to verified and approved external accounts.
Enforce routerID validation in the fill function
Add validation checks within the fill function to ensure that the routerID provided in the selfSwap request matches a known and approved router address. This added layer of security can help prevent unauthorized access to users' funds.
Analogy: A bank double-checking the recipient account details during a transfer process to prevent fraudulent transactions.
Regularly review and update approval allowances:
Users should regularly review and revoke token allowances granted to contracts. By doing so, they can minimize the risk of losing funds due to vulnerabilities in the contracts they interact with.
Analogy: A bank customer frequently reviewing and updating their list of approved payees to ensure that their funds are only accessible to trusted parties.
By implementing these recommendations, the security of the Dexible contract can be significantly improved, reducing the risk of similar exploits in the future and providing a safer environment for users to manage their digital assets.
Transaction Analysis: Tracing the Attacker's Steps
To understand the attacker's actions, we can analyze the transactions on the blockchain associated with the hack. We'll use the transaction 0x138daa4c as a reference.
Step 1: Identify the attacker's address
The attacker's address can be found from the transaction: 0x684083f312ac50f538cc4b634d85a2feafaab77a
Step 2: Analyze the transaction details
By inspecting the transaction details on a blockchain explorer like Etherscan, we can find information such as:
The selfSwap request data, The gas used and the status of the transaction (successful or failed). The tokens involved in the swap and the amounts transferred.
Step 3: Follow the flow of funds
After the attacker successfully exploited the vulnerability, they converted the stolen tokens to other cryptocurrencies or sent them to privacy-focused services like Tornado Cash to obscure their tracks. By tracing the flow of funds from the attacker's address, we can gather information about their actions after the hack.
Step 4: Observe related transactions
Examining other transactions associated with the attacker's address can provide insights into any patterns or similarities with other hacks. Additionally, it can help identify if the attacker has targeted multiple platforms or contracts.
By analyzing the transactions related to the hack, we can better understand the attacker's methods, actions, and the impact of the exploit. This information can be useful for preventing similar incidents in the future and improving the security of smart contracts.
Conclusion: Lessons Learned and the Importance of Security
The Dexible hack serves as a stark reminder of the importance of thorough security measures and audits in the world of decentralized finance. By exploiting a vulnerability in the selfSwap and fill functions, the attacker was able to bypass important safeguards and steal millions of dollars from unsuspecting users.
The incident highlights the need for rigorous security practices such as verifying router addresses on-chain, implementing whitelists, and regularly reviewing approval allowances. Following these recommendations can help prevent similar attacks and create a safer ecosystem for users to interact with DeFi platforms.
One crucial step in ensuring the security of smart contracts is to have them audited by reputable firms like BlockApex. A thorough audit by experienced professionals can help identify potential vulnerabilities and weaknesses in the code, allowing developers to address them before deployment. This proactive approach to security can save projects from significant financial losses and protect the trust of their user base.
In conclusion, the Dexible hack underscores the importance of robust security measures in the rapidly evolving DeFi landscape. By learning from these incidents and implementing best practices, we can build a more secure and trustworthy decentralized finance ecosystem.