Let's Talk

Distributed applications (DApps) are designed to take advantage of the decentralization offered by blockchain systems.  Instead of relying on a traditional web hosting platform - with its associated concerns about centralization and resiliency - DApps host their backend code on the blockchain as smart contracts.

For DApp developers, security can be a significant concern.  Blockchain’s immutable ledger means that a poorly-designed smart contract can be difficult to repair and that any successful attack against a vulnerable contract is irreversible.

However, many DApp developers are not doing enough to ensure the security of their applications.  While a smart contract code audit is an important first step in securing a DApp, it is not enough to guarantee the application’s security.

Smart Contract Audits Are Not Enough for DApp Security

Many DApps are deployed with only a smart contract audit for security (if any security testing is performed at all).  However, this type of security assessment misses a number of potential risks to the security of the DApp.

Infrastructure Considerations

Smart contract security audits are designed to identify common vulnerabilities within the DApp’s smart contract code.  Common things that auditors look for include access control issues, integer overflows and underflows, reentrancy vulnerabilities (for Solidity smart contracts), and similar issues.  These issues often have known patterns that can be easily identified within an application’s code.

However, smart contracts can also contain vulnerabilities that are not visible in the code.  These applications run on top of a distributed ledger platform, which is a very different environment from a traditional computer.  These differences create the potential for attacks at the platform-level that exploit assumptions made within a smart contract.

For example, decentralized finance (DeFi) smart contracts can be vulnerable to frontrunning attacks, where an attacker observes a transaction and creates a competing transaction that is processed first (due to a higher transaction fee or sympathetic miner).  These attacks are possible due to the transaction-based nature of blockchain and enable an attacker to cheat the system and make a guaranteed profit.

Ethereum’s vulnerability to frontrunning was “discovered” in August 2020.  However, similar attacks were performed over three years earlier in the Bancor smart contract.  This vulnerability exists because smart contracts run on Ethereum, not because of an issue in a code.  As a result, a smart contract audit is unlikely to discover this issue even if the potential vulnerability hadn’t been “forgotten” for three years.

Web Application Security

DApps are implemented in two parts, just like any other web application.  The DApp backend is implemented as a smart contract, while the frontend is implemented as a web application that makes calls to the smart contract backend.

Smart contract audits only assess the security of the DApp’s smart contract backend, meaning that they overlook any potential vulnerabilities in the web application code and its connections to the smart contract.  Unless an assessment includes vulnerability testing for the frontend as well, it only covers less than half of the potential attack surface.

Effectively Securing Distributed Applications

Smart contract audits cover only one layer of the blockchain ecosystem and entirely ignore external applications that the contract may interface with.  This means that they are woefully inadequate at assessing the true vulnerability of a DApp to exploitation.

An accurate DApp security assessment requires a comprehensive penetration test as well as a smart contract security audit.  This enables the auditors to identify all potential attack vectors against the DApp, not just the ones that are visible in the DApp’s smart contract backend code.

Functionality and security testing is an essential part of the software development lifecycle.  The average developer creates a bug every 13 lines of code.  Smart contracts, which are often written in newer programming languages (like Solidity) and on relatively new platforms, are likely to have an even higher error rate than this.

Software development best practices state that testing should be a part of the development process.  Typically the testing stage comes after the code is complete but before release.  The DevSecOps movement is trying to “shift security left” by making it part of automated developer workflows.

However, many DeFi projects aren’t following these best practices.  Instead, they choose to “test in production”, a practice that puts the software, its users, and the company at risk.

What is “Test in Production”?

Software vulnerabilities and other bugs can be found in a number of different ways.  Traditionally, many companies try to identify and correct as many issues as possible while the software is still in development.  As a result, only 15 of the original 70 in 1,000 lines of code reach the consumer.

Post release, many companies will also support additional vulnerability detection efforts through bug bounty programs.  These programs reward security researchers for ethically reporting vulnerabilities in the software, enabling the manufacturer to fix them before they are exploited.

The “test in production” movement skips the first phase of performing in-house bug hunting before release.  These smart contracts are released without having undergone any security audits or assessments.  Instead, the developers hope that any vulnerabilities discovered will not be exploited by an attacker before they can be corrected.

Test In Production Puts Users at Risk

DeFi DApp users invest money in these protocols in order to make trades and, hopefully, turn a profit by their actions.  The smart contracts that power these apps run on the blockchain and transactions are stored on the immutable distributed ledger, making it impossible to reverse a successful hack.

These facts make the “test in production” approach to DeFi security very dangerous for their users.  A simple smart contract vulnerability, like the one discovered in the YAM Finance smart contract, can result in a theft or loss of access to user funds.  Within two days of operation, the YAM Finance contract was hacked, permanently locking $750 million of user funds within the broken contract.

Testing in production saves DeFi developers time and effort by eliminating the need to perform proper debugging and security testing before launch.  However, these advantages come at the expense of their users.

Properly Securing DApps

Protecting DeFi apps (or any DApp) against attack requires integrating security into the development lifecycle.  By identifying and eliminating potential attack vectors before the code is placed in production, developers gain user trust and reduce the probability of an embarrassing and expensive hack.

An effective DApp security assessment requires more than just a smart contract audit.  DApps require a smart contract audit and a comprehensive penetration test of the associated web application to achieve full coverage of the potential attack surface.

Decentralized finance (DeFi) is the use of decentralized ledgers (like Ethereum) for financial transactions.  A major part of the DeFi space is trading, where traders take advantage of fluctuations in market prices and exchange rates to make a profit.

Understanding the potential for profit in the DeFi space (and how some traders are “hacking” it) requires a bit of background knowledge:

Putting all of these different factors together, a DeFi “hacker” can make large guaranteed profits within a single transaction on the blockchain.

Exploiting Flash Loan Functionality

One example of the impact of DeFi hacking is the first hack against the bZx exchange.  This hack enabled the attacker to make a profit of about $355,880 in Ether by the end of the transaction.

The image above shows the flow of events that enabled the attacker to make this profit:

  1. The attacker takes a 10,000 ETH flash loan out from dYdX
  2. Using 5,500 of this loan as collateral, the attacker borrows 112 wBTC from Compound
  3. With 1,300 of the original loan, the attacker takes out 5x leverage on ETH/wBTC from bZx Fulcrum
    1. To service this, Fulcrum purchases 51 wBTC with 4,338 ETH from Kyberswap.  This causes slippage since Kyberswap had limited liquidity, driving up the value of ETH relative to wBTC
  4. The 112 wBTC borrowed from Compound is traded for 6871 ETH at Kyberswap
  5. 10,000 ETH of the attacker’s 10,071 ETH (6871 from step 4 and 3,200 left over from the original flash loan) goes to pay off the original flash loan.  The rest goes to the attacker.
  6. The attacker pays off the Compound loan, trading 112 wBTC (worth about 4,300 ETC) for the original collateral of 5,500 ETH.

In the end, the attacker has a net profit of 1,271 ETH (71 from step 5 and 1200 from step 6), worth about $355,880.  The entire operation was made possible by a bug in the bZx code (since fixed) that failed to check for slippage before making the purchase (in 3a) from Kyberswap.

White Hat or Black Hat Hacking?

Whether or not the bZx attacker “hacked the system” isn’t really a question.  The attacker took advantage of the flaw in the code to use bZx’s resources to dramatically change the wBTC/ETH exchange rate on Kyberswap.  This allowed the attacker to cash out their wBTC at the expense of bZx.

However, the attacker accomplished this by doing exactly what they were supposed to do under the “rules” of DeFi.  Tools like Furucombo are designed to help traders to build transactions like this to make a profit.  The bZx hacker only “cheated” by taking advantage of a flaw in bZx Fulcrum’s code to have a much bigger payoff than expected.

This vulnerability could have been identified by an in-depth security audit and penetration test of bZx.  But was the attacker wrong to take advantage of it and play the DeFi game “too well”?