Code reuse is considered best practice in software engineering. Reusing high-quality, secure code can speed development processes and often results in higher-quality code than software developed entirely from scratch. Additionally, the reuse of high-quality, audited libraries reduces security risks by decreasing the probability that new vulnerabilities will creep into the code base.
In open source communities such as the blockchain and crypto community, code reuse is even more strongly encouraged. Open-source code released with permissive licenses is intended to be reused in other projects.
However, this can also create security risks. Smart contracts and other software that reuses existing, open-source code can inherit vulnerabilities from these dependencies or introduce new ones. These are the four most significant supply chain security risks for blockchain smart contracts.
1. Reuse of Insecure Smart Contract Code
Few smart contracts deployed on any blockchain are developed entirely from scratch. Even projects as simple as defining a new ERC-20 token commonly depend on templates or reuse existing code. This makes deploying a new token as simple as changing a few parameters within the sample code and deploying the contract to the blockchain.
However, not all of the publicly available smart contract sample code is high-quality or secure. Open source code — including the code of smart contracts currently active on a blockchain — may contain fundamental vulnerabilities such as integer overflows, reentrancy, or price oracle manipulation.
Smart contracts copying or reusing this vulnerable code inherit these same vulnerabilities. This dramatically expands the opportunities for an attacker to exploit the vulnerable code as the same exploit can be copy-pasted to target multiple different smart contracts.
Case Study: Synapse and Nerve Bridge
Synapse and Nerve Bridge are two smart contracts hosted on Binance Smart Chain (BSC), which has rebranded as BNB Chain. Both contracts forked code from another project called Saddle.Finance.
This forked code contained a price calculation error in two functions, named swap and swapUnderlying. In swap, the value of the LP token included a virtual price, but the same was not true of swapUnderlying. As a result, the value used by swapUnderlying would always be lower than the one used by swap.
The attacker used a flashloan attack to exploit this vulnerability. The different price calculations caused significant slippage, allowing the attacker to drain $537,000 from the Nerve project. An earlier attempt to steal $8 million from Synapse failed due to an error made by the attacker.
2. Insecurely Modifying Secure Code
While some sample smart contract code might be insecure, other examples are of higher quality. These code samples have been developed in accordance with best practices and have undergone testing and auditing to validate their functionality and security. For example, OpenZeppelin publishes high-quality sample code for multiple Ethereum token standards.
Copying or reusing these code samples is best practice. However, these code samples should be reused as-is. Modifications to the example code can introduce new vulnerabilities. For example, changing which underlying function is used to perform certain actions — such as using transfer or send instead of call — can leave the function vulnerable to exploitation.
Case Study: Qubit Finance
In January 2022, the cross-chain bridge between Ethereum and BSC/BNB Chain operated by Qubit Finance was exploited for $80 million in tokens. The attackers took advantage of vulnerabilities introduced with a modified version of a library function published by OpenZeppelin.
Cross-chain bridges allow a user to deposit value into an address on one blockchain and receive an equivalent amount on another chain. In this case, a vulnerability in the project’s implementation of safeTransferFrom allowed the attacker to extract tokens without depositing anything.
The safeTransferFrom function created by OpenZeppelin is designed to transfer ERC20 tokens from one address to another. When calling the token contract to transfer the tokens, it uses address.functionCall. If there is no smart contract code at the address — i.e. it is an externally owned address (EOA) — functionCall will return false.
The Qubit contract used a modified version of safeTransferFrom, which used address.call instead. This function does not return false when no code exists at the indicated address.
The attacker exploited this vulnerability by using the whitelisted 0x0 address as address. Combined with other vulnerabilities, this allowed them to make fake deposits that allowed real withdrawals at the other end of the bridge.
3. Failing to Apply Security Updates
Any piece of software can contain errors, and some of these errors are exploitable bugs. This is especially true in the smart contract space where both smart contracts and the infrastructure that they run on are in a state of constant evolution. Code that may be considered secure one way may be vulnerable the next when a new attack is discovered or a change to the underlying blockchain violates a core security assumption.
Software developers address these new security issues by pushing out updates to vulnerable code. Even on the blockchain, it is possible to update code and apply security patches, preventing future exploitation of a vulnerable smart contract.
If a smart contract copies the code of a vulnerable smart contract, updates to the original contract are not automatically applied to the child contract. The contract’s developers need to track updates to the original code and apply any required security updates to their own codebase.
Often, after one smart contract is attacked or publicizes a patch for a security vulnerability, attackers look for other contracts that are vulnerable to the same exploit. A failure to apply security updates for copied code leaves a smart contract vulnerable to attack.
Case Study: Fei Protocol/Rari Capital
In April 2022, the Fei Protocol — before its merge with Rari Capital — was the victim of an $80 million hack. The attacker exploited a reentrancy vulnerability in the protocol’s smart contracts.
These contracts were forked from Compound, a project whose unaudited smart contracts were hacked for $147 million the previous September. These contracts were known not to follow the check-effect-interaction pattern that protects against reentrancy attacks.
The vulnerable code used call.value when making calls to functions in other contracts. This Ethereum function makes it possible for the fallback function of the target contract to perform another call. By calling back into the vulnerable function, a malicious contract could perform a reentrancy attack.
The vulnerability in the Compound code was fixed long before the attack by switching from call.value to transfer, which doesn’t provide enough gas for a reentrancy attack. However, the Fei Protocol changed the code back to call.value, allowing the attacker to exploit the project for $80 million.
4. Mismatched Security Assumptions
Smart contracts may use code from a variety of different sources. For example, a project may write some code internally and use third-party code from a few different projects to implement the desired functionality.
Done properly, this code reuse can speed development and improve the security of the problem. However, it must be done carefully to avoid introducing vulnerabilities.
Different smart contracts and functions may make certain assumptions about their inputs, outputs, and the other functions that they interact with. If parts of a smart contract’s code make different and incompatible assumptions, this can leave the contract vulnerable to attack.
Case Study: xFORCE
The xFORCE vault contracts were a fork of the xSUSHI contracts. The project was the victim of a hack in April 2021 in which approximately $367,000 in FORCE tokens were stolen.
The attackers took advantage of a mismatch in how different parts of the xFORCE ecosystem handled errors in ERC20 tokens. A failed token transfer could either revert — causing the entire transaction to roll back — or return false, requiring testing and error handling in the calling function.
The xFORCE token vault exploited in the attack was an xSUSHI fork that assumed that all failed token transfers would cause reversion, so no error handling was included. However, one of the tokens used — an Aragon Minime token — returns false upon reversion.
This combination means that if a deposit into the contract fails, the user retains their tokens. However, the vault contract assumes that the deposit was successful — since no reversion occurred — and transfers xFORCE tokens to the depositor.
The attackers exploited this vulnerability by making deposits that would intentionally fail. This allowed them to collect xFORCE tokens. These tokens could then be deposited in exchange for FORCE tokens held by the vault contract.
DevSecOps Best Practices for Smart Contract Supply Chain Security
Many of the supply chain vulnerabilities impacting smart contract security arise from a failure to apply DevSecOps best practices. Some best practices that can help DeFi projects to secure their supply chains include:
- Use audited libraries: When possible, use libraries or sample code from a reputable source that has undergone proper testing and auditing. Reusing high-quality third-party code both reduces the time and cost of development and decreases the probability of an expensive cyberattack.
- Maintain an SBOM: A software bill of materials (SBOM) lists an application’s library dependencies and the source of any copied code. This makes it easier to scan for security updates that need to be applied to a smart contract’s code.
- Perform vulnerability scanning often: Many common smart contract vulnerabilities can be detected using automated tools, such as slither. Running automated tests throughout the development process enables vulnerabilities to be caught early, minimizing their impact on release timelines and smart contract security.
- Audit before launch: The value of smart contract security audits is clear from the fact that all but a couple of the top 20 most expensive DeFi hacks were of unaudited projects. Engage a reputable smart contract security auditor before launching any code (including updates) to the blockchain.
This article is the second in a series discussing how to improve smart contract security. Check out the first article in the series and learn more about the importance of DevSecOps for smart contract security.