Zokyo Auditing Tutorials
  • 🔐Zokyo Auditing Tutorials
  • 📚Tutorials
    • 🏃Tutorial 1: Front-Running
      • 🚀Prerequisites
      • 📘Understanding Front-Running
      • 👓Examples
      • ⚒️Mitigation Steps
      • 🏦Resource Bank to more front running examples
      • 🤝Front-Running Conclusion
    • 🧱Tutorial 2: Unsafe Casting
      • 🚀Prerequisites
      • 📘Understanding Casting
      • 👓Examples
      • 🤝Unsafe Casting Conclusion
    • 👍Tutorial 3: Approvals and Safe Approvals
      • 🚀Prerequisites
      • 📘Understanding Approvals
      • 👓Vulnerability Examples
        • 🔁ERC20 Approval Reset Requirement
        • 😴Ignoring Return Values from ERC20 approve() Function: Potential Miscount of Successful Approvals
        • 🚫Improper use of Open Zeppelins safeApprove() for Non-zero Allowance Increments
        • 🥾Omitted Approval for Contract Interactions Within a Protocol
        • 🤦‍♂️Failing to Reset Token Approvals in Case of Failed Transactions or other actions
        • 💭Miscellaneous
        • ERC20 Approve Race Condition Vulnerability
      • ⚒️Spot the Vulnerability
      • 🤝Approvals and Safe Approvals Conclusion
    • ⛓️Tutorial 4: Block.chainid, DOMAIN_SEPARATOR and EIP-2612 permit
      • 🚀Prerequisites
      • 📘Understanding Block.chainid and DOMAIN_SEPARATOR
      • 👓Examples
      • ⚒️General Mitigation Steps
      • 🤝Tutorial 4 Conclusion
  • 💰Tutorial 5: Fee-On-Transfer Tokens
    • 🚀Prerequisites
    • 📘Understanding Fee-On-Transfer
    • 👓Examples
    • 📘Links to more fee-on-transfer vulnerability examples
    • 🤝Fee-On-Transfer Tokens: Conclusion
  • 🌴Tutorial 6: Merkle Trees
    • 🚀Prerequisites
    • 📘Understanding Merkle Trees
    • 🔎Verification within a Merkle Tree:
    • 📜Merkle Proofs Within Smart Contracts
    • 🖋️Merkle Proof Solidity Implementation
    • 🛑Vulnerabilities When Using Merkle Trees
    • 💀Example Vulnerabilities
    • 🧠Exercise
    • 🤝Merkle Trees Conclusion
  • 🌳Tutorial 7: Merkle-Patricia Trees
    • 🚀Prerequisites
    • 📘Understanding Merkle-Patricia Trees
    • 📕Understanding Merkle-Patrica Trees pt.2
    • 🔎Verification within a Merkle-Patricia Tree
    • 🛑Vulnerabilities When Using Merkle-Patricia Trees
    • 💀Example Vulnerability
    • 🤝Merkle-Patricia Trees: Conclusion
  • 🔁Tutorial 8: Reentrancy
    • 🚀Prerequisites
    • 📘Understanding Reentrancy
    • ⚒️Mitigation
    • 💀The DAO Hack: An In-depth Examination
    • 👓Examples
    • 🏦Resource Bank To More Reentrancy Examples
    • 🤝Conclusion: Reflecting on the Reentrancy Vulnerability
  • 🔂Tutorial 9: Read-Only Reentrancy
    • 🚀Prerequisites
    • 📘Understanding Read-Only Reentrancy
    • 🔨Mitigating Read-Only Reentrancy
    • 👓Real World Examples
    • 🏦Resource Bank To More Reentrancy Examples
    • 🤝Read-Only Reentrancy: Conclusion
  • 🚆Tutorial 10: ERC20 transfer() and safeTransfer()
    • 🚀Prerequisites
    • 📘Understanding ERC20 transfer() and safeTransfer()
    • 👓Examples
    • 🤝Conclusion
  • 📞Tutorial 11: Low level .call(), .transfer() and .send()
    • 🚀Prerequisites
    • 📘Understanding .call, .transfer, and .send
    • 🛑Understanding the Vulnerabilities of .transfer and .send
    • 👓Examples
    • 🤝Low level .call(), .transfer() and .send() conclusion
  • ☎️Tutorial 12: Delegatecall Vulnerabilities in Precompiled Contracts
    • 🚀Prerequisites
    • 📳Understanding Delegatecall
    • ⛰️EVM, L2s, Bridges, and the Quest for Scalability
    • 🏗️Understanding Precompiles in the Ethereum Virtual Machine (EVM)
    • 💻Custom Precompiles
    • 💀Potential Vulnerabilities in EVM Implementations: Overlooked DelegateCall in Precompiled Contracts
    • 👓Real World Examples
    • 🤝Delegatecall and Precompiles: Conclusion
  • 🌊Tutorial 13: Liquid Staking
    • 🚀Prerequisites
    • 📘Understanding Liquid Staking
    • 💀Understanding Liquid Staking Vulnerabilities
    • 🛑Example Vulnerability
    • 🐜Example Vulnerability 2
    • 🕷️Example Vulnerability 3
    • 🤝Liquid Staking: Conclusion
  • 🚿Tutorial 14: Slippage
    • 🚀Prerequisites
    • 📘Understanding Slippage in Automated Market Makers (AMMs)
    • 💀Understanding the "Lack of Slippage Check" Vulnerability in Automated Market Makers (AMMs) and DEXs
    • 😡On-Chain Slippage Calculations Vulnerability
    • 📛0 slippage tolerance vulnerability
    • 👓Real World Examples
    • 🏦Resource bank to more slippage vulnerabilities
    • 🤝Slippage Conclusion
  • 📉Tutorial 15: Oracles
    • 🚀Prerequisites
    • 📘Understanding Oracles
    • 📈Types of price feeds
    • 😡Flash Loans
    • 💀Understanding Oracle Vulnerabilities
      • ⛓️The Danger of Single Oracle Dependence
      • ⬇️Using Deprecated Functions
      • ❌Lack of return data validation
      • 🕐Inconsistent or Absent Price Data Fetching/Updating Intervals
    • 🔫Decentralized Exchange (DEX) Price Oracles Vulnerabilities
    • 🛑Found Vulnerabilities In Oracle Implementations
      • ⚖️Newly Registered Assets Skew Consultation Results
      • ⚡Flash-Loan Oracle Manipulations
      • ⛓️Relying Only On Chainlink: PriceOracle Does Not Filter Price Feed Outliers
      • ✍️Not Validating Return Data e.g Chainlink: (lastestRoundData)
      • 🗯️Chainlink: Using latestAnswer instead of latestRoundData
      • 😭Reliance On Fetching Oracle Functionality
      • 🎱Wrong Assumption of 18 decimals
      • 🧀Stale Prices
      • 0️⃣Oracle Price Returning 0
      • 🛶TWAP Oracles
      • 😖Wrong Token Order In Return Value
      • 🏗️miscellaneous
    • 🤝Oracles: Conclusion
  • 🧠Tutorial 16: Zero Knowledge (ZK)
    • 🚀Prerequisites
    • 📚Theory
      • 🔌Circom
      • 💻Computation
      • 🛤️Arithmetic Circuits
      • 🚧Rank-1 Constraint System (R1CS)
      • ➗Quadratic Arithmetic Program
      • ✏️Linear Interactive Proof
      • ✨ZK-Snarks
    • 🤓Definitions and Essentials
      • 🔑Key
      • 😎Scalar Field Order
      • 🌳Incremental Merkle Tree
      • ✒️ECDSA signature
      • 📨Non-Interactive Proofs
      • 🏝️Fiat-Shamir transformation (or Fiat-Shamir heuristic)
      • 🪶Pedersen commitment
    • 💀Common Vulnerabilities in ZK Code
      • ⛓️Under-constrained Circuits
      • ❗Nondeterministic Circuits
      • 🌊Arithmetic Over/Under Flows
      • 🍂Mismatching Bit Lengths
      • 🌪️Unused Public Inputs Optimized Out
      • 🥶Frozen Heart: Forging of Zero Knowledge Proofs
      • 🚰Trusted Setup Leak
      • ⛔Assigned but not Constrained
    • 🐛Bugs In The Wild
      • 🌳Dark Forest v0.3: Missing Bit Length Check
      • 🔢BigInt: Missing Bit Length Check
      • 🚓Circom-Pairing: Missing Output Check Constraint
      • 🏹Semaphore: Missing Smart Contract Range Check
      • 🔫Zk-Kit: Missing Smart Contract Range Check
      • 🤖Aztec 2.0: Missing Bit Length Check / Nondeterministic Nullifier
      • ⏸️Aztec Plonk Verifier: 0 Bug
      • 🪂0xPARC StealthDrop: Nondeterministic Nullifier
      • 😨a16z ZkDrops: Missing Nullifier Range Check
      • 🤫MACI 1.0: Under-constrained Circuit
      • ❄️Bulletproofs Paper: Frozen Heart
      • 🏔️PlonK: Frozen Heart
      • 💤Zcash: Trusted Setup Leak
      • 🚨14. MiMC Hash: Assigned but not Constrained
      • 🚔PSE & Scroll zkEVM: Missing Overflow Constraint
      • ➡️PSE & Scroll zkEVM: Missing Constraint
      • 🤨Dusk Network: Missing Blinding Factors
      • 🌃EY Nightfall: Missing Nullifier Range Check
      • 🎆Summa: Unconstrained Constants Assignemnt
      • 📌Polygon zkEVM: Missing Remainder Constraint
    • 💿ZK Security Resources
  • 🤝Tutorial 17 DEX's (Decentralized Exchanges)
    • 🚀Prerequisites
    • 📚Understanding Decentralized Exchanges
    • 💀Common Vulnerabilities in DEX Code
      • 🔎The "Lack of Slippage Check" Vulnerability in Automated Market Makers (AMMs) a
      • 😡On-Chain Slippage Calculations Vulnerability
      • 📛Slippage tolerance vulnerability
      • 😵How Pool Implementation Mismatches Pose a Security Risk to Decentralized Exchanges (DEXs)
      • 🏊‍♂️Vulnerabilities in Initial Pool Creation - Liquidity Manipulation Attacks
      • 🛑Vulnerabilities In Oracle Implementations
        • ⚖️Newly Registered Assets Skew Consultation Results
        • ⚡Flash-Loan Oracle Manipulations
        • ⛓️Relying Only On Chainlink: PriceOracle Does Not Filter Price Feed Outliers
        • ✍️Not Validating Return Data e.g Chainlink: (lastestRoundData)
        • 🗯️Chainlink: Using latestAnswer instead of latestRoundData
        • 😭Reliance On Fetching Oracle Functionality
        • 🎱Wrong Assumption of 18 decimals
        • 🧀Stale Prices
        • 0️⃣Oracle Price Returning 0
        • 🛶TWAP Oracles
        • 😖Wrong Token Order In Return Value
        • 🏗️miscellaneous
      • 🥶Minting and Burning Liquidity Pool Tokens
      • 🎫Missing Checks
      • 🔞18 Decimal Assumption
        • 📌Understanding ERC20 Decimals
        • 💀Examples Of Vulnerabilities To Do With Assuming 18 Decimals
      • 🛣️Incorrect Swap Path
      • The Importance of Deadline Checks in Swaps
    • 🤝Conclusion
  • 🤖Tutorial 18: Proxies
    • 🚀Prerequisites
    • 📥Ethereum Storage and Memory
    • 📲Ethereum Calls and Delegate Calls
    • 💪Upgradability Patterns in Ethereum: Enhancing Smart Contracts Over Time
    • 🔝Proxy Upgrade Pattern in Ethereum
    • 🌀Exploring the Landscape of Ethereum Proxies
      • 🪞Transparent Proxies
      • ⬆️UUPS Proxies
      • 💡Beacon Proxies
      • 💎Diamond Proxies
  • 🔞Tutorial 19: 18 Decimal Assumption
    • 🚀Prerequisites
    • 📌Understanding ERC20 Decimals
    • 💀Examples Of Vulnerabilities To Do With Assuming 18 Decimals
    • 🤝Conclusion
  • ➗Tutorial 20: Arithmetic
    • 🚀Prerequisites
    • 🕳️Arithmetic pitfall 1: Division by 0
    • 🔪Arithmetic pitfall 2: Precision Loss Due To Rounding
    • 🥸Arithmetic pitfall 3: Erroneous Calculations
    • 🤝Conclusion
  • 🔁Tutorial 21: Unbounded Loops
    • 🚀Prerequisites
    • ⛽Gas Limit Vulnerability
    • 📨Transaction Failures Within Loops
    • 🤝Conclusion
  • 📔Tutorial 22: `isContract`
    • 🚀Prerequisites
    • 💀Understanding the 'isContract()` vulnerability
    • 🤝Conclusion
  • 💵Tutorial 23: Staking
    • 🚀Prerequisites
    • 💀First Depositor Inflation Attack in Staking Contracts
    • 🌪️Front-Running Rebase Attack (Stepwise Jump in Rewards)
    • ♨️Rugability of a Poorly Implemented recoverERC20 Function in Staking Contracts
    • 😠General Considerations for ERC777 Reentrancy Vulnerabilities
    • 🥏Vulnerability: _lpToken and Reward Token Confusion in Staking Contracts
    • 🌊Slippage Checks
    • 🌽The Harvest Functionality in Vaults: Issues and Best Practices
  • ⛓️Tutorial 24: Chain Re-org Vulnerability
    • 🚀Prerequisites
    • ♻️Chain Reorganization (Re-org) Vulnerability
    • 🧑‍⚖️Chain Re-org Vulnerability in Governance Voting Mechanisms
  • 🌉Tutorial 25: Cross Chain Bridges Vulnerabilities
    • 🚀Prerequisites
    • ♻️ERC777 Bridge Vulnerability: Reentrancy Attack in Token Accounting
      • 🛑Vulnerability: Withdrawals Can Be Locked Forever If Recipient Is a Contract
    • 👛The Dangers of Not Using SafeERC20 for Token Transfers
    • Uninitialized Variable Vulnerability in Upgradeable Smart Contracts
    • Unsafe External Calls and Their Vulnerabilities
    • Signature Replay Attacks in Cross-Chain Protocols
  • 🚰Tutorial 26: Integer Underflow and Overflow Vulnerabilities in Solidity (Before 0.8.0)
    • 🚀Prerequisites
    • 💀Understanding Integer Underflow and Overflow Vulnerabilities
    • 🤝Conclusion
  • 🥏Tutorial 27: OpenZeppelin Vulnerabilities
    • 🚀Prerequisites
    • 🛣️A Guide on Vulnerability Awareness and Management
      • 🤝Conclusion
  • 🖊️Tutorial 28: Signature Vulnerabilities / Replays
    • 🚀Prerequisites
    • 🔏Reusing EIP-712 Signatures in Private Sales
    • 🔁Replay Attacks on Failed Transactions
    • 📃Improper Token Validation in Permit Signature
  • 🤝Tutorial 29: Solmate Vulnerabilities
    • 🔏Lack of Code Size Check in Token Transfer Functions in Solmate
  • 🧱Tutorial 30: Inconsistent block lengths across chains
    • 🕛Incorrect Assumptions about Block Number in Multi-Chain Deployments
  • 💉Tutorial 31: NFT JSON and XSS injection
    • 📜Vulnerability: JSON Injection in tokenURI Functions
    • 📍Cross-Site Scripting (XSS) Vulnerability via SVG Construction in Smart Contracts
  • 🍃Tutorial 32: Merkle Leafs
    • 🖥️Misuse of Merkle Leaf Nodes
  • 0️Tutorial 33: Layer 0
    • 📩Lack of Force Resume in LayerZero Integrations
    • ⛽LayerZero-Specific Vulnerabilities in Airdropped Gas and Failure Handling
    • 🔓Understanding the Vulnerability of Blocking LayerZero Channels
    • 🖊️Copy of Understanding the Vulnerability of Blocking LayerZero Channels
  • ♻️Tutorial 34: Forgetting to Update the Global State in Smart Contracts
  • ‼️Tutorial 35: Wrong Function Signature
  • 🛑Tutorial 36: Handling Edge Cases of Banned Addresses in DeFi
  • Tutorial 37: initializer and onlyInitializing
  • ➗Tutorial 38: Eigen Layer
    • 📩Denial of Service in NodeDelegator Due to EigenLayer's maxPerDeposit Check
    • 📈Incorrect Share Issuance Due to Strategy Updates in EigenLayer Integrations
    • 🔁nonReentrant Vulnerability in EigenLayer Withdrawals
  • ⚫Tutorial 39: Wormhole
    • 📩Proposal Execution Failure Due to Guardian Set Change
  • 💼Tutorial 40: Uniswap V3
    • 📩Understanding and Mitigating Partial Swaps in Uniswap V3
    • 🌊Underflow Vulnerability in Uniswap V3 Position Fee Growth Calculations
    • ➗Handling Decimal Discrepancies in Uniswap V3 Price Calculations
  • 🔢Tutorial 41: Multiple Token Addresses in Proxied Tokens
    • 🔓Understanding Vulnerabilities Arising from Tokens with Multiple Entry Points
  • 🤖Tutorial 42: abiDecoder v2
    • 🥥Vulnerabilities from Manipulated Token Interactions Using ABI Decoding
  • ❓Tutorial 43: On-Chain Randomness
    • Vulnerabilities in On-Chain Randomness and How It Can Be Exploited
  • 😖Tutorial 44: Weird ERC20 Tokens
    • Weird Token List
  • 🔨Tutorial 45: Hardcoded stable coin values
  • ❤️Tutorial 46: The Risks of Chainlink Heartbeat Discrepancies in Smart Contracts
  • 👣Tutorial 47: The Risk of Forgetting a Withdrawal Mechanism in Smart Contracts
  • 💻Tutorial 48: Governance and Voting
    • Flash Loan Voting Exploit
    • Exploiting Self-Delegation
    • 💰Missing payable Keyword in Governance Execute Function
    • 👊Voting Multiple Times by Shifting Delegation
    • 🏑Missing Duplicate Veto Check
  • 📕Tutorial 49: Not Conforming To EIP standards
    • 💎Understanding EIP-2981: NFT Royalty Standard
    • 👍Improper Implementation of EIP-2612 Permit Standard
    • 🔁Vulnerabilities of Missing EIP-155 Replay Attack Protection
    • ➡️Vulnerabilities Due to Missing EIP-1967 in Proxy Contracts
    • 🔓Vulnerability of Design Preventing EIP-165 Extensibility
    • 🎟️The Dangers of Not Properly Implementing ERC-4626 in Yield Vaults
    • 🔁EIP-712 Implementation and Replay Attacks
  • ⏳Tutorial 50: Vesting
    • 🚔Vulnerability of Allowing Unauthorized Withdrawals in Vesting Contracts
    • 👊Vulnerability of Unbounded Timelock Loops in Vesting Contracts
    • ⬆️Vulnerability of Incorrect Linear Vesting Calculations
    • ⛳Missing hasStarted Modifier
    • 🔓Vulnerability in Bond Depositor's Vesting Period Reset
  • ⛽Tutorial 51: Ethereum's 63/64 Gas Rule
    • 🛢️Abusing Ethereum's 63/64 Gas Rule to Manipulate Contract Behavior
  • 📩Tutorial 52: NPM Dependency Confusion and Unclaimed Packages
    • 💎Exploiting Unclaimed NPM Packages and Scopes
  • 🎈Tutorial 53: Airdrops
    • 🛄Claiming on Behalf of Other Users
    • 🧲Repeated Airdrop Claims Vulnerability
    • 🍃Airdrop Vulnerability – Merkle Leaves and Parent Node Hash Collisions
  • 🎯Tutorial 54: Precision
    • 🎁Vulnerabilities Due to Insufficient Precision in Reward Calculations
    • Min-Shares: Fixed Minimum Share Values for Tokens with Low Decimal Precision
    • Vulnerability Due to Incorrect Rounding When the Numerator is Not a Multiple of the Denominator
    • Vulnerability from Small Deposits Being Rounded Down to Zero Shares in Smart Contracts
    • Precision Loss During Withdrawals from Vaults Can Block Token Transfers Due to Value < Amount
    • 18 Decimal Assumption Scaling: Loss of Precision in Asset Conversion Due to Incorrect Scaling
  • Tutorial 55: AssetIn == AssetOut, FromToken == ToToken
    • 🖼️Vulnerability: Missing fromToken != toToken Check
  • 🚿Tutorial 56: Vulnerabilities Related to LP Tokens Being the Same as Reward Tokens
    • 🖼️Vulnerabilities Caused by LP Tokens Being the Same as Reward Tokens
  • Tutorial 57: Unsanitized SWAP Paths and Arbitrary Contract Call Vulnerabilities
    • 📲Arbitrary Contract Calls from Unsanitized Paths
  • Tutorial 58: The Risk of Infinite Approvals and Arbitrary Contract Calls
    • 🪣Exploiting Infinite Approvals and Arbitrary Contract Calls
  • Tutorial 59: Low-Level Calls in Solidity Returning True for Non-Existent Contracts
    • Low-Level Calls Returning True for Non-Existent Contracts
  • 0️⃣Tutorial 60: The Impact of PUSH0 and the Shanghai Hardfork on Cross-Chain Deployments > 0.8.20
    • PUSH0 and Cross-Chain Compatibility Challenges
  • 🐍Tutorial 61: Vyper Vulnerable Versions
    • Vyper and the EVM
  • ⌨️Tutorial 62: Typos in Smart Contracts — The Silent Threat Leading to Interface Mismatch
    • Vyper and the EVM
  • ☁️Tutorial 63: Balance Check Using ==
    • The Vulnerability: == Balance Check
  • 💍Tutorial 64: Equal Royalties for Unequal NFTs
    • Understanding the Problem: Equal Royalties for Unequal NFTs
  • 🖼️Tutorial 65: ERC721 and NFTs
    • The Risk of Using transferFrom Instead of safeTransferFrom in ERC721 Projects
    • ❄️Why _safeMint Should Be Used Instead of _mint in ERC721 Projects
    • The Importance of Validating Token Types in Smart Contracts
    • 📬Implementing ERC721TokenReceiver to Handle ERC721 Safe Transfers
    • NFT Implementation Deviating from ERC721 Standard in Transfer Functions
    • NFT Approval Persistence after Transfer
    • 🎮Gameable NFT Launches through Pseudo-Randomness
    • 2️⃣Protecting Buyers from Losing Funds Due to Claimed NFT Rewards on Secondary Markets
    • ♻️Preventing Reentrancy When Using SafeERC721
    • 🖊️Preventing Re-use of EIP-712 Signatures in NFT Private Sales
  • 2️⃣Tutorial 66: Vulnerability Arising from NFTs Supporting Both ERC721 and ERC1155 Standards
  • 📷Tutorial 67: ERC1155 Vulnerabilities
    • ♻️Preventing Reentrancy in OpenZeppelin's SafeERC1155
    • 🛫Vulnerabilities in OpenZeppelin's ERC1155Supply Contract
    • Understanding Incorrect Token Owner Enumeration in ERC1155Enumerable
    • Avoiding Breaking ERC1155 Composability with Improper safeTransferFrom Implementation
    • 💍Ensuring Compatibility with EIP-2981 in ERC1155 Contracts
  • 🪟Informational Vulnerabilities
  • ⛽Gas Efficiency
  • 💻Automation Tools
  • 🔜Out Of Gas (Coming Soon)
  • 🔜DEX Aggregators (Coming Soon)
  • 🔜Bribes (Coming Soon)
  • 🔜Understanding Compiled Bytecode (coming soon)
  • 🔜Deployment Mistakes (coming soon)
  • 🔜Optimistic Roll-ups (coming soon)
  • 🔜Typos (coming soon)
  • 🔜Try-Catch (coming soon)
  • 🔜NFT Market-place (coming soon)
  • 🔜Upgrade-able Contracts (coming soon)
Powered by GitBook
On this page
  1. Tutorials
  2. Tutorial 1: Front-Running

Examples

PreviousUnderstanding Front-RunningNextMitigation Steps

Last updated 1 year ago

Understanding the Examples: In the "Examples" section, we delve into practical instances of front-running vulnerabilities unearthed from actual bug bounty reports, Code Arena analyses, and more. By studying these real-life cases, you'll gain a deeper, more tangible understanding of front running and its potential impact, enhancing your auditing skills in a practical and impactful way. Example 4 and 5 are my personal findings related to front running.

Example 1

Very Easy

Based on our previous discussion, see if you can find the Front running vulnerability in the code bellow:

In this example Adam creates a guessing game. You win 10 ether if you can find the correct string that hashes to the target hash and you are the first person to submit it. Let's see how this contract is vulnerable to front running

contract FindThisHash {
    bytes32 public constant hash =
        0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;

    constructor() payable {}

    function solve(string memory solution) public {
        require(hash == keccak256(abi.encodePacked(solution)), "Incorrect answer");

        (bool sent, ) = msg.sender.call{value: 10 ether}("");
        require(sent, "Failed to send Ether");
    }
}

Did you find it? Great! if not, consider this scenario:

  1. Adam deploys FindThisHash with 10 Ether.

  2. Bob finds the correct string that will hash to the target hash. ("Ethereum")

  3. Bob calls solve("Ethereum") with gas price set to 15 gwei.

  4. Eric is watching the transaction pool for the answer to be submitted.

  5. Eric sees Bob's answer and calls solve("Ethereum") with a higher gas price than Bob (100 gwei).

  6. Eric transaction was mined before Bob's transaction. Eric won the reward of 10 ether

What happened?

Transactions take some time before they are mined. Transactions not yet mined are put in the transaction pool. Transactions with higher gas price are typically mined first. An attacker can get the answer from the transaction pool, send a transaction with a higher gas price so that their transaction will be included in a block before the original.

Example 2: Real life Cod4rena Bug Find With Payout (Medium Risk Vulnerability)

See if you can find the Front running vulnerability in the code bellow!

    /// @dev Create a new vault
    function build(address owner, bytes12 vaultId, bytes6 seriesId, bytes6 ilkId)
        external
        auth
        returns(DataTypes.Vault memory vault)
    {
        require (vaultId != bytes12(0), "Vault id is zero");
        require (vaults[vaultId].seriesId == bytes6(0), "Vault already exists");   // Series can't take bytes6(0) as their id
        require (ilks[seriesId][ilkId] == true, "Ilk not added to series");
        vault = DataTypes.Vault({
            owner: owner,
            seriesId: seriesId,
            ilkId: ilkId
        });
        vaults[vaultId] = vault;

        emit VaultBuilt(vaultId, owner, seriesId, ilkId);
    }

Did you find it? Great, this was a real bug find in a bug bounty, so if you found it, it means you are able to already earn money through bounties, here is the explaination for those who didn't find it yet:

The vaultID for a new vault being built is required to be specified by the user building a vault via the build() function. An attacker can observe a build() as part of a batch transaction in the mempool, identify the vaultID being requested and front-run that by constructing a malicious batch transaction with only the build operation with that same vaultID. The protocol would create a vault with that vaultID and assign attacker as its owner. More importantly, the valid batch transaction in the mempool which was front-run will later fail to create its vault because that vaultID already exists, as per this check:

require (vaults[vaultId].seriesId == bytes6(0), "Vault already exists");   // Series can't take bytes6(0) as their id

As a result, the valid batch transaction fails entirely because of the attacker front-running with the observed vaultID.

While the attacker gains nothing except the ownership of an empty vault after spending the gas, this could grief the protocol’s real users by preventing them from opening a vault and interacting with the protocol in any manner.

Rationale for Medium-severity impact: While the likelihood of this may be low, the impact is high because valid vaults will never be successfully created and will lead to a DoS against the entire protocol’s functioning. So, with low likelihood and high impact, the severity (according to OWASP) is medium.

Example 3: Real life Cod4rena Bug Find With Payout (High Risk Vulnerability)

See if you can find the Front running vulnerability in the code bellow! This one is harder to spot. This code snippet is taken from a DEX (decentralized exchange)

    // Calculate output of swapping SPARTA for TOKEN & update recorded amounts
    function _swapBaseToToken(uint256 _x) internal returns (uint256 _y, uint256 _fee){
        uint256 _X = baseAmount;
        uint256 _Y = tokenAmount;
        _y =  iUTILS(_DAO().UTILS()).calcSwapOutput(_x, _X, _Y); // Calc TOKEN output
        uint fee = iUTILS(_DAO().UTILS()).calcSwapFee(_x, _X, _Y); // Calc TOKEN fee
        _fee = iUTILS(_DAO().UTILS()).calcSpotValueInBase(TOKEN, fee); // Convert TOKEN fee to SPARTA
        _setPoolAmounts(_X + _x, _Y - _y); // Update recorded BASE and TOKEN amounts
        _addPoolMetrics(_fee); // Add slip fee to the revenue metrics
        return (_y, _fee);
    }

The Function does not implement any slippage checks with comparing the swap / liquidity results with a minimum swap / liquidity value. Users can be frontrun and receive a worse price than expected when they initially submitted the transaction. There's no protection at all, no minimum return amount or deadline for the trade transaction to be valid which means the trade can be delayed by miners or users congesting the network, as well as being sandwich attacked - ultimately leading to loss of user funds. There are no minimum amounts out, or checks that front-running/slippage is sufficiently mitigated. This means that anyone with enough capital can force arbitrarily large slippage by sandwiching transactions, close to 100%.

Example 4: Personal Bug Bounty Find, Payout $2,500. Medium Risk

This is a bug I found while doing bug bounties, here is the code bellow, although easily finding thing the bug may need more context to the protocol, this code snipped should be enough. See if you can find the vulnerability:

 function bondForWithHint(
        uint256 _amount,
        address _owner,
        address _to,
        address _oldDelegateNewPosPrev,
        address _oldDelegateNewPosNext,
        address _currDelegateNewPosPrev,
        address _currDelegateNewPosNext
    ) public whenSystemNotPaused currentRoundInitialized {
        // the `autoClaimEarnings` modifier has been replaced with its internal function as a `Stack too deep` error work-around
        _autoClaimEarnings(_owner);
        Delegator storage del = delegators[_owner];

        uint256 currentRound = roundsManager().currentRound();
        // Amount to delegate
        uint256 delegationAmount = _amount;
        // Current delegate
        address currentDelegate = del.delegateAddress;
        // Current bonded amount
        uint256 currentBondedAmount = del.bondedAmount;

        if (delegatorStatus(_owner) == DelegatorStatus.Unbonded) {
            // New delegate
            // Set start round
            // Don't set start round if delegator is in pending state because the start round would not change
            del.startRound = currentRound.add(1);
            // Unbonded state = no existing delegate and no bonded stake
            // Thus, delegation amount = provided amount
        } else if (currentBondedAmount > 0 && currentDelegate != _to) {
            // Prevents third-party caller to change the delegate of a delegator
            require(msg.sender == _owner || msg.sender == l2Migrator(), "INVALID_CALLER");
            // A registered transcoder cannot delegate its bonded stake toward another address
            // because it can only be delegated toward itself
            // In the future, if delegation towards another registered transcoder as an already
            // registered transcoder becomes useful (i.e. for transitive delegation), this restriction
            // could be removed
            require(!isRegisteredTranscoder(_owner), "registered transcoders can't delegate towards other addresses");
            // Changing delegate
            // Set start round
            del.startRound = currentRound.add(1);
            // Update amount to delegate with previous delegation amount
            delegationAmount = delegationAmount.add(currentBondedAmount);

            decreaseTotalStake(currentDelegate, currentBondedAmount, _oldDelegateNewPosPrev, _oldDelegateNewPosNext);
        }

        {
            Transcoder storage newDelegate = transcoders[_to];
            EarningsPool.Data storage currPool = newDelegate.earningsPoolPerRound[currentRound];
            if (currPool.cumulativeRewardFactor == 0) {
                currPool.cumulativeRewardFactor = cumulativeFactorsPool(newDelegate, newDelegate.lastRewardRound)
                    .cumulativeRewardFactor;
            }
            if (currPool.cumulativeFeeFactor == 0) {
                currPool.cumulativeFeeFactor = cumulativeFactorsPool(newDelegate, newDelegate.lastFeeRound)
                    .cumulativeFeeFactor;
            }
        }

        // cannot delegate to someone without having bonded stake
        require(delegationAmount > 0, "delegation amount must be greater than 0");
        // Update delegate
        del.delegateAddress = _to;
        // Update bonded amount
        del.bondedAmount = currentBondedAmount.add(_amount);

        increaseTotalStake(_to, delegationAmount, _currDelegateNewPosPrev, _currDelegateNewPosNext);

        if (_amount > 0) {
            // Transfer the LPT to the Minter
            livepeerToken().transferFrom(msg.sender, address(minter()), _amount);
        }

        emit Bond(_to, currentDelegate, _owner, _amount, del.bondedAmount);
    }

Did you find it? Congratulations, this was classified as a medium risk finding with a payout of $2,500. If you found it then you are able to make money online. Here is the bug description:

In the current implementation of the bondForWithHint function in the provided smart contract, there is a potential for a front-running attack that may force a user to unintentionally become a transcoder, preventing them from delegating their tokens to other users.

This can be achieved by an attacker front-running a transaction where a user (let's call this user 'Adam') is trying to delegate tokens to another user (let's say, 'Bob') using the bondWithHint (or bondForWithHint) function. The attacker can front-run Adam's transaction by calling bondForWithHint, setting _owner to Adam's address, _to to Adam's address again, and the bond amount to any non-zero value (even as low as 1 wei).

The malicious transaction is coded as follows:

bondForWithHint(1, Adam, Adam, /*other parameters*/);

This would result in Adam unexpectedly becoming a transcoder because they now satisfy the check in the isRegisteredTranscoder function:

function isRegisteredTranscoder(address _transcoder) public view returns (bool) {
    Delegator storage d = delegators[_transcoder];
    return d.delegateAddress == _transcoder && d.bondedAmount > 0;
}

Because Adam is now a registered transcoder, their initial attempt to delegate to Bob using the bondWithHint function is blocked by the following check in the bondForWithHint function:

else if (currentBondedAmount > 0 && currentDelegate != _to) {
    require(!isRegisteredTranscoder(_owner), "registered transcoders can't delegate towards other addresses");

The message "registered transcoders can't delegate towards other addresses" will be triggered, and the delegation will fail.

A user unknowingly becomes a transcoder and is unable to delegate their tokens to another user as initially intended. This may cause confusion for the user and disrupt the network's normal functioning, as the ability to freely delegate is a crucial aspect of the protocol. More importantly, it can be used by malicious actors to disrupt the delegation process on a larger scale if used repetitively.

Example 5: Personal Bug Bounty Find, Payout $20,000. High Risk

This is a bug I found while doing bug bounties, here is the code bellow, In the context of the protocol this was classified as High risk with a payout of $20,000, although the payout could have been $50,000, however the severity was downgraded due to the difficulty of the attack. See if you can find the issue

The context of this protocol is that every user that is on this protocol is essentially "staked in" so if fraudulent activity is detected, it is reported and if found guilty, the governing body can penalize the user by taking their tokens away from them.

  
      struct Report {
        address reporter;
        address reportedAddress;
        address secondReportedAddress;
        uint256 reportTimestamps;
        ILERC20 reportTokens;
        bool secondReports;
        bool reporterClaimStatus;
    }
  
  
  /// @notice This function will generate a report
    /// @dev This function must be called by a non blacklisted/reported address. 
    /// It will generate a report for an address linked to a token.
    /// Lossless Contracts, Admin addresses and Dexes cannot be reported.
    /// @param _token Token address of the stolen funds
    /// @param _account Potential malicious address
    function report(ILERC20 _token, address _account) override public notBlacklisted whenNotPaused returns (uint256){
        require(_account != address(0), "LSS: Cannot report zero address");
        require(!losslessController.whitelist(_account), "LSS: Cannot report LSS protocol");
        require(!losslessController.dexList(_account), "LSS: Cannot report Dex");

        uint256 reportId = tokenReports[_token].reports[_account];

        require(reportId == 0 || 
                reportInfo[reportId].reportTimestamps + reportLifetime < block.timestamp || 
                losslessGovernance.isReportSolved(reportId) && 
                !losslessGovernance.reportResolution(reportId), "LSS: Report already exists");

        reportCount += 1;
        reportId = reportCount;
        reportInfo[reportId].reporter = msg.sender;

        tokenReports[_token].reports[_account] = reportId;
        reportInfo[reportId].reportTimestamps = block.timestamp;
        reportInfo[reportId].reportTokens = _token;

        require(stakingToken.transferFrom(msg.sender, address(this), reportingAmount), "LSS: Reporting stake failed");

        losslessController.addToBlacklist(_account);
        reportInfo[reportId].reportedAddress = _account;
        
        losslessController.activateEmergency(_token);

        emit ReportSubmission(_token, _account, reportId, reportingAmount);

        return reportId;
    }


    /// @notice This function will add a second address to a given report.
    /// @dev This funtion must be called by a non blacklisted/reported address. 
    /// It will generate a second report linked to the first one created. 
    /// This can be used in the event that the malicious actor is able to frontrun the first report by swapping the tokens or transfering.
    /// @param _reportId Report that was previously generated.
    /// @param _account Potential malicious address
    function secondReport(uint256 _reportId, address _account) override public whenNotPaused {
        require(_account != address(0), "LSS: Cannot report zero address");
        require(!losslessGovernance.isReportSolved(_reportId) && !losslessGovernance.reportResolution(_reportId), "LSS: Report already solved");
        require(!losslessController.whitelist(_account), "LSS: Cannot report LSS protocol");
        require(!losslessController.dexList(_account), "LSS: Cannot report Dex");

        Report storage queriedReport = reportInfo[_reportId]; 

        uint256 reportTimestamp = queriedReport.reportTimestamps;
        ILERC20 token = queriedReport.reportTokens;

        require(_reportId != 0 && reportTimestamp + reportLifetime > block.timestamp, "LSS: report does not exists");
        require(queriedReport.secondReports == false, "LSS: Another already submitted");
        require(msg.sender == queriedReport.reporter, "LSS: invalid reporter");

        queriedReport.secondReports = true;
        tokenReports[token].reports[_account] = _reportId;

        losslessController.addToBlacklist(_account);
        queriedReport.secondReportedAddress = _account;

        emit SecondReportSubmission(token, _account, _reportId);
    }
    
    /// @notice This function solves a report based on the voting resolution of the three pilars
    /// @dev Only can be run by the three pilars.
    /// When the report gets resolved, if it's resolved negatively, the reported address gets removed from the blacklist
    /// If the report is solved positively, the funds of the reported account get retrieved in order to be distributed among stakers and the reporter.
    /// @param _reportId Report to be resolved
    function resolveReport(uint256 _reportId) override public whenNotPaused {

        require(!isReportSolved(_reportId), "LSS: Report already solved");


        (,,,uint256 reportTimestamps,,,) = losslessReporting.getReportInfo(_reportId);
        
        if (reportTimestamps + losslessReporting.reportLifetime() > block.timestamp) {
            _resolveActive(_reportId);
        } else {
            _resolveExpired(_reportId);
        }
        
        reportVotes[_reportId].resolved = true;
        delete reportedAddresses;

        emit ReportResolve(_reportId, reportVotes[_reportId].resolution);
    }
    
    /// @notice This function is for the reporter to claim their rewards
    /// @param _reportId Staked report
    function reporterClaim(uint256 _reportId) override public whenNotPaused {
        require(reportInfo[_reportId].reporter == msg.sender, "LSS: Only reporter");
        require(losslessGovernance.reportResolution(_reportId), "LSS: Report solved negatively");

        Report storage queriedReport = reportInfo[_reportId];

        require(!queriedReport.reporterClaimStatus, "LSS: You already claimed");

        queriedReport.reporterClaimStatus = true;

        uint256 amountToClaim = reporterClaimableAmount(_reportId);

        require(queriedReport.reportTokens.transfer(msg.sender, amountToClaim), "LSS: Token transfer failed");
        require(stakingToken.transfer(msg.sender, reportingAmount), "LSS: Reporting stake failed");
        emit ReporterClaim(msg.sender, _reportId, amountToClaim);
    }

Did you find it? Great, that means you have the skills required to earn $20,000 - $50,000 on a single bug report! For those who didnt find the bug, don't worry, here is a description:

Once a user has called the function report, the user can then call secondReport and input a different address to be penalized and subsequently have its tokens removed. This presents a very nasty front-running risk, but its not the reporter being front-run....its the reporter doing the front-running! This is because after a report is voted on by the governance and validated, when the governance calls resolveReport, the reporter can listen to this contract call, and front run this transaction by calling secondReport and inputting the address of a victim (e.g. a whale or a dex). Because this transaction is completed before 'resolveRepot', 'resloveReport' now pulls the tokens from the victim and distributes it to everybody involved, the proposedWallet, stakers, community members.

Any questions so far?

📚
🏃
👓
Book an audit with Zokyo
Solidity by example
ask Omar