I had the honor to partcipate in 2025 nexhunt ctf ,So here we are on the Blockchain Network,lets dive in and solve this challenges and get some flags.

this writeup is clearly for the blockchain challenges on this ctf.

first challenge

chainclue challenge

Goal: Find the flag hidden in the transaction data of a Sepolia testnet transaction.

Provided Information:

  • Transaction Hash: 0x1c1e14180c2e5dceefc260208199e23a8c61524dd54bd2e378cee00e14555c14
  • Contract Address: 0xFb67326dAacdD9163c0eeEB9E429D7D4B6c4EBb1
  • Network:Sepolia Testnet

Steps Taken

  1. Analyze the Transaction on Etherscan: The first step was to view the transaction details on the Sepolia Etherscan explorer. This is the standard procedure for analyzing any on-chain activity.

    • URL: https://sepolia.etherscan.io/tx/0x1c1e14180c2e5dceefc260208199e23a8c61524dd54bd2e378cee00e14555c14

sepolia testnet address of this chainrule address

  1. Inspect Input Data: On the transaction details page, the most relevant field for hidden data is the**Input Data** (also known as the transaction payload). This field contained a long hexadecimal string, which is the bytecode for the contract creation and initialization.

  2. Identify the Hidden String: The challenge states the flag is “hidden somewhere in the transaction data.” In blockchain CTFs, flags are often stored as a simple string encoded in hexadecimal (ASCII or UTF-8) within the input data. I searched the raw hexadecimal input data for the hex representation of the common flag prefix, nexus{, which is 6e657875737b.

  3. automate:

    Then for an easy path i told ai to create for me a script to decode the entire blob to get the flag.

    #!/usr/bin/env python3
    # evm_disasm.py
    # Usage: python3 evm_disasm.py bytecode.txt
    # or:    python3 evm_disasm.py 6080604052...
    #
    # Output: readable disassembly + extracted strings + metadata sniffing
    
    import sys
    import re
    from textwrap import shorten
    
    # Minimal EVM opcode table for readability (common opcodes)
    OPCODES = {
      0x00: "STOP",0x01:"ADD",0x02:"MUL",0x03:"SUB",0x04:"DIV",0x05:"SDIV",0x06:"MOD",0x07:"SMOD",
      0x08:"ADDMOD",0x09:"MULMOD",0x0a:"EXP",0x0b:"SIGNEXTEND",
      0x10:"LT",0x11:"GT",0x12:"SLT",0x13:"SGT",0x14:"EQ",0x15:"ISZERO",0x16:"AND",0x17:"OR",0x18:"XOR",
      0x19:"NOT",0x1a:"BYTE",0x20:"SHA3",
      0x30:"ADDRESS",0x31:"BALANCE",0x32:"ORIGIN",0x33:"CALLER",0x34:"CALLVALUE",0x35:"CALLDATALOAD",
      0x36:"CALLDATASIZE",0x37:"CALLDATACOPY",0x38:"CODESIZE",0x39:"CODECOPY",0x3a:"GASPRICE",
      0x3b:"EXTCODESIZE",0x3c:"EXTCODECOPY",0x3d:"RETURNDATASIZE",0x3e:"RETURNDATACOPY",
      0x40:"BLOCKHASH",0x41:"COINBASE",0x42:"TIMESTAMP",0x43:"NUMBER",0x44:"DIFFICULTY",0x45:"GASLIMIT",
      0x50:"POP",0x51:"MLOAD",0x52:"MSTORE",0x53:"MSTORE8",0x54:"SLOAD",0x55:"SSTORE",
      0x56:"JUMP",0x57:"JUMPI",0x58:"PC",0x59:"MSIZE",0x5a:"GAS",0x5b:"JUMPDEST",
      0x60:"PUSH1",0x61:"PUSH2",0x62:"PUSH3",0x63:"PUSH4",0x64:"PUSH5",0x65:"PUSH6",0x66:"PUSH7",
      0x67:"PUSH8",0x68:"PUSH9",0x69:"PUSH10",0x6a:"PUSH11",0x6b:"PUSH12",0x6c:"PUSH13",0x6d:"PUSH14",
      0x6e:"PUSH15",0x6f:"PUSH16",0x70:"PUSH17",0x71:"PUSH18",0x72:"PUSH19",0x73:"PUSH20",0x74:"PUSH21",
      0x75:"PUSH22",0x76:"PUSH23",0x77:"PUSH24",0x78:"PUSH25",0x79:"PUSH26",0x7a:"PUSH27",0x7b:"PUSH28",
      0x7c:"PUSH29",0x7d:"PUSH30",0x7e:"PUSH31",0x7f:"PUSH32",
      0x80:"DUP1",0x81:"DUP2",0x82:"DUP3",0x83:"DUP4",0x84:"DUP5",0x85:"DUP6",0x86:"DUP7",0x87:"DUP8",
      0x88:"DUP9",0x89:"DUP10",0x8a:"DUP11",0x8b:"DUP12",0x8c:"DUP13",0x8d:"DUP14",0x8e:"DUP15",0x8f:"DUP16",
      0x90:"SWAP1",0x91:"SWAP2",0x92:"SWAP3",0x93:"SWAP4",0x94:"SWAP5",0x95:"SWAP6",0x96:"SWAP7",0x97:"SWAP8",
      0x98:"SWAP9",0x99:"SWAP10",0x9a:"SWAP11",0x9b:"SWAP12",0x9c:"SWAP13",0x9d:"SWAP14",0x9e:"SWAP15",0x9f:"SWAP16",
      0xf0:"CREATE",0xf1:"CALL",0xf2:"CALLCODE",0xf3:"RETURN",0xf4:"DELEGATECALL",0xf5:"CREATE2",
      0xfa:"STATICCALL",0xfd:"REVERT",0xfe:"INVALID",0xff:"SELFDESTRUCT"
    }
    
    def read_hex_input(arg):
        s = None
        if arg.endswith('.txt'):
            s = open(arg, 'r').read().strip()
        else:
            s = arg.strip()
        s = s[2:] if s.startswith("0x") else s
        s = re.sub(r'[^0-9a-fA-F]', '', s)  # sanitize
        return bytes.fromhex(s)
    
    def is_printable(b):
        return 0x20 <= b <= 0x7e
    
    def ascii_preview(bts):
        s = ''.join(chr(b) if is_printable(b) else '.' for b in bts)
        return shorten(s, width=40, placeholder='...')
    
    def disassemble(bts):
        i = 0
        out = []
        L = len(bts)
        while i < L:
            opc = bts[i]
            name = OPCODES.get(opc, hex(opc))
            line = {"offset": i, "opcode": name, "opc_byte": opc, "operand": b''}
            i += 1
            # handle PUSH1..PUSH32
            if 0x60 <= opc <= 0x7f:
                n = opc - 0x5f  # number of bytes to read
                if i + n <= L:
                    operand = bts[i:i+n]
                    line["operand"] = operand
                else:
                    line["operand"] = bts[i:]  # truncated
                i += n
            out.append(line)
        return out
    
    def extract_printable_strings(bts, min_len=4):
        ascii_runs = []
        cur = bytearray()
        for b in bts:
            if is_printable(b):
                cur.append(b)
            else:
                if len(cur) >= min_len:
                    ascii_runs.append(bytes(cur).decode('ascii', errors='ignore'))
                cur = bytearray()
        if len(cur) >= min_len:
            ascii_runs.append(bytes(cur).decode('ascii', errors='ignore'))
        return ascii_runs
    
    def find_ipfs_metadata(bts):
        # search for ASCII "ipfs" bytes sequence (69706673)
        idx = bts.find(b'ipfs')
        results = []
        if idx != -1:
            # get the following bytes up to 64 bytes for inspection
            tail = bts[idx: idx+1+64]
            # many Solidity metadata patterns encode ipfs in CBOR/RLP; we will also try to find a 32-byte hash after 0x1220 pattern
            # search for pattern 0x1220 (0x12 0x20) which in Solidity metadata precedes 32-byte hash in some encodings
            p = bts.find(b'\x12\x20')
            if p != -1 and p+2+32 <= len(bts):
                hash32 = bts[p+2:p+2+32]
                results.append(("12 20 followed by 32 bytes", hash32.hex()))
            results.append(("ipfs_offset", idx, tail.hex()))
        return results
    
    def main():
        if len(sys.argv) < 2:
            print("Usage: python3 evm_disasm.py bytecode.txt  OR  python3 evm_disasm.py 6080...")
            sys.exit(1)
        bts = read_hex_input(sys.argv[1])
        print(f"Total bytes: {len(bts)}")
        print("="*60)
        dis = disassemble(bts)
        # print a compact disassembly (offset, opcode, operand hex, ascii preview for operand)
        for entry in dis:
            off = entry["offset"]
            opc = entry["opcode"]
            opb = entry["operand"]
            if opb:
                print(f"{off:04x}: {opc:8}  {opb.hex()}  ascii='{ascii_preview(opb)}'")
            else:
                print(f"{off:04x}: {opc}")
        print("="*60)
        # printable strings
        strings = extract_printable_strings(bts, min_len=4)
        print("Printable ASCII strings (len>=4):")
        for s in strings:
            print(" -", s)
        print("="*60)
        # metadata sniff
        meta = find_ipfs_metadata(bts)
        if meta:
            print("Possible metadata/ipfs findings:")
            for m in meta:
                print(" -", m)
        else:
            print("No obvious 'ipfs' marker or 0x1220+32 pattern found.")
        print("="*60)
        # show tail for manual inspection (last 128 bytes)
        tail_len = min(128, len(bts))
        tail = bts[-tail_len:]
        print(f"Last {tail_len} bytes (hex): {tail.hex()}")
        print("Done.")
    
    if __name__ == "__main__":
        main()

The bytecode.txt content which contains the long hex string.

0x60806040526040518060400160405280601c81526020017f6e657875737b54723463335f5468335f5472346e7334637431306e7d00000000815250600190816200004a919062000301565b5034801562000057575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550620003e5565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200011957607f821691505b6020821081036200012f576200012e620000d4565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620001937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000156565b6200019f868362000156565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620001e9620001e3620001dd84620001b7565b620001c0565b620001b7565b9050919050565b5f819050919050565b6200020483620001c9565b6200021c6200021382620001f0565b84845462000162565b825550505050565b5f90565b6200023262000224565b6200023f818484620001f9565b505050565b5b8181101562000266576200025a5f8262000228565b60018101905062000245565b5050565b601f821115620002b5576200027f8162000135565b6200028a8462000147565b810160208510156200029a578190505b620002b2620002a98562000147565b83018262000244565b50505b505050565b5f82821c905092915050565b5f620002d75f1984600802620002ba565b1980831691505092915050565b5f620002f18383620002c6565b9150826002028217905092915050565b6200030c826200009d565b67ffffffffffffffff811115620003285762000327620000a7565b5b62000334825462000101565b620003418282856200026a565b5f60209050601f83116001811462000377575f841562000362578287015190505b6200036e8582620002e4565b865550620003dd565b601f198416620003878662000135565b5f5b82811015620003b05784890151825560018201915060208501945060208101905062000389565b86831015620003d05784890151620003cc601f891682620002c6565b8355505b6001600288020188555050505b505050505050565b61067f80620003f35f395ff3fe608060405260043610610037575f3560e01c806328b355af146100425780633ccfd60b1461007e5780638da5cb5b146100945761003e565b3661003e57005b5f80fd5b34801561004d575f80fd5b5061006860048036038101906100639190610378565b6100be565b60405161007591906103d9565b60405180910390f35b348015610089575f80fd5b50610092610116565b005b34801561009f575f80fd5b506100a8610208565b6040516100b59190610431565b60405180910390f35b5f60016040516020016100d19190610543565b60405160208183030381529060405280519060200120826040516020016100f891906105bb565b60405160208183030381529060405280519060200120149050919050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161019a9061062b565b60405180910390fd5b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc4790811502906040515f60405180830381858888f19350505050158015610205573d5f803e3d5ffd5b50565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61028a82610244565b810181811067ffffffffffffffff821117156102a9576102a8610254565b5b80604052505050565b5f6102bb61022b565b90506102c78282610281565b919050565b5f67ffffffffffffffff8211156102e6576102e5610254565b5b6102ef82610244565b9050602081019050919050565b828183375f83830152505050565b5f61031c610317846102cc565b6102b2565b90508281526020810184848401111561033857610337610240565b5b6103438482856102fc565b509392505050565b5f82601f83011261035f5761035e61023c565b5b813561036f84826020860161030a565b91505092915050565b5f6020828403121561038d5761038c610234565b5b5f82013567ffffffffffffffff8111156103aa576103a9610238565b5b6103b68482850161034b565b91505092915050565b5f8115159050919050565b6103d3816103bf565b82525050565b5f6020820190506103ec5f8301846103ca565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61041b826103f2565b9050919050565b61042b81610411565b82525050565b5f6020820190506104445f830184610422565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061048e57607f821691505b6020821081036104a1576104a061044a565b5b50919050565b5f81905092915050565b5f819050815f5260205f209050919050565b5f81546104cf81610477565b6104d981866104a7565b9450600182165f81146104f357600181146105085761053a565b60ff198316865281151582028601935061053a565b610511856104b1565b5f5b8381101561053257815481890152600182019150602081019050610513565b838801955050505b50505092915050565b5f61054e82846104c3565b915081905092915050565b5f81519050919050565b5f5b83811015610580578082015181840152602081019050610565565b5f8484015250505050565b5f61059582610559565b61059f81856104a7565b93506105af818560208601610563565b80840191505092915050565b5f6105c6828461058b565b915081905092915050565b5f82825260208201905092915050565b7f4e6f7420746865206f776e6572000000000000000000000000000000000000005f82015250565b5f610615600d836105d1565b9150610620826105e1565b602082019050919050565b5f6020820190508181035f83015261064281610609565b905091905056fea2646970667358221220fea5cd3a84f134f4d4158109fa036ae8bfe87b4a407b8c769d6ba07f90d967ea64736f6c63430008140033

4.Extract and Decode the Hexadecimal Segment: The following 32-byte (64-character) hexadecimal segment was found embedded in the input data:for those who need the exact string part.

  • Hex Segment: 6e657875737b54723463335f5468335f5472346e7334637431306e7d

for the brave citizens lets run the script and get the flag..

the output of the script in chainrule challenge

Decoding this hexadecimal string from ASCII/running the script yields the flag:

  • Decoded String: nexus{Tr4c3_Th3_Tr4ns4ct10n}

Flag

The flag for the “Chain Clue” challenge is:

nexus{Tr4c3_Th3_Tr4ns4ct10n}

Next challenge

Silent Flag

silent flag

Goal: Recover the original value (flag) from an event emitted by a smart contract, given an archive file containing event log components.

Provided Information:

  • Archive file Archive.zip containing: abi.json,
[
  {
    "type": "function",
    "name": "leak",
    "inputs": [
      {
        "name": "id",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "event",
    "name": "Stored",
    "inputs": [
      {
        "name": "id",
        "type": "bytes32",
        "indexed": true,
        "internalType": "bytes32"
      },
      {
        "name": "data",
        "type": "bytes",
        "indexed": false,
        "internalType": "bytes"
      }
    ],
    "anonymous": false
  }
]
  • topic0,
0x2b017342b91efedb50bcabb8f1d8e8b6e6ad1dc391c876174642e80868b896ed

topic1,

0x0000000000000000000000000000000000000000000000000000000000001337

and data.

0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c59524f42444c6f07656803757e68730474077306797068050705024a00000000

Steps Taken

  1. Extract and Analyze Files: The provided archive was extracted, revealing the components of a single Ethereum event log:

    • abi.json: Contained the contract’s ABI, including the event signature.
    • topic0: The Keccak-256 hash of the event signature, identifying the event type.
    • topic1: The indexed parameter of the event.
    • data: The non-indexed paramet00020000000000000000000000000000000000000000000000000000000000000001c59524f42444c6f07656803757e68730474077306797068050705024a00000000`

    For an ABI-encoded bytes type, the data structure is:

    • 32 bytes: Offset to the data (here, 0x20).
    • 32 bytes: Length of the data (here, 0x1c or 28 bytes).
    • Actual Encoded Data (28 bytes): 59524f42444c6f07656803757e68730474077306797068050705024a
  2. Decode the Data (Single-Byte XOR): The data was suspected to be encoded using a simple XOR cipher, possibly with the hint 0x1337 or a single-byte key. Assuming the flag starts with the given CTF prefix nexus{, we can deer of the event, containing the hidden data.

  3. Determine Event Structure: From abi.json, the relevant event was identified:

    {
      "type": "event",
      "name": "Stored",
      "inputs": [
        { "name": "id", "type": "bytes32", "indexed": true },
        { "name": "data", "type": "bytes", "indexed": false }
      ],
      "anonymous": false
    }
    • Indexed Parameter (Topic 1): The id field from topic1 was 0x00...001337. This value (0x1337) is a common hint in CTFs.
    • Non-Indexed Parameter (Data): The data field contained the flag, likely encoded.
  4. Extract Encoded Data: The content of the data file was: `0x00000000000000000000000000000000000000000000000000000000000termine the key:

    • Expected first character: n (ASCII 0x6e)
    • Encoded first byte: 0x59
    • Key Calculation: 0x6e ^ 0x59 = 0x37

    Applying the single-byte XOR key 0x37 to the entire encoded data:

    for the easy way the encoded hex it the below one

    • Encoded Hex: 59524f42444c6f07656803757e68730474077306797068050705024a
  5. Script

    for the brave citizens we run the3 script and get the flag

    def solve_silent_flag():
        # The data field from the event log, after removing the 0x prefix and ABI encoding overhead
        # Data part: 59524f42444c6f07656803757e68730474077306797068050705024a
        encoded_hex = "59524f42444c6f07656803757e68730474077306797068050705024a"
        encoded_bytes = bytes.fromhex(encoded_hex)
    
        # Key determined by XORing the first byte of the encoded data (0x59)
        # with the first byte of the assumed flag prefix "n" (0x6e): 0x59 ^ 0x6e = 0x37.
        # This key is consistent for the entire prefix "nexus{".
        key = 0x37
    
        decoded_bytes = bytearray()
    
        for byte in encoded_bytes:
            # XOR each byte of the encoded data with the single-byte key
            decoded_byte = byte ^ key
            decoded_bytes.append(decoded_byte)
    
        try:
            # The flag is expected to be a string
            flag = decoded_bytes.decode('ascii')
            print(f"Decoded Flag: {flag}")
        except UnicodeDecodeError:
            print("Could not decode to ASCII. Decoded bytes (hex):")
            print(decoded_bytes.hex())
    
    if __name__ == "__main__":
        solve_silent_flag()

    after running the script or decoding the given encoded hex your way we get.

the final flag after running the script

Flag

The flag for the “Silent Flag” challenge is:

nexus{X0R_4BI_D3C0D1NG_2025}

HAPPY HACKING!!