01
—
前言
此内容仅作为展示Solidity常见错误的概念证明。它严格用于教育目的,不应被解释为鼓励或认可任何形式的非法活动或实际的黑客攻击企图。所提供的信息仅供参考和学习,基于此内容采取的任何行动均由个人全权负责。使用这些信息应遵守适用的法律、法规和道德标准。
DeFiVulnLabs一共有47个48个漏洞实验,包括各种经典的合约漏洞和一些少见的可能造成安全问题的不安全代码,本系列将逐一解析每个漏洞,包括官方的解释和自己的理解。
本篇是这个系列的最后倒数第二篇文章,且看且珍惜。
发现新增了一个漏洞Transient Storage Misuse
02
—
逻辑错误-合约锁定处理错误导致可多次提款
漏洞解析:
该漏洞是非常典型的逻辑错误问题,单纯是因为开发者脑子一糊涂没注意想当然的情况下所产生的。攻击者可以通过来漏洞来进行多次提款。
代码地址:
https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Incorrect_sanity_checks.sol
代码解析:
合约本身是一个存在漏洞的银行,然后存在一个CreateLocker函数和unlockToken
函数,然后在unlockToken里存在漏洞,导致可以多次提款。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
//一个存在漏洞的银行系统合约
contract VulnerableBank {
struct Locker {
bool hasLockedTokens;
uint256 amount;
uint256 lockTime;
address tokenAddress;
}
//嵌套字典
//{address:{id:Locker}}
mapping(address => mapping(uint256 => Locker)) private _unlockToken;
uint256 private _nextLockerId = 1;
//创建锁定
functioncreateLocker(
address tokenAddress,
uint256 amount,
uint256 lockTime
) public{
//存入代币必须大于0
require(amount > 0, "Amount must be greater than 0");
//解锁的时间大于当前区块时间
require(lockTime > block.timestamp, "Lock time must be in the future");
//发送者上toeknAddress的地址必须大于amount
require(
IERC20(tokenAddress).balanceOf(msg.sender) >= amount,
"Insufficient token balance"
);
//调用transferFrom将代币转移到银行合约中
IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
// 然后创建锁定时间,主要是hasLockedTokens=True
Locker storage locker = _unlockToken[msg.sender][_nextLockerId];
locker.hasLockedTokens = true;
locker.amount = amount;
locker.lockTime = lockTime;
locker.tokenAddress = tokenAddress;
_nextLockerId++; //给这笔存款添加一个id
}
//解锁代币并转账
functionunlockToken(uint256 lockerId) public{
//获取lockid下的Locker
Locker storage locker = _unlockToken[msg.sender][lockerId];
// 获取存放代币的值
uint256 amount = locker.amount;
//判断是否 hasLockedTokens=True ,也就是Bank里是否还存在没解锁的token
require(locker.hasLockedTokens, "No locked tokens");
// 如果当前区块时间大于解锁的时间,代币则置零
if (block.timestamp > locker.lockTime) {
locker.amount = 0;
}
//然后转账
//这里乍一看没问题,但是实际上上面如果当前区块时间小于解锁的时间,代币就不置零
//那么也就意味着locker.amount不会为0,我们无限调用unlockToken即可
IERC20(locker.tokenAddress).transfer(msg.sender, amount);
}
}
合约部署,如果出现问题,可能是如下原因所导致的
两个函数的调用createLocker主要存放Token,Token数量,以及锁定时间,在remix不大好复现,原因是需要输入一个TokenAddress,但是这个值不好确定。
03
—
foundry复现
Alice 利用这个漏洞,将 VulnerableBank 合约中的 110,000 个代币全部提取出来
contract FixedeBank {
....
functioncreateLocker(
.....
}
function unlockToken(uint256 lockerId) public {
Locker storage locker = _unlockToken[msg.sender][lockerId];
require(locker.hasLockedTokens, "No locked tokens");
require(block.timestamp > locker.lockTime, "Tokens are still locked");
// Save the amount to a local variable
uint256 amount = locker.amount;
//
locker.hasLockedTokens = false;
locker.amount = 0;
// Transfer tokens to the locker owner
IERC20(locker.tokenAddress).transfer(msg.sender, amount);
}
}
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(四十七)逻辑错误-合约锁定处理错误导致可多次提款
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论