πŸ‘“Examples

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 Solidity by example

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!

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:

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)

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:

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:

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

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:

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.

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? ask Omar

Last updated