Index of cyberattacks (from recent to oldest):

For scams and known exploits in Ethereum smart contracts check the next link out:

Etherscan exploit label: https://etherscan.io/accounts/label/exploit


💣 ShadowFi $300,000 Hack (2022)

Due that burn function was left as public, an attacker burned 10.3 Million SDF tokens worth of $300,000.

https://bscscan.com/address/0x10bc28d2810dD462E16facfF18f78783e859351b#code

# Line 962 (ShadowFiToken.sol)

    function burn(address account, uint256 _amount) public {
        _transferFrom(account, DEAD, _amount);

        emit burnTokens(account, _amount);
    }

💣 Curve.fi Frontend $570,000 Hack (2022)

Curve.fi webserver got hacked and the attacker was able to change the smart contract address to a malicious one, siphoning $570,000 from users that didn’t realize the hack.

References:

https://decrypt.co/107120/ethereum-defi-exchange-curve-frontend-hack-hijack

💣 Nomad bridge $190 Million hack (2022)

An update to the protocol made possible to forge messages that weren’t yet processed by the Replica contract.

References:

💣 Solana Wallet Slope $4.1 Million stolen from user wallets (2022)

It seems Slope wallet was storing private keys in its analytics service and somebody got access to it, stealing funds from 9,231 wallets and siphoning $4.1 Million in user funds. Phantom also got affected by the users who imported their wallets from Slope as their private key were already compromised.

Later on, sentry also add privatekey|private_key in a blacklist to not store anything containing that variable. (It is not clear yet if they also added mnemonic… to avoid future mistakes).

References:

💣 AUDIO $18 Million hack (2022)

Audacious governance contract had an internal storage collision hack:

💣 OLA $4.67 Million reentrancy hack (2022)

On 31st March, Lending network Voltage Finance, powered by Ola Finance, was attacked taking advantage of the re-entrancy bug, the attacker was then able to remove the collateral without first paying back the loan.

References:

💣 INV $15.6 Million price manipulation through keep3r oracle (2022)

Pouring $3 Million USD into INV to soar the prices in SushiSwap, they got tons of loans using INV as collateral in ANC Market Maker which uses keep3r as price oracle.

Not really they took advantage of a smart contract vulnerability, but they just stressed the market to force a situation, just as happened in the 2010 market flash crash.

💣 BAYC Discord compromise and phishing (2022)

Bored Apes Yacht club and other NFTs projects got their Discord servers compromised on April 1st 2022 due to a vulnerable version of Ticket Tool bot tool.

Using an exploit in Ticket Tool bot, the attacker gained admin access and was able to post a message as the official admin and sending a fraudulent cheap mint phishing site:

As usually happens the domain used in the phishing attack was registered just the same day of the attack:

Ticket Tool Official Statement

💣 Ronin Bridge $625 Million Hack (2022)

Ronin is the Ethereum-based sidechain that powers Axie Infinity NFT game (Smart contract address: 0x1A2a1c938CE3eC39b6D47113c7955bAa9DD454F2).

The exploit and draining of funds started on March 23 but wasn’t discovered until March 29 when a user was not able to withdraw 5k ETH from the bridge. Five validator private keys were hacked; 4 Sky Mavis validators and 1 Axie DAO.

Most probably by some sort of social engineering engineering attack, they compromised 5 out of 9 Ronin validators being able to transfer $625 million of funds out from the bridge to this wallet 0x098B716B8Aaf21512996dC57EB0615e2383E2f96.

It seems the precedents of the attack were placed in November 2021, when Sky Mavis loosen his security to absorb a huge spike on active users, using a gas-free RPC validator that was used as part of the attack, as the Axie DAO validator IP was still on the allowlist. Once the $RON token was deployed the gas-free RPC validators were no longer necessary, but Sky Mavis forgot to remove them.

The two fraudulent transactions:

The funds are being washed through service Tornado.cash as shown in this tx: 0x284c64dc6788dc7bcdaaee2eee20c430e0860904fb3b1ab11147767f896084e1 where 2,018.23529 Ether ($6,577,872.82) are being sent to Tornado.cash in batches of 100 ETH (April 7th 2022).

References:

💣 Poly Network Access Control $610 Million hack (2021)

Poly Network Bridge offers interoperability between multiple blockchains: https://bridge.poly.network/

Extract from https://slowmist.medium.com/the-root-cause-of-poly-network-being-hacked-ec2ee1b0c68f

The details of the attack

1. The core of this attack is that the verifyHeaderAndExecuteTx function of the EthCrossChainManager contract can execute specific cross-chain transactions through the _executeCrossChainTx function.

2. Since the owner of the EthCrossChainData contract is the EthCrossChainManager contract, the EthCrossChainManager contract can modify the keeper of the contract by calling the putCurEpochConPubKeyBytes function of the EthCrossChainData contract.

3. The verifyHeaderAndExecuteTx function of the EthCrossChainManager contract can perform user-specified cross-chain transactions by calling the _executeCrossChainTx function internally. So the attacker only needs to pass in the carefully constructed data through the verifyHeaderAndExecuteTx function for the _executeCrossChainTx function to execute the call to the EthCrossChainData contract PutCurEpochConPubKeyBytes function to change the keeper role to the address specified attackers.

4. After replacing the address of the keeper role, the attacker can construct a transaction at will and withdraw any amount of funds from the contract.

To call the vulnerable function is a little bit tricky as the attacker had to found out, the valid sighash value to trigger the method:

<function name>(<function input types>)

>http://ethers.utils.id('putCurEpochConPubKeyBytes(bytes)').slice(0, 10)
'0x41973cd9'

> http://ethers.utils.id('f1121318093(bytes,bytes,uint64)').slice(0, 10)
'0x41973cd9'

References:

💣 Wormhole Qbridge $324 Million hack (2022)

QubitFin’s bridge contract, QBridge, had two main problems:

  1. safeTransferFrom without proper address check
  2. 0x00 explicitly whitelisted

The hacker called deposit() in the QBridge ETH contract without really making any deposit and emitted the Deposit event The exploit was caused by tokenAddress.safeTransferFrom in QBridgeHandler.sol which didn’t revert the transaction when tokenAddress is the 0x00. [Adapted from: https://twitter.com/CertiKCommunity/status/1486892063006334982]

References:

  • Patch before the attack:

https://github.com/certusone/wormhole/commit/7edbbd3677ee6ca681be8722a607bc576a3912c8#diff-0d27d8889edd071b86d3f3299276882d97613ad6ab3b0b6412ae4ebf3ccd6370R92

  • Vulnerable smart contract version (tag v2.7.2):
[email protected]:wormhole$ git checkout tags/v2.7.2
Previous HEAD position was 9ed71c00 node: terra reobservation
HEAD is now at f829195e node/pkg/solana: kill recovery feature

[email protected]:~/SCDB/wormhole$ git branch
* (HEAD detached at v2.7.2)
  dev.v2
Test your blockchain knowledge with Learn Blockchain Security

💣 Ethereum splitDAO reentrancy attack (2016)

The DAO was launched on 30 April 2016 and attacked on June 17th. The exploit resulted in almost 33% of funds were siphoned to another contract and later some funds were withdrawn using either Grin or Wasabi Wallet.

The impact was so huge in the Ethereum blockchain that after some debate, the community decided to do a hard fork to reverse the attack transactions and return the funds to the investors. The old untouched blockchain still remains under the token Ethereum Classic (ETC).

The origin of the issue was in the function splitDAO of the DAO smart contract. It was able to send funds before updating the values, what is called as reentrancy attack.

    function splitDAO(
        uint _proposalID,
        address _newCurator
    ) noEther onlyTokenholders returns (bool _success) {

        Proposal p = proposals[_proposalID];

        // Sanity check

        if (now < p.votingDeadline  // has the voting deadline arrived?
            //The request for a split expires XX days after the voting deadline
            || now > p.votingDeadline + splitExecutionPeriod
            // Does the new Curator address match?
            || p.recipient != _newCurator
            // Is it a new curator proposal?
            || !p.newCurator
            // Have you voted for this split?
            || !p.votedYes[msg.sender]
            // Did you already vote on another proposal?
            || (blocked[msg.sender] != _proposalID && blocked[msg.sender] != 0) )  {

            throw;
        }

        // If the new DAO doesn't exist yet, create the new DAO and store the
        // current split data
        if (address(p.splitData[0].newDAO) == 0) {
            p.splitData[0].newDAO = createNewDAO(_newCurator);
            // Call depth limit reached, etc.
            if (address(p.splitData[0].newDAO) == 0)
                throw;
            // should never happen
            if (this.balance < sumOfProposalDeposits)
                throw;
            p.splitData[0].splitBalance = actualBalance();
            p.splitData[0].rewardToken = rewardToken[address(this)];
            p.splitData[0].totalSupply = totalSupply;
            p.proposalPassed = true;
        }

        // Move ether and assign new Tokens
        uint fundsToBeMoved =
            (balances[msg.sender] * p.splitData[0].splitBalance) /
            p.splitData[0].totalSupply;
        if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false)
            throw;


        // Assign reward rights to new DAO
        uint rewardTokenToBeMoved =
            (balances[msg.sender] * p.splitData[0].rewardToken) /
            p.splitData[0].totalSupply;

        uint paidOutToBeMoved = DAOpaidOut[address(this)] * rewardTokenToBeMoved /
            rewardToken[address(this)];

        rewardToken[address(p.splitData[0].newDAO)] += rewardTokenToBeMoved;
        if (rewardToken[address(this)] < rewardTokenToBeMoved)
            throw;
        rewardToken[address(this)] -= rewardTokenToBeMoved;

        DAOpaidOut[address(p.splitData[0].newDAO)] += paidOutToBeMoved;
        if (DAOpaidOut[address(this)] < paidOutToBeMoved)
            throw;
        DAOpaidOut[address(this)] -= paidOutToBeMoved;

        // Burn DAO Tokens
        Transfer(msg.sender, 0, balances[msg.sender]);
        withdrawRewardFor(msg.sender); // be nice, and get his rewards
        totalSupply -= balances[msg.sender];
        balances[msg.sender] = 0;
        paidOut[msg.sender] = 0;
        return true;
    }

References:

💣 King of the Ether Throne (KotET) DOS on gas fees (2016)

King of the Ether Throne was a popular game in 2016 where you could send Ether to the smart contract address to become the new King. The prerequisite was to send more Ether that the previous King sent in order to dethrone him. Also the new King paid a compensation to the previous King. If nobody claimed the throne in 14 days, the game was reset.

The main problem was that the payment to the dethroned King was sent to an Ethereum mist contract-based wallet instead of a contract account which cost more than the maximum 2600 gas fee, actually the gas fee was 10,000 Gwei. So the payment was unsuccessful and the Ether returned to the contract address, getting stuck until the gas limit was expanded and address 0xb2afec1da55c15ad57b3310f9008c47f4e028de3 was able to claim 101.30877 Ether on February 19th 2016 09:51:14 PM +UTC.

References:

💣 GovernMental Jackpot Timestamp based hack (2016)

GovernMental was a ponzi scheme very popular in 2016. It rewarded the last player to join for at least one minute it accumulated 1,100 ETH.

It suffered of two security problems: the first was a DOS due of how the creditors list was structured gas price was increasing for each new player until the gas price surpassed the limit of a block, blocking 1,100 ETH (tx id 0x0d80d67202bd9cb6773df8dd2020e7190a1b0793e8ec4fc105257e8128f0506b) until the gas price limit was modified (June 17th 2016).

The second problem was that a miner could modify the timestamp to fake that he joined somewhere in the future and become the winner.

    function lendGovernmentMoney(address buddy) returns (bool) {
        uint amount = msg.value;
        // Check if the system already broke down. If for 12h no new creditor gives new credit to the system it will brake down.
        // 12h are on average = 60*60*12/12.5 = 3456
        if (lastTimeOfNewCredit + TWELVE_HOURS < block.timestamp) {
            // Return money to sender
            msg.sender.send(amount);
            // Sends all contract money to the last creditor
            creditorAddresses[creditorAddresses.length - 1].send(profitFromCrash);
            corruptElite.send(this.balance);
            // Reset contract state
            lastCreditorPayedOut = 0;
            lastTimeOfNewCredit = block.timestamp;
            profitFromCrash = 0;
            creditorAddresses = new address[](0);
            creditorAmounts = new uint[](0);
            round += 1;
            return false;
        }

References:

💣 PancakeSwap Lottery Frontrunning Vulnerability (2021)

Protections against frontrunning existed for the buy method, but didn’t exist for the multibuy method.

Patch:

function multiBuy(uint256 _price, uint8[4][] memory _numbers) external {
require (!drawed(), ‘drawed, can not buy now’);
require(!drawingPhase, ‘drawing, can not buy now’);  # <<<<------ FIX
require (_price >= minPrice, ‘price must above minPrice’);
uint256 totalPrice = 0;`

References:


How useful was this post?

Click on a star to rate it!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Leave a Reply