Appearance
S1C9: Password Protected
1. 问题
合约有一个密码保护函数。需要找到正确的密码才能调用 mintFlag。
2. 原因
密码存储在合约的 storage 中。由于区块链上的所有数据都是公开的,可以直接读取 storage 槽位获取密码。
3. 方案
- 使用
cast storage读取合约的 storage 槽位 - 找到密码存储的槽位
- 使用该密码调用 mintFlag
bash
# 读取 storage 槽位(尝试不同 slot)
for slot in 0 1 2 3 4 5; do
cast storage <CHALLENGE> $slot --rpc-url $RPC
done
# 使用找到的密码
cast send <CHALLENGE> "mintFlag(string)" "found_password"
4. 遇到的陷阱
密码格式: 密码可能存储在多个槽位中(如果超过 32 字节),需要正确拼接。
5. 陷阱的原因
Solidity 中 string 类型如果长度 ≤ 31 字节,存储在单个槽位中(短字符串优化)。如果更长,当前槽位存储长度,数据存储在 keccak256(slot) 开始的连续槽位中。
6. 如何解决
先检查槽位值的最右字节是否为长度 × 2(短字符串编码),如果不是则读取 keccak256(slot) 开始的连续槽位。
7. 技术要点
| 要点 | 说明 |
|---|---|
| 区块链透明性 | 所有 storage 数据都是公开可读的 |
| 短字符串编码 | 长度 ≤ 31 字节直接存于槽位,> 31 字节使用 keccak256 寻址 |
| Storage 槽位 | 使用 cast storage 或 eth_getStorageAt 读取 |