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

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 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 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.

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

Dedaub Security Suite (former Watchdog) is not just a tool; it’s 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

In the ever-changing field of Smart Contract security, proactivity is key. 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; 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: 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.

Ethereum improvement proposal 4788 | EIP-4877 Summary

Dedaub was commissioned by the Ethereum Foundation to perform a security audit of the bytecode of a smart contract that was introduced to the EIP-4877 in a recent change, enabling the on-chain storing and accessing of the beacon block roots of recent blocks.

In this blog post, titled “EIP-4877 summary,” we highlight key insights from the audit. You can access the complete report here.

The audited contract uses the block’s timestamp as a key for their parent beacon blocks’ roots. To bind the contract’s storage footprint while retaining accurate information, a set of two ring buffers are used (using a HISTORY_BUFFER_LENGTH with a value of 98304):

  1. The first one stores the timestamp (i.e. the key) and is used to ensure that the result for the provided timestamp is the one that is currently stored on-chain. Its value will be stored at storage location timestamp % HISTORY_BUFFER_LENGTH.
  2. The second one is used to store the beacon root chain value for the timestamp. Ιts value will be stored at storage location HISTORY_BUFFER_LENGTH + timestamp % HISTORY_BUFFER_LENGTH.

The audited contract implements two methods, set() and get(). As the contract does not adhere to the contract ABI specification, the method to be executed is chosen based on the contract’s caller; if called by the special address 0xfffffffffffffffffffffffffffffffffffffffe, the set() function is called, while every other address the calls the get() function. Both methods accept the first 32 bytes of the call’s calldata as their arguments.

The highest severity vulnerability found during this audit is one where calling the get() function. More specifically, if this is called with a value of zero it will not fail and return the zero value back.

// get()
require(msg.data.length == 32);
require(calldata0_32 == STORAGE[calldata0_32 % 0x18000]);
return STORAGE[0x18000 + calldata0_32 % 0x18000];

EIP-4877 Summary | Main body of the get() function

Although this does not affect the contract’s functionality for valid timestamps it can potentially lead to misuse, and funds stolen in projects that rely on this root to exist and valid. Therefore we suggested adding a special case for the zero value, in the get() function or invalidating it by storing a value in the 0th storage slot during the contract’s construction.

You can read the full audit report here.

EIP-4877 Summary

Ethereum Study – Rlp to Ssz Mpt Commitment Migration

The Ethereum Foundation commissioned our team to examine the potential impact of Ethereum Improvement Proposals (EIPs) 6404 and 6466. These EIPs propose the modification of Merkle-Patricia Trie (MPT) commitments for transactions and receipts, respectively. Importantly, this entails a change in the serialization algorithm, from Recursive Length Prefix (RLP) format to the Simple Serialize (SSZ) format for the Receipts and Transactions containers. In turn, this changes the Receipts Root and Transactions Root fields in the execution layer headers.

A primary concern is that this transition could disrupt contracts that rely on RLP for proofs on data committed to the Ethereum mainnet. These contracts may include critical parts of decentralized bridges, which generate proofs about some log that was emitted in historical transactions.

EIPs 6404 and 6466 | This research seeks to quantify and qualify the extent of potential disruption caused by these changes. Identifying the specific on-chain patterns that verify commitments in this manner represents a significant challenge, necessitating a semi-automated examination of all smart contracts deployed on the Ethereum network, together with their recent behavior. The study also attempts to identify which projects these contracts are part of, and whether actions can be taken, on-chain (such as upgrading) or off-chain (such as modifying their respective oracles) to limit the impact of these changes.

For the proposed EIPs, we were able to measure the extent of the impact of these changes. The effects are observed on a handful of known projects, all of which are cross-chain bridges.

Notably, many other protocols that do employ RLP functionality are not affected. For instance the Optimism and Polygon bridges use RLP operations for inclusion proofs when bridging from L2 networks back to Ethereum, and, thus, are not affected by the Ethereum encoding of transactions.

Project NameWebsiteEstimated Impact
zkBridgehttps://zkbridge.comModerate
LayerZerohttps://layerzero.network/Moderate
Telepathyhttps://docs.telepathy.xyz/Moderate

Finally, an interesting result of our study is that out of the two proposed EIPs, only EIP-6466 (Receipts Root EIP) was observed to have an impact on the inspected protocols. This makes sense as log-inclusion proofs are probably the most common way to conduct cross-chain message passing.

EIPs 6404 and 6466


Read the rest of the study here.

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.

EIP-4758 and EIP-6780 | Removal of Selfdestruct

Dedaub was commissioned by the Ethereum Foundation to perform an impact study of Ethereum Improvement Proposals (EIPs) 4758 and 6780 on existing contracts. EIP-4758 proposes to deactivate SELFDESTRUCT by changing it to SENDALL, which recovers all funds (in ETH) to the beneficiary without deleting any code or storage. On the other hand, EIP-6780 modifies SELFDESTRUCT to work only in the same transaction in which the contract was created, while in all other cases it recovers all funds but does not delete any other account data.

The aim of this study is (i) to help the Ethereum community decide whether to implement, based on the impact of these changes to the ecosystem, EIP-4758 or EIP-6780. In either case we also aimed to (ii) find out which projects are affected and by how much. To evaluate the impact of these proposed changes, we performed comprehensive queries over past on-chain behaviors of smart contacts and queries on code and bytecode of deployed contracts; inspected code manually; checked balances, approvals, and contract proxying state; and informally interviewed developers.

The study found that a small number of known projects and many smart contracts, mainly involved in Miner Extractable Value (MEV) bot networks, would be affected. Quantitatively, over 98% of SELFDESTRUCT-CREATE2 pairs in known contracts would remain unaffected if EIP-6780 is implemented, while the impact of EIP-4758 is less certain. Metamorphic contracts used for upgrades were found to be rare. If implemented today, EIP-4758 could affect some functionalities of certain projects, including AxelarNetwork, Pine Finance, Revest, and JPEGd (with high impact), and Sorbet Finance, Celer, Gelato, Ricmoo’s Wisps, Chainhop Protocol (with low impact), and Thousand Ether Homepage (with moderate impact). However, most of these projects could be upgraded in time for a deployment of the proposed EIPs.

Based on this, we judged the impact of EIP-4758 and EIP-6780 to be manageable and could be a net positive due to the simplification of Ethereum Clients’ implementations, especially if EIP-6780 is selected.

EIP-4758 and EIP-6780 | Removal of Selfdestruct

The study used dynamic analysis and static program analysis, along with smart contract inspection and transaction debugging.


Read more

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.