数字经济CTF-RISE区块链题目详解
字数 1739 2025-08-22 12:22:43
区块链CTF题目解析:RISE合约漏洞利用
一、题目概述
这是一道区块链安全CTF题目,考察对智能合约的逆向分析和逻辑漏洞利用能力。题目提供了一个未公开源码的智能合约,要求通过逆向分析找到获取flag的方法。
二、合约关键函数分析
1. payforflag函数(目标函数)
function payforflag(var arg0) {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x03;
if (storage[keccak256(memory[0x00:0x40])] <= 0x0f4240) {
revert(memory[0x00:0x00]);
}
// 其他操作...
}
关键条件:storage[keccak256(memory[0x00:0x40])] > 0x0f4240(即1000000)
目标:使调用者的storage[3]值超过1000000
2. func_0293函数(关键漏洞函数)
function func_0293(var arg0) {
// 检查storage[3] > 0
var var1 = 0x0de0b6b3a7640000; // 1 ether
var var2 = msg.value;
var0 = var2 / var1; // 计算发送的以太数量
if (arg0 != storage[0x01]) {
// 错误路径:清零storage[3]
} else {
// 正确路径:storage[3] += (发送的以太数量) * storage[0x02]
storage[temp0] = storage[temp0] + var0 * storage[0x02];
storage[0x02] = 0x01;
}
}
关键点:当传入正确的arg0(等于storage[1])时,可以按比例增加storage[3]
3. func_03B2函数(设置storage[1])
function func_03B2(var arg0) {
// 检查storage[3] > 0
if (arg0 == 0x00) {
storage[0x00] = msg.sender;
storage[0x01] = storage[keccak256(memory[0x00:0x40])]; // storage[1] = storage[3]
storage[keccak256(memory[0x00:0x40])] = 0x00; // 清零storage[3]
}
// 其他分支...
}
功能:当传入0时,将storage[1]设置为当前storage[3]的值
4. func_08C6函数(设置storage[2])
function func_08C6(var arg0) {
if (msg.sender == storage[0x00]) {
storage[0x02] = arg0; // 可设置任意值
}
}
前提:需要msg.sender == storage[0]
5. deposit函数(充值)
function deposit() {
var var1 = 0x0de0b6b3a7640000; // 1 ether
storage[temp0] = storage[temp0] + msg.value / var1; // storage[3] += 发送的以太数量
}
功能:按1 ether为单位增加storage[3]
三、漏洞利用步骤
攻击思路
利用函数间的组合操作,通过以下步骤实现storage[3]的指数级增长:
- 首次充值1 ether,使storage[3] = 1
- 调用func_03B2(0)设置storage[1] = 1
- 调用func_08C6(1000000)设置storage[2] = 1000000
- 再次充值2 ether
- 调用func_0293(1)使storage[3] += 2 * 1000000 = 2000002
- 调用payforflag获取flag
详细步骤解析
-
初始充值:
- 调用deposit()发送1 ether
- 结果:storage[3] = 1
-
设置storage[1]:
- 调用func_03B2(0)
- 效果:
- storage[0] = msg.sender
- storage[1] = storage[3] = 1
- storage[3] = 0
-
设置storage[2]为放大系数:
- 调用func_08C6(1000000)
- 由于storage[0]已设置为msg.sender,可以成功调用
- 结果:storage[2] = 1000000
-
二次充值:
- 调用deposit()发送2 ether
- 结果:storage[3] = 2
-
关键操作:
- 调用func_0293(1)
- 由于storage[1] = 1,arg0 = 1,进入else分支
- 计算:storage[3] += (2) * 1000000 = 2000002
-
获取flag:
- 调用payforflag()
- 检查storage[3] = 2000002 > 1000000,条件满足
- 成功获取flag
四、技术要点总结
-
存储布局理解:
- storage[0]: 合约所有者
- storage[1]: 验证参数
- storage[2]: 乘法因子
- storage[3]: 用户余额
- storage[4]: 空投记录
-
关键漏洞:
- 函数组合可导致storage[3]的指数增长
- 通过设置storage[2]为极大值,再利用func_0293的乘法效应
-
安全启示:
- 智能合约中数值运算需考虑边界条件
- 关键参数修改应设置适当权限
- 函数间的组合可能产生非预期效果
五、完整攻击流程代码示例
// 伪代码展示攻击流程
async function exploit() {
// 1. 初始充值1 ether
await contract.deposit({value: ethers.utils.parseEther("1")});
// 2. 设置storage[1] = 1
await contract.func_03B2(0);
// 3. 设置storage[2] = 1000000
await contract.func_08C6(1000000);
// 4. 二次充值2 ether
await contract.deposit({value: ethers.utils.parseEther("2")});
// 5. 触发乘法效应
await contract.func_0293(1);
// 6. 获取flag
await contract.payforflag("your_email@example.com");
}
六、验证方法
可通过以下方式验证攻击是否成功:
- 检查合约storage[3]的值是否超过1000000
- 在区块链浏览器中查看payforflag调用事件
- 检查是否收到flag邮件
七、扩展思考
- 如果storage[2]设置得更大,是否可以减少充值次数?
- 如何防止此类漏洞?可能的解决方案:
- 对storage[2]设置上限
- 限制函数调用顺序
- 增加时间锁或冷却期
通过这道题目,我们深入理解了智能合约中存储变量操作和函数组合可能带来的安全隐患,这对智能合约安全审计具有重要参考价值。