Category: Tech Deep Dive

Dive into the Dedaub blog for expert insights on smart contract security and blockchain innovation. Explore the latest advancements in the Ethereum ecosystem, including program analysis and DeFi exploits, through concise, expert-driven content designed for developers and blockchain enthusiasts.

  • Thestandard.io Exploit | A Thorough Analysis by Dedaub

    Thestandard.io Exploit | A Thorough Analysis by Dedaub

    Hello everyone, this is Yannis Bollanos, Security Researcher at Dedaub. A few days ago, we published a tweet about the thestandard.io exploit that took place on November 6th, 2023, which you can find here: https://twitter.com/dedaub/status/1734598398055981471.

    The positive response from the X audience indicates a strong interest in the topic. As a result, I have decided to expand it into a blog post that can be used as a reference in the future.

    Thestandard.io exploit occurred on November 6th, 2023, and according to Crypto.news, approximately 280K EUROs were at risk. Fortunately, most of the funds have been recovered, so this is a hack story with a happy ending.

    After the excitement and tension of the moment subside, it is important to reflect on what happened and how we can prevent similar attacks in the future. It’s a great opportunity to re-emphasize that protocols should use defensive checks/assertions at every point their code interacts with a decentralized exchange (DEX).

    The @thestandard.io protocol issues coins to users who open over-collateralized positions, helping the protocol’s assets maintain a stable value by adjusting liquidity provision by actual market rates.

    In the @thestandard.io attack scenario, a SmartVault contract oversees the management of each user’s position, taking responsibility for adequately verifying the position’s liquidity. Users can issue coins by calling `mint`:

    Thestandard_io Exploit | A Thorough Analysis by Dedaub

    The SmartVault allows the exchanging of deposited collateral tokens through Uniswap’s V3 router (0xe592427a0aece92de3edee1f18e0157c05861564 on Arbitrum). Here is where things get interesting:

    SmartVaultV2 – Arbitrum – Source code (0x2E9f9Cc46679DBb5D94a1397Bd922cA5F6dA99Cd) a smart contract deployed on Arbitrum.

    One may inspect the source code in the (Dedaub) Contract Library for SmartVaultV2 – 0x2E9f9Cc46679DBb5D94a1397Bd922cA5F6dA99Cd. Below is the screenshot.

    Thestandard_io Exploit | A Thorough Analysis by Dedaub

    The things to note are:

    • With amountOutMinimum set to 0, the swap operation would succeed no matter the extent of the slippage incurred.
    • There were no other safeguards in place to ensure a fair exchange for the value provided in the contract.

    This enabled the owner of the vault contract to initiate a swap on a pool that might have been maliciously ’tilted,’ allowing for an exchange at an arbitrarily different price from the market price.

    There are two ways to profit from this:

    • (1) Utilize a flash loan and purposely sandwich the swap operation between a tilting and an un-tiling swap on the pool. This is a fairly typical attack pattern commonly used in exploits.

    OR

    • (2) Have the swap operation occur on a pool, the liquidity of which (as well as the execution price) is entirely controlled by the attacker. This can be done only on freshly created pools or in pools with near-0 liquidity.

    The attacker chose option (2) since a Uniswap V3 pool for PAXG-WBTC didn’t exist then. Here’s how everything is put together to form the attack:

    Attack Transaction:

    1. The attacker creates the Uniswap v3 PAXG-WBTC pool
    2. The attacker flash borrows 10 WBTC ( and a tiny extra amount to provide as initial liquidity)
    3. The attacker provides 10 WBTC as collateral and mints as many EUROs as possible

    One may inspect the relevant transaction in the (Dedaub) Contract Library for 0x51293c1155a1d33d8fc9389721362044c3a67e0ac732b3a6ec7661d47b03df9f – Arbitrum. Below is the screenshot.

    Thestandard_io Exploit | A Thorough Analysis by Dedaub

    The attacker provides liquidity to the PAXG/WBTC pool. WBTC and PAXG are at a 1:1 ratio within the tick range in which liquidity is minted. This is over-valuing PAXG by a lot.

    The attacker swaps the deposited WBTC for PAXG, and the swap operation goes through the attacker-controlled pool. The vault is now under-collateralized, in terms of real value: the PAXG it obtained has much less value than the EUROs issued.

    The attacker then burns all of his liquidity on Uniswap, and he notably receives ~9.9 WBTC. At this point, the attacker still holds the originally minted EUROs.

    The attacker swaps 10k of his EUROs for USDCs. Some USDCs are then employed to obtain the few remaining WBTCs needed to repay the flash loan.

    In the end, the attacker walks away with 280k EUROs and ~8.5k USDC.

    Fortunately, the attacker has returned ~240k EUROs back to the protocol:

    One may inspect the relevant transaction in the (Dedaub) Contract Library for 0xb08633c44d5f7c6fc10ad5685642c54e97900165bd1d64a1d003c99d1eec9a4b – Arbitrum. Below is the screenshot.

    Thestandard_io Exploit | A Thorough Analysis by Dedaub

    Thestandard.io Exploit | Key Learning

    Smart Contract developers should not solely rely on assumptions about on-chain liquidity/asset prices. The code should consistently enforce these assumptions (within a reasonable deviation).

  • Transaction Simulation Solutions | An In-depth Guide

    Transaction Simulation Solutions | An In-depth Guide

    Introduction to Transaction Simulation Solutions

    Transaction simulation tools improve developer and user experience when operating decentralized Web3 applications (Smart Contracts running on programmable blockchains).

    These tools can lower the risk and guesswork during development, deployment, and subsequent operation of Web3 applications. And they’re particularly useful in hostile security environments such as public blockchains.

    Transaction simulation tools allow developers and users to “dry-run” the execution of transactions on the blockchain without committing the state changes of this transaction to the ledger.

    For example, an end user can deposit funds in a yield farming vault and understand what proportion of the vault the deposit would be entitled to.

    Another example is the simulation of a decentralized autonomous organization (DAO) proposal to evaluate its integrity and functionality, ensuring it’s not malicious before implementation.

    In this article, we will explore the user experience and security issues that users and developers face when interacting with Web3 applications and how transaction simulation tools can help mitigate them.

    By the end of this article, you’ll better understand what transaction simulation tools do, how they work, and how they can improve both user and developer experience.

    The Need for Transaction Simulation Solutions in Blockchain

    Web3 applications, such as DeFi applications, enable novel financial primitives with many more possibilities for end users. However, the complexity and irreversibility of blockchain transactions have led to unexpected fund losses for many users, often due to poorly designed interfaces in these applications.

    Loss of funds is not the only issue for Web3 applications. We often face reverted or out-of-gas transactions, wasting funds, which is especially detrimental to our experience when interacting with Web3 applications.

    The impact of these challenges is not limited to regular end-users. Developers and Web3 teams face the complex task of ensuring their contracts perform as intended.

    Interacting with a blockchain protocol in a complex manner, for instance, through a multisig account, is a highly daunting task. Typically, it can be accomplished by forking the blockchain, but this is time-consuming.

    Real-world scenarios underscore how critical transaction simulation solutions are. For instance, in platforms Yearn Finance or Uniswap Labs, where complex financial transactions are constant, the necessity to simulate transactions is invaluable.

    In these cases, simulations allow users to review the outcomes of Smart Contract transactions in a controlled environment, giving teams time to identify and address potential issues before running them on-chain.

    Types of Transaction Simulation Solutions Available

    The market offers a variety of transaction simulation solutions, each catering to different needs and preferences.

    Browser Extensions are popular for their ease of use, integrating with web browsers to offer simulation capabilities alongside wallet interactions.

    In-Wallet Simulations integrate with the wallet software, providing a seamless experience for users to simulate transactions within the wallet interface.

    Standalone Tools are comprehensive software solutions. These offer advanced features and greater flexibility for complex simulations. Developers and organizations needing detailed analyses and custom simulation scenarios prefer standalone tools.

    Advantages of Using Transaction Simulation Tools

    ERROR PREVENTION

    Error prevention is a crucial advantage of transaction simulation tools, as they enable developers to simulate transactions in a controlled environment.

    This process helps identify and correct errors before executing them on the blockchain, significantly reducing the likelihood of costly mistakes such as failed transactions that consume resources without achieving their intended outcomes.

    Consequently, these tools greatly enhance blockchain applications’ overall reliability and efficiency.

    EDUCATIONAL VALUE

    For newcomers to blockchain development, transaction simulation solutions are invaluable educational resources. They provide a hands-on, risk-free platform for understanding the intricacies of blockchain transactions.

    They allow developers to experiment with different scenarios, gaining practical insights into the operation of Smart Contracts. This experiential learning accelerates any developer’s expertise in blockchain technology, empowering them to build more sophisticated and secure dApps.

    Choosing the Right Transaction Simulation Solution

    Selecting an appropriate transaction simulation solution is crucial for blockchain developers. These tools come in various forms, each suited to different needs and environments.

    Factors to Consider:

    • Network Support: Ensure the tool supports all relevant blockchain networks your project interacts with. For instance, if your Smart Contract runs on Ethereum and Polygon, the chosen transaction simulation solution must accommodate both.
    • Ease of Integration: Assess how seamlessly the tool integrates into your existing development. A smooth integration minimizes disruptions and maintains development flow.
    • User Experience: Assess the tool’s user interface and usability. A good simulator should offer clear insights into the transaction process, aiding decision-making and error identification.
    • Type of Tool: Decide between browser extensions and wallet-based simulators. Browser extensions are generally more flexible and accessible to test across various wallets, whereas wallet-based solutions offer a more integrated experience.

    EVALUATION CRITERIA:

    • Reliability and Support: Investigate the tool’s performance history and the provider’s responsiveness to support queries and updates.
    • Track Record: Consider the provider’s reputation within the blockchain community. Long-standing, positively reviewed tools often indicate reliability and efficacy.

    RECOMMENDATIONS:

    • Opt for solutions that prioritize security and accuracy in transaction simulation.
    • Avoid tools that are overly complex or do not offer transparent processes, as these can hinder rather than help your development efforts.
    • Stay informed about the latest developments in transaction simulation technologies to ensure your choice remains relevant and effective.

    Selecting the right tool is crucial. It must meet technical requirements and adhere to the highest security and efficiency standards in the blockchain space.

    Dedaub Watchdog Transaction Simulator

    The Dedaub Watchdog Transaction Simulator allows users to simulate transactions when interacting with complex Smart Contracts before committing to the main chain.

    It allows an understanding of all the various actions that would happen without the risk of losing funds. The Dedaub Watchdog transaction simulation provides three approaches, depending on specific use cases:

    • Through the Dedaub Simulation API, developers can integrate simulation directly into their applications.
    • Through the read/write/simulate feature on any Smart Contract page in Watchdog.
    • By installing the Dedaub TX Simulator Snap in Metamask.

    When used by an end-user, such as in the latter two approaches, the transaction simulation presents relevant information in convenient formats through the Watchdog UI.

    One such format is the (i) trace format, which contains all intermediate Smart Contract functions called, new Smart Contracts created, and events fired.

    The other format contains fund transfer, and (ii) includes the amount of funds transferred, both for the user and other participants in the transaction.

    (Trace format above)

    (funds transferred above)

    When used by Web3 users, an important use case is checking the legitimacy and reliability of the accounts and Smart Contracts involved in the transaction. By simulating transactions, users can also gain insight into potential outcomes, allowing them to identify risks proactively.

    The Dedaub Watchdog Transaction Simulator leverages the Dedaub Smart Contract database. The database offers detailed, real-time information on all deployed Smart Contracts on-chain, providing deep insights into the workings of Smart Contracts.

    Conclusion

    In conclusion, transaction simulation tools, particularly those exemplified by the Dedaub Watchdog Transaction Simulator, represent an advancement in Web3 application development and user interaction. They provide an extra layer of security and insight, allowing developers and end-users to identify and rectify potential issues in Smart Contract transactions promptly. These tools prevent costly errors and fund losses and serve as educational resources for those new to blockchain technology. With their ability to simulate complex financial transactions in a controlled environment, transaction simulation solutions enhance the efficiency, reliability, and overall user experience of interacting with Web3 applications.

  • Smart Contracts | Tale of Little Bugs

    Smart Contracts | Tale of Little Bugs

    As most programmers would admit, the most annoying bugs are often the “little” ones. Tiny logic errors caused by a few wrong characters in a single line of code, compiling fine and remaining undetected, patiently waiting to crash our program at the worst possible moment. We’ve all written such bugs, spent countless hours debugging them, and uttered the most horrific profanities when we finally discovered that we lost our sleep over a couple of wrong characters.

    Smart Contracts | Tale of Little Bugs

    But losing a night’s sleep over a little bug isn’t the worst of our worries. At least not if one writes software for NASA, whose Mars Climate Orbiter famously burned up in the Martian atmosphere due to a software bug. Well, NASA software is complex; such a catastrophic bug should clearly be complicated, impossible to understand by mere mortals, right? Far from it, the bug that led to the loss of the $125 million Mars Climate Orbiter was a trivial but crucial missing multiplication by 4.45. Europeans aren’t immune to little bugs either; the loss of ESA’s $370 million Ariane V rocket in just 39 seconds was caused but a simple integer overflow error.

    Thankfully, for the longest time, one needed to be employed by a space agency to worry about a little bug having such enormous financial consequences. That is until Smart Contracts arrived! Now, programs consisting of a few hundred lines of relatively “simple” code, developed by small teams over a relatively short period of time, are directly responsible for safeguarding various types of multi-million-dollar assets. All it takes is one undetected little bug and we get, not a spectacular rocket explosion, but an equally spectacular crypto hack that makes the Mars Climate Orbiter seem like pocket change.

    So, let’s look at an instructive example of such a little bug. Smart Contracts typically use Solidity modifiers to guard their functions, performing crucial security checks.

    modifier isOwner() {
        // Make sure we're called by our trusted owner before doing anything.
        require(msg.sender == owner, "Caller is not owner");
        _;
    }

    Writing such a check is simple, no need to be a NASA engineer to do it. But better double and triple-check it because the consequences of the tiniest of bugs in that line are enormous.

    error CallerNotOwner();  // gas efficient and easy to recognize
    
    modifier isOwner() {
        // I wish this were valid code, but it isn't.
        require(msg.sender == owner, CallerNotOwner());
        _;
    }

    Not a big deal, you’ll say, require is just a combination of a check and a revert; we can rewrite it and perform the two steps manually.

    modifier isOwner() {
        // This works fine
        if(msg.sender != owner)
            revert CallerNotOwner();
        _;
    }

    Mission accomplished, but you might have noticed a small detail. In the code above msg.sender == owner was replaced by its negationmsg.sender != owner. This is because require expects a condition that should hold, while its if/revert replacement expects a condition that should not. So, in general, we should replace

    require(some_complicated_expression, "my error");

    by

    if(!some_complicated_expression)
        revert MyError();

    This negation of the Boolean expression is exactly the beginning of our “little bugs” story. Well, how hard is it to simply add a “!”? But that’s not exactly what we did above, is it? No programmer that appreciates code simplicity and elegance writes

    if(!(msg.sender == owner))

    Everyone would simplify it to

    if(!(msg.sender == owner))

    bringing the negation inside the Boolean expression. And what if the negated expression is more complex? Logic, being the foundation of computer science, provides us with simple rules:

    !(A && B)  is equivalent to  (!A || !B)
    !(A || B)  is equivalent to  (!A && !B)

    Just carefully follow the rules inside the complex Boolean expression, and you’ll be fine. Easier said than done; I bet every single programmer with a few years of experience has incorrectly negated a Boolean formula at some point in their career.

    So, it shouldn’t be surprising that this exact bug appeared in one of our recent audits. The above commit aimed at replacing a string error with a custom one and, in doing so, changed:

    modifier onlyOwnerOrUpdater() {
        require(
            owner() == _msgSender() ||
            (updater != address(0) && _msgSender() == address(this)),
            "NetworkRegistry: !owner || !updater"
        );
        _;
    }

    to

    modifier onlyOwnerOrUpdater() {
        if (_msgSender() != owner() &&
            (updater == address(0) && _msgSender() != address(this)))
            revert NetworkRegistry__OnlyOwnerOrUpdater();
        _;
    }

    Did you spot the negation error? The expression is of the form A || (B && C), so its negation becomes !A && (!B || !C), the && in B && C should change to ||. So, the correct check should be

    if (_msgSender() != owner() &&
        (updater == address(0) || _msgSender() != address(this)))

    These two wrong characters (&& instead of ||) completely change the logic of the modifier; now an unauthorized call with updater != address(0) and _msgSender() != address(this)) will not trigger the error as it should, which could easily lead to a total loss of funds for this specific contract.

    Of course, the point is not that Smart Contracts are impossible to secure: this bug was caught by the audit (the chances of catching it were very high), and even if it weren’t, we are confident that it would still have been found before releasing the code, either by manual inspection or automated tests.

    But its mere existence shows that Smart Contracts, as with all programs, are not immune to little bugs. Even the simplest of changes require caution and should be properly tested and audited, both internally and by external teams, to minimize the chances of a catastrophic little bug as much as possible.

  • The Critical Thirdweb Vulnerability

    The Critical Thirdweb Vulnerability

    Summary: The root cause of the thirdweb critical vulnerability is that independent libraries implementing ERC2771 & Multicall, such as OpenZeppelin Libraries, interact badly, when combined. This allows attackers to spoof the _msgSender() with all sorts of access control implications including loss of funds.

    Critical Thirdweb Vulnerability

    The issue is complex, but can be explained using a simple analogy. Imagine a bank that will let one of the bank officials carry out a transaction on your behalf, as long as the instruction is written on a piece of paper with your verified signature. This is a very common scenario, for instance with some preferred bank clients. So, you go to the bank official and hand him a signed piece of paper. Your instructions are “take this sealed box to the cashier, open it, and give him what’s inside”. The bank official happily executes your signed instructions, after checking your id against your signature. The sealed box contains another piece of paper reading …”do a withdrawal on behalf of Elon Musk”, signed with a fake signature. The cashier takes this piece of paper from the bank official, thinking that the signature was checked, when, really, the only signature that was checked was on the instructions to deliver and open the box. That’s it!

    Now let’s look into the technical mechanics for how this vulnerability works, and how to protect your project from this issue.

    The Critical Thirdweb Vulnerability | Background

    First, we need to cover some preliminaries. In particular we need to first understand the implementation of the ERC2771 standard and the OpenZeppelin Multicall library. ERC2771 gives the ability to have a “virtual” msg.sender, i.e., caller of a public function of a smart contract.

    ERC2771 defines a contract-level protocol for Recipient contracts to accept meta-transactions through trusted Forwarder contracts. No protocol changes are made. Recipient contracts are sent the effective msg.sender (referred to as _msgSender()) and msg.data (referred to as _msgData()) by appending additional calldata.

    ETHEREUM ERC-2771

    Therefore, this virtual msg.sender, called _msgSender() is set by a trusted external party, the forwarder. And how does the forwarder tell the contract what is the virtual msg.sender? It appends an extra parameter to all calls. This means that all functions of a contract that supports such virtual msg.senders need to take in an extra parameter which they interpret as the msg.sender. The other side of the vulnerability is Multicall. It is a way to have a single call that becomes many calls (to the same contract) in sequence. How does this happen? By making all the info of the “many calls” be parameters of the “outer” single call.

    The Critical Thirdweb Vulnerability | Root cause?

    The problem with these two libraries is that the forwarders (in ERC2771) were not designed to work with multicall. They add a single _msgSender() parameter to the outer call of a multicall. But remember: all functions now expect this parameter! Where can they get it from? The parameters of the *outer* multicall.

    So, if an attacker uses multicall to call, say, 3 functions in sequence, the attacker can define all the parameters to these function calls, including the _msgSender()! This means that the attacker can make a call appear to be coming from anyone!

    The Critical Thirdweb Vulnerability | Evaluating the impact

    We have tried to reach out to most large projects that might have been affected (in collaboration with thirdweb and OpenZeppelin) over the last few days. However, if you are worried about this issue affecting your contract, we have flagged any contract affected on Watchdog and made this information available to the public. However, the extent to which your contract is affected depends on actual implementation of the contract. First, evaluate functions with access to _msgSender() (transitively). Do these functions contract check access control mechanisms using _msgSender()? For example, can someone withdraw or burn coins for the _msgSender()? In that case, the issue affects your contract, critically. In many of these contracts there may be onlyOwner or onlyRole modifiers that make use of _msgSender(). In addition, look for common transfer functions such as safeTransferFrom() or transfer(). The effect is also modulated by the value of the assets held by the contract, or if this contract represents an asset. Make sure to find out if your contract is a token in a Uniswap-like liquidity pool. It is possible that all the liquidity in this pool could be stolen due to this issue.

    The Critical Thirdweb Vulnerability | Mitigation

    The rest of the article outlines mitigation. Thirdweb has developed and deployed a mitigation tool that can possibly assist you. A large number of affected contracts were deployed by their product. However, oftentimes you’d need to take additional actions. Should you require assistance the team at Dedaub can at least point you to the right information. You may contact us here. In the rest of the article we list some some mitigation options we’ve observed over the last few days to be successful. Legal disclaimer: This should not be construed to be professional advice by our team.

    PREFERRED MITIGATION: DISABLE TRUSTED FORWARDER

    Some ERC2771 library implementations allow resetting a trusted forwarder. Doing so will prevent any gasless transaction from being executed through the forwarder, solving the issue (albeit at the cost of missing functionality). Unfortunately there are many instances where resetting the trusted forwarder is not possible, so the rest of the mitigation steps apply. Otherwise, your smart contract is probably safe from this issue.

    ADVANCED MITIGATION METHODS

    These mitigation methods may take time and expertise to successfully execute. If time is critical, you can consider decreasing the blast radius in the next section in case an attacker hacks the contract while you are in the process of planning a mitigation.

    If your contract is Upgradeable, prepare an upgrade. Removing either multicall (and all functionality that delegatecalls to the same smart contract) prevents the attack. In addition, removing ERC-2771 functionality also prevents the attack. Other ways to prevent this attack involve adding a module that allows doctoring of the contract’s storage and removing the trusted forwarders in this way. This latter is difficult to execute correctly.

    DECREASING THE BLAST RADIUS

    Some steps can be taken in cases you do not manage to mitigate the issue in a timely fashion to limit the amount of stolen assets from your contract. This can be done in several ways:

    1. Ask your users to remove approvals from your contract. You can additionally check which users have approved your smart contract to transfer funds by checking on app.dedaub.com, navigating to your smart contract, navigating to balances and then allowers. Note that publicly announcing removal of approvals can work both in favor and against you since malicious hackers could be tipped off.
    2. Pausing your contract may help users from continuing to use it, but, depending on the implementation it might not prevent the attack.
    3. Remove liquidity from Uniswap-like pools in case the token is held by a pool, otherwise the liquidity in this pool may be drained in some cases.

    Conclusion

    The thirdweb vulnerability is an unfortunate issue that came about due to the composability of libraries in a single smart contract, through inheritance mechanisms. Unfortunately, although libraries are supposed to be abstractions, when it comes to security abstractions can easily be broken and implementations can affect each other in unforseen ways. This was even the case despite the overwhelming majority of affected libraries were developed by the same organization. In their defence however, it is very hard to make libraries interoperable, and, furthermore even harder to make them upgradable. Our audit team at Dedaub regularly finds issues in smart contracts that employ “safe” 3rd party libraries. Our decompiler and contract analysis tools really help in such cases as they work on the actual deployed code of a smart contract. We regularly find issues related to upgradability, but other issues may be lurking.

    We would like to commend the work of countless other security engineers who have helped reach out to affected projects!

  • Smart Contract Audits Guide

    Smart Contract Audits Guide

    Smart Contract Audit Essentials: Navigating the Web 3 Landscape with Expertise and Security

    With blockchain platforms, Smart Contract Audits play a critical role in ensuring the security and reliability of decentralized applications. These audits are routine checks and an indispensable part of the development process, safeguarding all transactions and agreements that define the blockchain ecosystem.

    Smart Contracts, with their immutable and autonomous nature, demand absolute precision in their code. Any oversight or vulnerability can lead to significant financial losses or erode trust in the technology.

    Smart Contract Audits Guide

    At Dedaub, we blend academic thoroughness with a hacker’s practical acumen to delve deep into Smart Contract code. The main goal of a Smart Contract Audit is to eliminate faults. Our approach is to understand the intricacies of each contract and its potential pitfalls, to provide solutions that fortify its foundation.

    To date, we have conducted over 200 rigorous audits for leading blockchain protocols and safeguarded billions in Total Value Locked (TVL).

    Leading blockchain clients such as the Ethereum Foundation, Chainlink, and Coinbase have placed their trust in us, not just for our ability to spot vulnerabilities but for our commitment to elevating the standards of blockchain security.

    The Critical Role of Audits in Blockchain Security

    At its core, a Smart Contract Audit is a meticulous process where experts scrutinize the code of a blockchain Smart Contract (SC) to identify vulnerabilities, inefficiencies, and potential exploits.

    The systematic examination of Smart Contract Audits is crucial in the blockchain domain, where SCs play a pivotal role in automating, verifying, and enforcing the terms of a digital contract. This is essential when using blockchain technology because transactions are irreversible, making the accuracy and security of SCs essential.

    Smart Contract Audits combine automated tools with expert reviews. The process starts with thoroughly analyzing the contract’s design and architecture. It continues with a detailed line-by-line code examination to uncover hidden issues.

    Auditors look for common vulnerabilities like reentrancy attacks, overflow/underflow issues, gas limit problems, and more nuanced logic errors that could compromise the contract’s functionality.

    Dedaub is a reliable partner with expertise and dedication to excellence. We specialize in ensuring that Smart Contracts adhere to the highest security and reliability standards, regardless of the protocol used.

    The Dedaub Audit Methodology

    At Dedaub, each of our Smart Contract Audits is a meticulously crafted process. Each one uniquely combines academic precision with practical hacking insights. This comprehensive approach is structured into five stages, ensuring a thorough and effective audit tailored to each project’s needs.

    Stage 1: Cost and Schedule Proposal

    Our process begins with carefully assessing the Smart Contract’s codebase, considering its scope and complexity. We formulate a cost-effective proposal and a realistic timeline that aligns with your project’s deadlines and budget constraints. This initial stage sets the groundwork for a well-organized audit process.

    Stage 2: Audit Commencement

    In the second stage, our experts dedicate the agreed time to analyze your Smart Contract thoroughly. This phase includes ongoing interaction with your development team. This fosters a collaborative and efficient audit, where we examine every aspect of the Smart Contracts to identify potential vulnerabilities.

    Stage 3: Preliminary Findings Delivery

    We then categorize and detail the findings in a preliminary report, classifying them by risk level: Critical, High, Medium, Low, or Advisory. A discussion session with your team is held at this stage to clarify any issues and set the groundwork for the next improvement steps.

    Stage 4: Issue Resolution Process

    At this stage, your developers work to address the identified issues, guided by our tailored advice provided in the initial report. This collaborative approach ensures the effective implementation of solutions to enhance the contract’s security and functionality.

    Stage 5: Final Review and Report

    In the final stage, we conduct a comprehensive post-mitigation review to confirm the resolution of all issues. The process culminates with a detailed final report documenting the entire audit process and its outcomes. This results in a clear roadmap for ongoing Smart Contract security.

    Dedaub’s audit methodology is designed to ensure precise and practical auditing of Smart Contracts. Our approach helps to enhance the security issues of blockchain projects by effectively identifying and addressing potential vulnerabilities

    Case Studies

    We work for the Ethereum Foundation on complex studies such as Ethereum Improvement Proposals (EIPs) EIP-4878EIP 6404, EIP 6466EIP 4758 and EIP 6780.

    The EIP 6404 and EIP 6466 is a study to assess the potential impact of Ethereum Improvement Proposals (EIPs) 6404 and 6466. In a project commissioned by the Ethereum Foundation, Dedaub undertook an extensive study to assess the potential impact of Ethereum Improvement Proposals (EIPs) 6404 and 6466.

    These EIPs proposed significant modifications to the Ethereum network, particularly in the serialization algorithm for transactions and receipts. This shift involved moving from the Recursive Length Prefix (RLP) format to the Simple Serialize (SSZ) format.

    This change directly impacted the Receipts Root and Transactions Root fields in the execution layer headers, presenting a complex challenge for existing Smart Contracts on the Ethereum mainnet.

    The Challenge

    The primary concern was the potential disruption to contracts relying on RLP for proofs, especially those critical to decentralized bridges. These bridges play a crucial role in creating proofs about historical transaction logs.

    Our objective was to quantify and qualify the extent of potential disruption and identify specific on-chain patterns verifying commitments in this new manner. This required a detailed, semi-automated examination of all Smart Contracts on the Ethereum network, analyzing their recent behavior to gauge the impact of these changes.

    Our Approach

    We analyzed various Smart Contracts, identifying those critical to projects and assessing possible mitigating actions. Our team concentrated on evaluating the impact of these changes, especially on projects involving cross-chain bridges, and considered both on-chain solutions like upgrades and off-chain strategies like modifying oracles.

    Findings and Impact

    Our study revealed that the changes proposed in the EIPs notably affected a handful of projects, predominantly cross-chain bridges. Some of the key projects impacted included:

    Interestingly, our findings showed that out of the two proposed EIPs, only EIP-6466 (Receipts Root EIP) significantly impacted the inspected protocols. This was due to its effect on log-inclusion proofs, a common method for conducting cross-chain message passing.

    Why Choose Dedaub for Smart Contract Audits?

    If you’re looking to get a Smart Contract audit for your blockchain project, choosing the right partner is important. Dedaub is a reliable and trustworthy choice in this regard, not just because of our technical expertise but also because of the values we stand for – integrity, innovation, and the empowerment of blockchain talent. Our approach is rooted in these core values, directly translating into our high-quality audits.

    Integrity in Every Audit

    At Dedaub, integrity is at the forefront of everything we do. This means conducting audits with the utmost honesty, thoroughness, and transparency. Our clients’ trust in us is integral to their success.

    Our commitment to integrity ensures that every audit is conducted with meticulous attention to detail, offering our clients a true and complete assessment of their Smart Contract’s security.

    Pioneering Innovation

    Innovation is key in the rapidly evolving blockchain landscape. Our team constantly explores the latest advancements in blockchain technology and Smart Contract development. This pursuit of innovation enables us to provide cutting-edge solutions to our clients, ensuring their Smart Contracts are resilient against current and future security threats.

    Empowering Blockchain Talent

    We believe in empowering the next generation of blockchain professionals. Through our Smart Contract Audits, we secure our clients’ projects and share knowledge and insights that contribute to the overall growth of the blockchain community.

    By educating and nurturing talent, we’re helping to build a more secure and robust blockchain ecosystem.

    These core values of Dedaub translate into a thorough and forward-thinking audit service that contributes positively to the broader blockchain community. Choosing Dedaub means partnering with a team that is deeply invested in the success and security of your project, as well as the advancement of the entire blockchain industry.

    The Future of Smart Contract Audits, Embracing ZK Audits and Beyond

    The landscape of Smart Contract Auditing is constantly evolving and is being influenced by groundbreaking trends and innovations. One of these trends is the emergence of Zero-Knowledge (ZK) proofs, a pivotal technology that is reshaping how audits are conducted. At Dedaub, we are always at the forefront of these advancements and are integrating them to offer more robust and sophisticated audit services.

    Our team has a combination of cryptography expertise and hands-on knowledge of ZK-proof systems and technologies. Our auditors invest significant time in continuous education on foundational knowledge and applied knowledge, with a recent emphasis on the domain of zero-knowledge proofs.

    Conclusion

    The significance of Smart Contract Audits in fortifying the Web3 ecosystem cannot be overstated. As the digital landscape evolves, these audits form the backbone of trust and security, ensuring blockchain technologies function as intended and uphold the highest standards of reliability and integrity.

    Dedaub, with our unique blend of academic rigor and practical expertise, stands as a vanguard in this field. We offer comprehensive audits that safeguard against vulnerabilities and fortify the foundations of decentralized applications.

    We invite you to leverage our extensive experience and expertise. Contact us at Dedaub to discuss how we can elevate the security and performance of your Smart Contracts, paving the way for a safer, more robust Web3 future. More info.

  • Smart Contract Security Tools | A Guide to Dedaub Security Suite, Step-by-step Tutorial

    Smart Contract Security Tools | A Guide to Dedaub Security Suite, Step-by-step Tutorial

    Dedaub Security Suite (former Watchdog) is a comprehensive security system designed for Smart Contract analysis and transaction monitoring. To make the most of Dedaub Security Suite’s offers, we’ve released a detailed step-by-step tutorial to guide you through its various capabilities.

    Let’s delve into how this tutorial empowers you to harness the full potential of Watchdog.

    Smart Contract Security Tools | Static Analysis

    Smart Contract security is always evolving, and staying ahead of threats is crucial. Dedaub Security Suite‘s Static Analysis serves as your first line of defense, rigorously examining contract bytecode to flag potential vulnerabilities before they manifest into real threats. Our tutorial shows you how to navigate this preemptive feature for a stronger, more resilient codebase.

    • Deep-dive into contract bytecode to identify looming vulnerabilities with Watchdog’s state-of-the-art static analysis engine.
    • Benefit from various warning types, alerting you to diverse potential issues.
    • Harness the power of extensive warning categorization and tagging, including tens of warning categories, such as reentrancy, signature malleability, and untrusted transfers.
    • Craft your own code queries to scrutinize specific vulnerabilities, behaviors, or attributes in contracts (such as balances, allowances, or recent transactions).

    Smart Contract Security Tools | Transaction Monitoring

    Blockchain is a fast-paced world, and reactive strategies don’t work too well. Dedaub Security Suite‘s Transaction Monitoring empowers you to respond, anticipate, and preempt security threats with real-time blockchain surveillance. Learn to set up intricate filters and monitoring systems via our in-depth tutorial.

    • Conduct deep transaction analysis for nuanced insights into contract interactions, down to minor details.
    • Use advanced filters to focus on the events most critical to your project’s security.
    • Leverage macros to calculate and extract specific data values for even deeper transaction scrutiny.
    • Access detailed transaction logs, replete with decoded function calls, emitted events, and vital status information.
    • Tailor your monitoring scope by setting transaction amount or frequency conditions, sharpening your project’s risk management.

    Smart Contract Security Tools | Reports

    Regular updates on your project’s security posture are not a luxury but a necessity. Dedaub Security Suite‘s Reports feature goes beyond mere data compilation, offering actionable insights to inform your strategic decision-making. Master the generation and interpretation of these comprehensive reports through our tutorial.

    • Receive meticulous, in-depth reports to dissect and understand contract vulnerabilities in detail.
    • Expect rigorously compiled quarterly reviews to gauge your project’s security landscape consistently.
    • Benefit from an added layer of human scrutiny, focusing on high-severity vulnerabilities that automated systems might overlook.

    Development Support: Safety Before Deployment

    Deploying a Smart Contract is irreversible and any vulnerabilities can become permanent liabilities. Our tutorial allows you to utilize Watchdog’s Development Support feature for critical pre-deployment assessments. Learn how to upload project snapshots and scrutinize them against potential security flaws.

    • Seamlessly upload snapshots of your projects that are still in the development phase.
    • Utilize support for popular development frameworks such as Foundry and Hardhat.
    • Engage pre-deployment checks to catch vulnerabilities before they become part of the blockchain.
    • Use the project snapshot feature for an additional layer of pre-deployment scrutiny.

    Stay Ahead with Dedaub Security Suite

    Smart Contract Security Tools

    Unleash Dedaub Security Suite”s full capabilities, gaining the right expertise. Learn the nitty-gritty details to take full control of your Smart Contract security. Watch our comprehensive step-by-step tutorial now!

  • Platypus Finance Hack

    Platypus Finance Hack


    Platypus Finance Hack: The platform was targeted by a flashloan attack, resulting in an approximate $2 million loss. This sophisticated attack utilized flashloans alongside tactics to alter slippage calculations in various swaps, thereby manipulating the price of the swapped assets to benefit the attacker.

    Platypus Finance Hack | The Attack Summary

    At 12th Oct 2023, 06:32 UTC, an attacker on Avalanche C-Chain (addresses: 0x0cd4fd0eecd2c5ad24de7f17ae35f9db6ac51ee7 & 0x464073F659591507d9255B833D163ef1Af5ccc2C), performed multiple on-chain transactions via smart contracts deployed within the same transaction itself. We shall concentrate on a single instance on this attack, where the attacker profits around $570k. The operations performed are as follows:

    Flash loan on AAVE
    Deposit 1050k WAVAX
    Deposit 316k sAVAX
    Swap 600k sAVAX to 659k WAVAX
    Withdraw 728k WAVAX

    Swap 1200k WAVAX to 1250 sAVAX
    Withdraw 34k WAVAX
    Swap 600k sAVAX to 840k WAVAX
    Withdraw 316k sAVAX
    Repay AAVE Flash Loan

    Note that the deposits and swaps are performed on a relatively novel “Stable Swap” AMM (Platypus).

    Judging by the events that took place and by looking at calculations performed throughout the attack, we are fairly sure that the root cause of this attack occurs due to a manipulated slippage calculation. Moreover, the mechanism employed in calculating the slippage (and thus the price at which a swap takes place) is flawed, in cases where liability balance and cash balances are manipulated differently. When such a condition arises, the slippage manipulation can be in the attacker’s favor in both directions of the swap, thus breaking the invariant that the slippage is symmetric.

    In order to understand the intimate mechanics of the protocol, let’s first back up and look the key features of the Platypus AMM and lending protocol:

    1. Unilateral Liquidity Provision: Platypus allows users to provide liquidity to just one side of a trading pair, rather than requiring liquidity for both tokens in a pair.
    2. Account-based Design: Instead of using pools for each token pair, the protocol uses accounts to record assets and liabilities, allowing for a more flexible and capital-efficient system.
    3. Coverage Ratio: Platypus uses a “coverage ratio” as an input parameter instead of simply focusing on liquidity. The coverage ratio is defined as the assets (A) divided by the liabilities (L) for a given token account. A higher ratio indicates lower default risk. This is a departure from Curve’s stableswap invariant and allows the token pool to grow based on organic demand and supply.
    4. Open Liquidity Pool: The protocol is designed to be extensible, allowing new tokens to be added to existing pools easily. For example, starting with a base of USDT, USDC, and DAI, more tokens like TUSD and FRAX can be added later.
    5. Price Oracle: Platypus uses external price oracles like Chainlink to track the exchange rate of each token in terms of USD. This is important for maintaining pegs and calculating exchange rates for swaps.
    6. Solvency Risk: The protocol aims to keep the coverage ratio above a certain level to mitigate the risk of default. If a withdrawal request exceeds the assets available in a specific token account, that could trigger a default.

    Platypus Finance Hack | DETAILED DESCRIPTION

    Now that we got a glimpse of the features, in this section we will discuss how prices are calculated.

    Platypus Finance Hack

    Platypus uses an Oracle to calculate the ideal prices between assets. When the assets are of the same variety (e.g., wrapped vs. staked versions), such a price Oracle is easily implemented. However, what the makes a big impact to the price in this exploit is the slippage calculation, which can benefit the attacker. The goal of the attacker was to amplify the slippage in their favor, by using a clever trick.

    Normally, in this protocol, depositing and withdrawing increases or decreases, respectively, both assets (called cash) and liability in tandem. However, when a withdrawal takes place but there is not enough cash remaining to satisfy the withdrawal, the full liability amount is decreased, despite the asset amount partially decreasing. When this happens, the slippage amount is seemingly manipulated towards the attacker in both directions of a swap.

    /**
         * @notice Yellow Paper Def. 2.4 (Asset Slippage)
         * @dev Calculates -Si or -Sj (slippage from and slippage to)
         * @param k K slippage parameter in WAD
         * @param n N slippage parameter
         * @param c1 C1 slippage parameter in WAD
         * @param xThreshold xThreshold slippage parameter in WAD
         * @param cash cash position of asset in WAD
         * @param cashChange cashChange of asset in WAD
         * @param addCash true if we are adding cash, false otherwise
         * @return The result of one-sided asset slippage
         */
        function _slippage(
            uint256 k,
            uint256 n,
            uint256 c1,
            uint256 xThreshold,
            uint256 cash,
            uint256 liability,
            uint256 cashChange,
            bool addCash
        ) internal pure returns (uint256) {
            uint256 covBefore = cash.wdiv(liability);
            uint256 covAfter;
            if (addCash) {
                covAfter = (cash + cashChange).wdiv(liability);
            } else {
                covAfter = (cash - cashChange).wdiv(liability);
            }
    
            // if cov stays unchanged, slippage is 0
            if (covBefore == covAfter) {
                return 0;
            }
    
            uint256 slippageBefore = _slippageFunc(k, n, c1, xThreshold, covBefore);
            uint256 slippageAfter = _slippageFunc(k, n, c1, xThreshold, covAfter);
    
            if (covBefore > covAfter) {
                return (slippageAfter - slippageBefore).wdiv(covBefore - covAfter);
            } else {
                return (slippageBefore - slippageAfter).wdiv(covAfter - covBefore);
            }
        }
    
        /**
         * @notice Yellow Paper Def. 2.5 (Swapping Slippage). Calculates 1 - (Si - Sj).
         * Uses the formula 1 + (-Si) - (-Sj), with the -Si, -Sj returned from _slippage
         * @dev Adjusted to prevent dealing with underflow of uint256
         * @param si -si slippage parameter in WAD
         * @param sj -sj slippage parameter
         * @return The result of swapping slippage (1 - Si->j)
         */
        function _swappingSlippage(uint256 si, uint256 sj) internal pure returns (uint256) {
            return WAD + si - sj;
        }

    Decreasing Liability but Not Asset balance manipulates slippage

    LESSONS LEARNED

    The more complex a protocol’s financial algorithms are, the more difficult it is to protect from design deficiencies being exploited. Platypus emphasized the ability to maintain a very low slippage and one-sided deposits, which are very hard to implement in a decentralized manner. The Platypus Finance Hack was a difficult one to follow, but there are still ways to improve the security of protocols like these.

    One way we could help with similar protocols is by increasing the security posture, in multiple ways. Note that this attacker was relatively smart, and bypassed the mempool when exploiting the protocol, which renders simple mempool scanning techniques ineffective. At the same time the attacks on different pools happened in different transaction. Some vaults could have been paused more quickly had a sophisticated monitoring solution like Watchdog been employed. Finally the protocol’s financial design & calculations are to blame here, employing the services of specialist security firms like ours to conduct financial design audits could have prevented this attack.

    The attacker themselves also made a mistake in the attack, and the Platypus team rescued $575 (such funds were transferred to 0x068e297e8ff74115c9e1c4b5b83b700fda5afdeb).

    We wish the Platypus team good luck in getting the protocol up and running again, and recovering from this serious incident.

  • Preparing for Your First Web3 Audit

    Preparing for Your First Web3 Audit

    Your project is at an advanced state of engineering and you have decided to hire an auditor to maximize security and legitimacy. Great decision! However, not all audit experiences are equal, so follow this guide to maximize your mileage.

    During auditing, you are employing security consultants to go over your code. For auditors, studying your code and issuing an Web3 audit report is a complex balance. Auditors need to have some level of confidence to sign off on your project, yet they will not spend infinite time to gain this confidence. The time allotted has typically been scoped based on your code size and apparent complexity, using extensive past experience.

    Unpleasant Truth #1: At the end of an Web3 audit, no professional auditor will ever claim 100% confidence. Sometimes the auditor will explicitly assess that they do not have as high confidence as they would like, but the time they can allocate is simply over!

    To enhance the auditor’s confidence and facilitate their work:

    1. Provide Succinct, But Sufficient, Documentation: Before auditors start their work, provide them with clear and comprehensive documentation that explains the intent and design of your project. This is not merely about code specifics, but should also provide an understanding of the project at a high level. Of course, when one understands both levels, it should match the specifics.
    2. Use Consistent Naming and Comments: Try to use consistent variable and function names to explicitly document the intent of the code. Use comments to document complex parts of the code but also make sure these are consistent with the code. If you can write invariants or explain the intent of complex parts, this can go a long way.
    3. Set Up a Communication Channel: Establish a synchronous communication channel between your team and the auditors to facilitate information sharing. If requested, walk the auditors through your code before they start the Web3 audit or when they prefer to (e.g., after they have read the documentation, or after one pass over the code). Be responsive during the audit period.
    4. Ensure the Project is Fully Tested and Compiles: By the time an audit begins, make sure the project compiles without errors and is fully tested. This will allow the auditors to focus on the difficult parts of the code, rather than discovering that some functions are uncallable or do not do what they are expected to do under straightforward inputs. Deploy your code on a testnet and exercise it to its limits, focusing on unexpected, corner-case, and possibly adversarial behavior.
    5. Understand the Limitations of an Audit: An Web3 audit is not a testing service or a magical way to find all bugs. Auditors will not know your specification (i.e., your mathematical formulas, your desired behavior) if you do not clearly communicate it. Auditing will likely miss violations of functional correctness when the definition of correct calculations is not given.
    6. Focus on Adversarial Environmental Conditions: Auditors’ time is best spent thinking about adversarial environmental conditions and not simply uncommon inputs. The latter is a functional correctness issue, best uncovered via testing.

    Unpleasant Truth #2: Auditing sometimes misses issues that the programmers themselves find. This is not surprising. The auditor is not the owner of your code. You are!

    Auditors will think about all issues to the best of their ability, but, in the end, they are only “involved”, not “committed” to your project. Therefore, you will benefit the most from an audit if you have the right frame of mind. You are the owner. Your project will face an adversarial world. You have the final word, the final command. Thankfully, good people are here to help you. Help them help you. And magnify their help. If an issue is found, ask yourself: how did I miss it? How does it generalize? What other analogous issues may I have missed?

    Web3 audit

    We are looking forward to working with you to best secure your project! Request an audit.

  • I See Dead Code

    I See Dead Code

    What if I told you that over one-third of recently-deployed Ethereum smart contracts consist mostly of unusable junk?

    Dead Code

    We recently identified a bug in the solidity compiler, resulting in the inclusion of dead code in the deployed bytecode of contracts.

    What we did not know (and did not expect!) was how pervasive the bug was, affecting (almost certainly) many tens of thousands of contracts and the majority of their deployed bytecode.

    We discovered the bug when evaluating Gigahorse, our open source binary-lifter underpinning the decompiler and analyzer at https://library.dedaub.com/, οn recently deployed contracts.
    We originally reported this bug back in November. Following the Solidity team’s acknowledgement of the issue, we performed experiments at scale to investigate the impact.

    We had no idea…

    Issue description

    The bug manifests itself when library methods are only called by a contract’s constructor. The EVM bytecode produced by these methods should only be part of the contract’s creation bytecode (initcode). However, the low-level code implementing these “dead” library methods makes it to the contract’s runtime bytecode, increasing its size for no benefit.

    Simple real world example

    Looking at an EIP1967 proxy contract deployed at Ethereum address 0x5bbb007b32828f4550cd2a3d16c8dbe3c555ef90. The decompilation output of Dedaub’s contract library shows what the on chain code really does:

    // Decompiled by library.dedaub.com
    // 2023.01.16 02:06 UTC
    
    // Data structures and variables inferred from the use of storage instructions
    address _fallback; // STORAGE[0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc] bytes 0 to 19
    
    
    function () public payable { 
        v0 = _fallback.delegatecall(MEM[0 len msg.data.length], MEM[0 len 0]).gas(msg.gas);
        require(v0); // checks call status, propagates error data on error
        return MEM[0 len (RETURNDATASIZE())];
    }
    
    function implementation() public nonPayable { 
        return _fallback;
    }
    
    // Note: The function selector is not present in the original solidity code.
    // However, we display it for the sake of completeness.
    
    function __function_selector__(bytes4 function_selector) public payable { 
        MEM[64] = 128;
        if (msg.data.length < 4) {
            if (!msg.data.length) {
                ();
            }
        } else if (0x5c60da1b == function_selector >> 224) {
            implementation();
        }
        v0 = _fallback.delegatecall(MEM[0 len msg.data.length], MEM[0 len 0]).gas(msg.gas);
        require(v0); // checks call status, propagates error data on error
        return MEM[0 len (RETURNDATASIZE())];
    }

    Our lifter reports that 30 of the 46 low-level code blocks can never be executed when transacting with the contract. By closely inspecting the results we realized that the call to Address.functionDelegateCall() at line 260 (of the source code) results in the code of library methods declared in lines 170, 180, 195. 36, 231 being part of the deployed bytecode although they are never used in the logic of the runtime code.
    We can confirm the issue by hand-tweaking the source code, declaring the library methods inside the compiled contract instead of a library. As a result the deployed bytecode size drops from 808 to 298 bytes.

    Impact on deployed contracts

    We assembled a total of 10,000 unique bytecodes of contracts compiled using solc 0.8.17 deployed on the Ethereum mainnet. Analyzing them using gigahorse we can tell that at least 35% of the above have some dead code and 33% of them have dead code taking up the majority of their runtime bytecode! These results are dominated by manifold NFT proxies but other proxy contracts also face the same issue.

    The most extreme example we found was a contract using the diamond proxy pattern. Its deployed bytecode is 4908 bytes, but removing the dead libraries results in a bytecode of just 302 bytes!

    On large contracts, thankfully, the impact of this issue is usually negligible. But most deployed contracts by number are small contracts, and they are heavily burdened by dead code.

    And this was a surprise, to say the least.

  • Poly Network Hack Postmortem

    Poly Network Hack Postmortem

    On July 2nd, 2023 06:47:20 PM UTC Poly Network suffered what was initially reported to be a notional $34b hack (the actual realized amounts were far less, due to most of the tokens being illiquid). The Poly team paused their smart contracts EthCrossChainManager on several chains, most notably on Metis, BSC and Ethereum. After our team reconstructed the attack, we concluded that the root cause was not a logical bug on the smart contract, but, most likely, stolen (or misused) private keys of 3 out of 4 of Poly network’s keepers (off-chain systems controlled by the team). In order to understand how the attack took place, we need to understand the architecture of Poly’s cross-chain managers.

    Poly runs a network of cross-chain management contracts, allowing tokens to be “transferred” from an origin chain to a destination chain. These contracts accept proofs of token transfer changes on the origin chain, together with encoded arguments for a transaction that withdraws these tokens on the current chain.

    function verifyHeaderAndExecuteTx(bytes memory proof, bytes memory rawHeader, bytes memory headerProof, bytes memory curRawHeader,bytes memory headerSig) whenNotPaused public returns (bool)
    // proof = Poly chain tx merkle proof
    // rawHeader = The header containing crossStateRoot to verify the above tx merkle proof
    // headerSig = The converted signature variable for solidity derived from Poly chain's keepers

    Main entry point allowing users to “unlock” tokens on the “destination” chain that were “locked” on the origin chain

    In Poly, the operation to transfer tokens from the origin chain is referred to as “lock” and the function to retrieve the tokens is referred to “unlock”. Poly employs a system of so-called “consensus nodes” that are essentially EOAs that sign off on the “unlock” event on the destination chain, by including relevant entropy from the origin chain confirming the lock event. This entropy consists of a state root reflecting the locked tokens on the origin chain. Here’s the relevant code which checks that the “header” structure was correctly signed by the “consensus nodes”. The header contains the state root of a Merkle tree. Since the entire header is signed, so is the state root, and, by extension the entire state as witnessed by the Merkle tree.

    function verifySig(bytes memory _rawHeader, bytes memory _sigList, address[] memory _keepers, uint _m) internal pure returns (bool){
            // (Dedaub comment)        
            //_rawHeader = 0x0000000000000000000000001e8bb7336ce3a75ea668e10854c6b6c9530dab7...
            //_sigList = // List of 3 signatures from 0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825,0x4c46e1f946362547546677Bfa719598385ce56f2,0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0
            //_keepers = ["0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825","0x4c46e1f946362547546677Bfa719598385ce56f2","0xF81F676832F6dFEC4A5d0671BD27156425fCEF98","0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0"]
            //_m = 3
            
            bytes32 hash = getHeaderHash(_rawHeader);
    
            uint sigCount = _sigList.length.div(POLYCHAIN_SIGNATURE_LEN);
            address[] memory signers = new address[](sigCount);
    
            // (Dedaub comment)
            //   signers = [
            //     0x4c46e1f946362547546677Bfa719598385ce56f2,
            //     0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825,
            //     0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0
            // ]
            bytes32 r;
            bytes32 s;
            uint8 v;
            for(uint j = 0; j  < sigCount; j++){
                r = Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN, 32));
                s =  Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN + 32, 32));
                v =  uint8(_sigList[j*POLYCHAIN_SIGNATURE_LEN + 64]) + 27;
                signers[j] =  ecrecover(sha256(abi.encodePacked(hash)), v, r, s);
                if (signers[j] == address(0)) return false;
            }
            return Utils.containMAddresses(_keepers, signers, _m);
        }

    Function to verify signed header, which contains the very-important state root. Comments added by Dedaub.

    Our team verified that the code above was correctly invoked and that the header was indeed signed by 3 of the centralized keepers, satisfying the (k-1) out of k keeper signature scheme. We also checked that the list of keepers was not modified prior to the attack. Indeed, over the span of 2 years, the list of keepers remains unchanged and consists of 4 EOAs. It is common for decentralized protocols to employ “keepers”, i.e., external systems controlled by the development team, that feed vital information into the smart contracts. This is sometimes necessary since smart contracts cannot operate autonomously, and need to be invoked externally. What’s less common, however, is to rely on 3 keepers for the end-to-end security in a high TVL cross-chain bridge.

    Continuing with our investigation, assuming the attacker did not have control over 3 of the EOAs, the Merkle prover would have been the likely cause of a logical bug in the smart contracts. We therefore looked into this next.

    /* @notice                  Verify Poly chain transaction whether exist or not
        *  @param _auditPath        Poly chain merkle proof
        *  @param _root             Poly chain root
        *  @return                  The verified value included in _auditPath
        */
        function merkleProve(bytes memory _auditPath, bytes32 _root) internal pure returns (bytes memory) {
            uint256 off = 0;
            bytes memory value;
            //_auditPath = 0xef20a106246297a2d44f97e78f3f402804011ce360c224ac33b87fe8b6d7b7e618c306000000000000002000000000000000000000000000000000000000000000000000000000000382fc20114c912bcc8ae04b5f5bd386a4bddd8770ae2c3111b7537327c3a369d07179d6142f7ac9436ba4b548f9582af91ca1ef02cd2f1f03020000000000000014250e76987d838a75310c34bf422ea9f1ac4cc90606756e6c6f636b4a14cd1faff6e578fa5cac469d2418c95671ba1a62fe14e0afadad1d93704761c8550f21a53de3468ba5990008f882cc883fe55c3d18000000000000000000000000000000000000000000
            (value, off)  = ZeroCopySource.NextVarBytes(_auditPath, off);
    
            bytes32 hash = Utils.hashLeaf(value);
            uint size = _auditPath.length.sub(off).div(33);
            bytes32 nodeHash;
            byte pos;
            for (uint i = 0; i < size; i++) {
                (pos, off) = ZeroCopySource.NextByte(_auditPath, off);
                (nodeHash, off) = ZeroCopySource.NextHash(_auditPath, off);
                if (pos == 0x00) {
                    hash = Utils.hashChildren(nodeHash, hash);
                } else if (pos == 0x01) {
                    hash = Utils.hashChildren(hash, nodeHash);
                } else {
                    revert("merkleProve, NextByte for position info failed");
                }
            }
            require(hash == _root, "merkleProve, expect root is not equal actual root");
            return value;
        }

    Poly Network Hack | Merkle prover of the Poly chain

    The Merkle prover above takes as input a byte sequence (_auditPath) containing the leaf node followed by a path through the Merkle tree that proves the existence of the leaf node, given the state root (_root). Remember, this state root has already been signed by the keepers. In case you’re unfamiliar how Merkle trees work, the picture below depicts a Merkle tree, which is a cryptographically-secure data structure. The key of the algorithm rests on the fact that the root of the Merkle tree contains (transitively) entropy from all the leaf nodes in the tree. Therefore a proof (often called “witness”) can be easily constructed, and cheaply verified. We only need to trust the root of the tree, and if it’s trusted, so is anything else verified by a Merkle tree witness.

    Poly Network Hack

    The kicker here is that in order to simplify the exploitation scenario, the attacker made full use of the flexibility afforded by the verifier’s implementation. It turns out that the verifier allows for zero-length witnesses. Essentially, the attacker passed in the leaf node, which is exactly 240 bytes in this case, and an empty path as a proof. As it turns out in this case, the hash of the leaf node needs to correspond to the state root (hash) in order for this proof to succeed. This further adds merit to the hypothesis that the Poly chain keepers were likely compromised and signed a state root that turned out to be artificially constructed. The only information within it contained an unlock command that sends tokens to the attacker.

    It is unfortunate to note that Poly network had been previously attacked by a greyhat hacker almost two years ago.

    Finally, it took Poly network 7 hours to react to today’s attack, and in the meantime the attacker had orchestrated several transactions on multiple chains to exploit this.

    Despite the narrative above, there is no definitive proof so far that the keys were stolen. It could have been a rugpull, or it could have been compromised off-chain software running on 3 out of 4 of the keepers. The effect is the same, as far as we can observe. What appears to be unequivocal in the Poly network hack is the fact that a logical bug was not exploited in the smart contracts carrying out the token transfers and that the keepers signed a maliciously-crafted proof. If indeed the Poly network developers confirm the attack has to do with compromised signature keys, as is likely the case, this brings to question the suitability of centralized bridges controlling so much funds. The attack also suggests less-than-perfect monitoring by the Poly network team of the underlying bridge. Had the protocol been set up with a fast monitoring solution, such as Dedaub Watchdog, this would have significantly reduced the reaction time and possibly saved some funds.